I've hit a weird problem with my Android app. My main activity has a menu attached to the menu button. Problem is, the menu button works exactly once. Once pressed, the app has to be restarted before the menu button will work again.
The (sanitised) code is:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
startActivity(new Intent(this, PreferencesActivity.class));
return(super.onCreateOptionsMenu(menu));
}
#Override
public void onBackPressed() {
this.finish();
}
#Override
public void onDestroy() {
super.onDestroy();
this.finish();
}
#Override
public void onStop() {
super.onStop();
this.finish();
}
and the preferences activity looks like
public class PreferencesActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
addPreferencesFromResource(R.xml.preferences);
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onStop() {
super.onStop();
this.finish();
}
#Override
public void onBackPressed() {
this.finish();
}
}
Any ideas how I should resolve this?
TIA
The onCreateOptionsMenu is only called once, right before you open the OptionsMenu the first time.
Instead use onPrepareOptionsMenu to call your startActivity(new Intent(this, PreferencesActivity.class));
onPrepareOptionsMenuis called everytime you click on the Menu-Button.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
startActivity(new Intent(this, PreferencesActivity.class));
return super.onPrepareOptionsMenu(menu);
}
The problem is that you are starting an Activity instead of showing a menu.
The normal way would be to inflate a menu inside the onCreate Optionsmenu and have a settings item in this menu, that if pressed shows the Preference Activity.
Thommy is right. If you want to start the preference Activity right away (keep in mind that this is unusual behavior for an Android App) you can do this in the onPrepareOptionsMenu.
In the normal flow the menu is created once in the onCreateOptionsMenu callback and after that it is reused.
After clicking on the menu button you are moving to the PreferencesActivity and whenever you click back over there... the first activity is closed because of the onBackPressed method.
Can you tell me what exactly you are looking for ?
Related
I'm learning how to create an app and have figured out how to build a login activity and a menu for logging out that returns you to the login layout. However, if I navigate through my different layouts/activities, the menu item for logging out duplicates. I believe it's due to having the menu created on each layout, but I'm not sure how to change it so that it doesn't duplicate.
Here's my fragment.
public class UserFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
return inflater.inflate(R.layout.activity_user_fragment, container, false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_logout, menu);
}
}
My LoginActivity.class
public class LoginActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Button b1login = (Button) findViewById(R.id.btlogin);
Button b2login_cancel = (Button) findViewById(R.id.btcancel_login);
assert b1login != null;
b1login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText ed1 = (EditText) findViewById(R.id.etuser_name);
EditText ed2 = (EditText) findViewById(R.id.etpassword);
if (ed1.getText().toString().equals(getText(R.string.user_id)) &&
ed2.getText().toString().equals(getText(R.string.user_password))) {
Toast.makeText(getApplicationContext(), R.string.successful_login,
Toast.LENGTH_SHORT).show();
setContentView(R.layout.activity_clients);
} else {
Toast.makeText(getApplicationContext(), R.string.unsuccessful_login,
Toast.LENGTH_SHORT).show();
}
}
});
assert b2login_cancel != null;
b2login_cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.exit(0);
}
});
}
//Menu option logout return to login screen.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_item_logout) {
Intent i = new Intent(this, LoginActivity.class);
this.startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
}
public void addClient(View view) {
setContentView(R.layout.activity_new_client);
}
public void submitClient(View view) {
setContentView(R.layout.activity_sessions);
}
public void cancelClient(View view) {
setContentView(R.layout.activity_clients);
}
public void newSession(View view) {
setContentView(R.layout.activity_new_session);
}
public void cancelSessionCompletion(View view) {
setContentView(R.layout.activity_sessions);
}
public void cancelSession(View view) {
setContentView(R.layout.activity_sessions);
}
}
Fragment layout.
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menu_item_logout"
android:icon="#drawable/ic_logout"
android:title="#string/logout"
app:showAsAction="ifRoom|withText" />
</menu
EDIT:
I deleted the fragment where I initially created the menu as well as the menu code from the UserFragment and hard coded the menu itself into the LoginActivity class, which fixed the duplication issue.
Code adding menu.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
However, the menu now shows on the login screen. How do I prevent it from showing there?
For having different menu items in different activities you need to create different menu xml layouts.
So one file named menu.xml is already there in res folder. You need to create new xml file in same folder.
Then in java code of that activity :- in override Oncreateoptionmenu inflate the menu you want.
Here is what you need to do:
In your LoginActivity class, you want to NOT inflate the menu because you don't want users to do see the logout option.
In your Activity that loads after Login, you actually want to inflate the Logout menu - well, because you want them to see and use that option.
In your code, I see that your fragment uses the method :
setHasOptionsMenu(true);
What you can do here is use an if statement to see if a user has logged in before and if not, set that to true otherwise, do nothing (which is like setting to false).
You can reuse the menu in other activities if you want to enable users to Logout from those activities or just leave them it out!
I hope this helps!
EDIT
According to your sample code above, your first part shows what you would do inside a Fragment class - you setHasOptionsMenu(true). Now, it seems that you have two logout menus, in the activity and in the fragment - which I do not see where you have used your Fragment in your LoginActivity - unless it is somewhere else in a different activity!
Similar to my answer here, https://stackoverflow.com/a/63008005/5916188, a work-around would be to remove the menu each time, if it exists, before adding it.
Note that the existing menu is removed item by item (R.id...) while the inflated one is added by menu (R.menu...) as usual.
This way, you can keep the menu in the fragment. Like this:
#Override public void onCreateOptionsMenu (#NonNull Menu menu, #NonNull MenuInflater
inflater)
{
super.onCreateOptionsMenu (menu, inflater);
removeMenuItemIfPresent (menu, R.id.menu_search_options);
removeMenuItemIfPresent (menu, R.id.menu_sample_filter1);
removeMenuItemIfPresent (menu, R.id.menu_sample_filter2);
inflater.inflate (R.menu.menu_search_menu, menu);
}
private void removeMenuItemIfPresent (#NonNull Menu menu, int resourceIDToRemove)
{
MenuItem menuItemToRemove = menu.findItem (resourceIDToRemove);
if (menuItemToRemove != null) {
menu.removeItem (menuItemToRemove.getItemId ());
}
}
I have the following scenario:
Activity A > Activity B
Activity A < Activity B
What i would like to do is keep the state of Activity A when clicking Activity B's back button.
Activity A code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_details);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null)
{
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
// check for saved instance
if (savedInstanceState != null)
{
//restore saved values
}
else
{
//initialize members with default values
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putString("typeID", typeID);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
typeID = savedInstanceState.getString("typeID");
}
public boolean gotoActivityB(View view)
{
Intent intent = new Intent(getApplicationContext(), ActivityB.class);
startActivity(intent);
return false;
}
Activity B code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buy_item);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null)
{
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
//do some magic...
}
public boolean onOptionsItemSelected(MenuItem item)
{
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
startActivity(intent);
return false;
}
Every time i go from Activity B to Activity A, savedInstanceState is equal to null, in other words, Activity A state isn't saved or restored.
What am i missing here?
!!SOLUTION!!
Based on #cybersam's answer, Activities maintain their state by default. So there is no need for the savedInstanceState. To solve my problem i only had to update my back button events to:
public boolean onOptionsItemSelected(MenuItem item)
{
finish();
return true;
}
As the documentation for onSaveInstanceState() states:
An example when onPause() is called and not
onSaveInstanceState(Bundle) is when activity B is launched in front of
activity A: the system may avoid calling onSaveInstanceState(Bundle)
on activity A if it isn't killed during the lifetime of B since the
state of the user interface of A will stay intact.
So you cannot assume that onSaveInstanceState() would be called on A just because B is launched in front of it. In fact, most of the time, it will not be.
[EDITED]
Your code for B seems to be calling startActivity() to "go back" to the prior activity. If you just want B to go back to the prior activity, you can (usually) just call finish() to exit B, which should allow A to reappear (with its state intact), since it will become the top Activity in the stack.
I am attempting to override onBackPressed(). However it appears to not detect when I click the back button in the action bar.
I currently have this code:
#Override
public void onBackPressed() {
Log.i("DATA", "Hit onBackPressed()");
super.onBackPressed();
}
The log message never appears in the LogCat. I know that this log statement works because it is copied from another method with a different message that DOES display in the LogCat.
I have searched for answers, and I have tried using onKeyDown and detecting if it is the BACK button being clicked but I still have the same issue.
Information about the project:
Android Studio 0.9.3
Method is located in blank activity
target sdk 21
minimum sdk 15
testing device is a Samsung Galaxy 5 (not emulator)
Any help would be greatly appreciated!!
EDIT:
This is a copy of my working code (this is test code so the activity name is not descriptive):
public class MainActivity2 extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_activity2);
getActionBar().setDisplayHomeAsUpEnabled(true);//Displays the back button
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main_activity2, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Log.i("DATA", "Hit Actionbar Back Button");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
The message "Hit Actionbar Back Button" now appears in the LogCat.
onBackPressed() is invoked when user clicks on a hardware back button (or on the 'up' button in the navigation bar), not the button in the action bar. For this one you need to override onOptionsItemSelected() method. Example:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// click on 'up' button in the action bar, handle it here
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Please try this code,
public class MainActivity2 extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
Toast.makeText(getApplicationContext(), "back press is call", Toast.LENGTH_LONG).show();
}
}
I have an activity, say MainActivity that has just onCreate method (no onResume, onStop, etc)
when a contBut is clicked, another activity, say SecondActivity is started and with pressing exitBut, we navigate back to MainActivity. in this case, onCreate of MainActivity is recalled which I can't figure why. I expect it to resume the activity, or at least not to call onCreate.
here is the code for MainActivity:
public class MainActivity extends Activity {
private Button contBut;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contBut = (Button) findViewById(R.id.cont);
contBut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("d", "in count onclick");
Intent intent = new Intent(MainActivity.this, ContActivity.class);
startActivity(intent);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
here is the code for SecondActivity:
public class SecondActivity extends Activity{
private Button exitBut;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cont);
exitBut = (Button) findViewById(R.id.exit);
exitBut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
problem solved.
in the developer options section of my device, "Don't keep activities" was checked.
i have tabactivity with 3 child activity, but i get issue back press not work to close Tab activity...
i set in Tab activity with
#Override
public void onBackPressed() {
super.onBackPressed();
}
and i set in each child activity with
#Override
public void onBackPressed() {
this.getParent().onBackPressed();
}
its seem not work, ho to fix it ? sorry for my english.. thanks
Try to add calling for finish() method inside your onBackPressed() method
ex:
#Override
public void onBackPressed() {
this.getParent().onBackPressed();
this.finish();
}
#Override
public void onBackPressed() {
super.onBackPressed();
this.finish();
}