Android - Up navigation in ActionBar on ICS - android

Up navigation in the Action Bar is simple to setup and works correctly on Jelly Bean:
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
and for that activity in the AndroidManifest
android:parentActivityName=".MainActivity"
but the attribute android:parentActivityName is not available on ICS (4.0.3). The Android documentation is very vague on how to use it on ICS, says
To support older devices with the support library, also include a <meta-data> element that specifies the parent activity as the value for android.support.PARENT_ACTIVITY.
...which I did, but what is this about the support library? I followed the Support Library setup guide on http://developer.android.com/tools/support-library/setup.html but... it still doesn't make the damn up button work in my app on ICS. It's such a small thing, why does it have to be so insanely complicated?

I encountered this problem as well, and wound up with a slightly different solution.
On each screen in my app, I inflate a menu, even if there are no menu options on that screen:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu (Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
}
The code above sets up a menu, but never inflates one.
I then explicitly set the up navigation to point to a specific class. Note the use of android.R.id.home:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
Intent i = null;
switch (item.getItemId())
{
case android.R.id.home:
i = new Intent(getActivity(), DesiredActivity.class);
startActivityForResult(i, 0);
break;
default:
break;
}
return true;
}
I have tested this on Android version 4.0.3, 4.0.4, 4.4.2 and it works.

You can do what NKijak answered.
Another way to do this is to handle the Up action manually in the Child Activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
startActivity(new Intent(this, ParentActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP));
default:
return super.onOptionsItemSelected(item);
}
}
And best part of this manual approach is that it works as expected(resumes the ParentActivity instead of ReCreating it) for all API Levels.

You're extending Activity instead of android.support.v7.app.ActionBarActivity.
Then use getSupportActionBar() instead of getActionBar().

Related

AppCompact v21 back navigation with fragment menu

So.. I'm facing a problem that has been driving me crazy for the past hours.
I have an App using the AppCompact v21 and toolbar. I also handle back navigation using:
getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
In combination with the parent activity on the manifest. Which works perfect....
My problem is:
I have an activity with 3 tabs with a viewpager and I need one of the fragments to have it's own menu.
I can inflate the menu just fine but once the menu is inflated the back arrow in that fragment don't work anymore. In the other 2 fragments of the view pager the back navigation through the toolbar still works.
Inside my fragment:
// Inside onCreate...
this.setHasOptionsMenu(true);
// Later on somewhere else...
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_submit, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
// my menu logic goes here.
return true;
}
Any suggestions?
When you always return true in onOptionsItemSelected(), that means you've handled every menu item possible (including the Up button). You should instead return super.onOptionsItemSelected(item) in cases where you do not handle one of your items:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Your menu logic such as
case R.id.your_menu_item:
// Do something
return true;
default:
return super.onOptionsItemSelected(item);
}
}

Back button works, but the actionbar button does not?

I have a pretty simple use case which seems to be working for all my Activities except one. All my Activities are calling getActionBar().setDisplayHomeAsUpEnabled(true);. The only place this isn't working is on an Activity I just added, which is also the only one I'm launching from the main activity's info menu. The actual OS back button works as expected in this case, but the Home button is not. Here's the menu logic that starts the activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.share:
share();
return true;
case R.id.support:
emailSupport();
return true;
case R.id.settings:
settings();
return true;
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
The problem is with the SettingsActivity, called in the menu by the settings() method you see in the switch above:
private void settings(){
Intent intent = new Intent(InfoMenuActivity.this, SettingsActivity.class);
startActivity(intent);
}
And the onCreate of the SettingsActivity is pretty simple too:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prefs = this.getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE);
setView();
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setDisplayShowHomeEnabled(true);
}
I originally only had the first getActionBar() call, but added the second to see if it changed anything, but it did not.
What am I missing?
From the comments it seems that you don't know how to set the parent activity in the manifest and keep supporting older versions.
For API 16 you can use parentActivityName attribute but for older versions you'll have to add a meta tag:
<activity
android:name="com.example.SettingsActivity"
android:label="Settings Activity"
android:parentActivityName="com.example.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.MainActivity" />
</activity>
If all you want to do is override the up button functionality to act as a back button then don't forget that in every activity you'll have to override onOptionsItemSelected.
However remember that overriding any default and expected android behaviour is bad practice.

Up Navigation from Actionbar in Android

In my android app i am using up navigation icon on actionbar.
In my child activity i have set
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
In manifest I have set
<activity
android:name="app.sclms.UserAccount"
android:label="#string/app_name"
android:parentActivityName="app.sclms.MainMenu">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="app.sclms.MainMenu" />
</activity>
But whenever I click on up icon my application get exited.
What is missing here, I don't understand.
Please read this section: http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp
Specifically, add this code:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
I however do not recommend using navigateUpFromSameTask as it will cause your ParentActivity to be re-created instead of being resumed on ICS and lower. The reason for this behavior is because NavUtils behaves differently for Pre JellyBean and Post JellyBean as explained in this SO.
A better way is to do this is to handle the Up action manually in the Child Activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
startActivity(new Intent(this, ParentActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP));
default:
return super.onOptionsItemSelected(item);
}
}
And best part of this manual approach is that it works as expected(resumes the ParentActivity instead of ReCreating it) for all API Levels.

Way to set Home Application Icon in ActionBarSherlock

I am confused the way to set home icon on ActionbarSherlock and of course am new to this ActionBarSherlock. Have checked many sources, but unable to get how to set the home icon. Below is my class that sets the ActionbarSherlock.
public abstract class BaseActivity extends SherlockActivity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem miPrefs = menu.add("Login");
miPrefs.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
miPrefs.setOnMenuItemClickListener(new OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
Intent loginIntent = new Intent(BaseActivity.this, LoginForm.class);
startActivity(loginIntent);
return true;
}
});
return true;
}
}
Of course I know how to set application icon as home icon in the normal action bar. The following is the way I usually set the normal actionbar.
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem menu1 = menu.add(0, 0, 0, "Login");
menu1.setIcon(R.drawable.image1);
menu1.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
In the onCreate(), we have to get the actionbar by getActionBar() and then with actionbar.setDisplayHomeAsEnabled(true), it is possible to set the application icon as home icon. By setting the following we can listen to the clicks of home icon.
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
// Here we can keep the code to get to the mainactivity.
return true;
}
}
Also, when I just try to get the actionbar by ActionBar actionbar = getSupportActionBar(); in oncreate(), I get this error,
Type mismatch: cannot convert from com.actionbarsherlock.app.ActionBar to android.app.ActionBar
I'm confused about how to set the application icon as home icon based on the above code of ActionbarSherlock and listen for the clicks. How can I get that done?
Enabling the App Icon to be clickable in the ActionBar (using ABS)
#Override
public void onCreate() {
super.onCreate();
getSupportActionBar().setHomeButtonEnabled(true);
}
ABS is a library so when you want to access it's features, you must use it's own methods/classes, not to be confused with the default Android methods/classes (such as getActionBar() and getSupportActionBar()). A great place for sample code is https://github.com/JakeWharton/ActionBarSherlock/tree/master/samples/demos.
Listening to clicks
The same as what you have above.

Fragment not receiving menu callbacks

I have a fragment class that extends Fragment and calls setHasOptionsMenu to participate in the menu. This class also implements onCreateOptionsMenu, onPrepareOptionsMenu and onOptionsItemSelected.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
....
}
I'm dynamically loading this fragment using a FragmentTransaction in my Activity (that extends FragmentActivity).
However none of the menu callbacks (onCreateOptionsMenu, onPrepareOptionsMenu and onOptionsItemSelected) are being called (I've debugged with some breakpoints in those methods) and the menu isn't shown.
Am I missing something? Do I need to add something in my Activity?
I'm using the Android Compatibility Library, compiling with L11 SDK and testing in a Xoom.
EDIT: I've found the problem. My AndroidManifest is targeting L11, this seems to hide the menu button and prevent from the callbacks being called. However if I remove this from the manifest I loose some other features I need (for example the activated state in lists). Does anyone know how to solve this issue (enable the menu button) without removing the targetSdkVersion=11 from the Manifest?
Aromero,
Don't forget to override the onCreateOptionsMenu using the fragment version of the method, similar to this:
#Override
public void onCreateOptionsMenu (Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.queue_options, menu);
super.onCreateOptionsMenu(menu, inflater);
}
This goes in the fragment, by the way, and adds to the inflated menu of the Activity, if there is one. Had the same problem myself, until I figured this out.
Kim
If you're having this problem with ActionBarSherlock, you need to make sure your Fragments are SherlockFragments, not mere SupportFragments, and that what you're overriding is
public void onPrepareOptionsMenu (com.actionbarsherlock.view.Menu menu) {
NOT
public void onPrepareOptionsMenu (android.view.Menu menu) {
If you do the latter, you should get some sort of warning about the function being final and you being unable to override it. This is a warning that you're trying to override the wrong function!
If you fix the error by switching the class from SherlockFragment to a mere Fragment, you can create the function . . . but it won't get called.
I had the same problem, but i think its better to summarize and introduce the last step to get it working:
Add setHasOptionsMenu(true) method in your Fragment's onCreate(Bundle savedInstanceState) method.
Override onCreateOptionsMenu(Menu menu, MenuInflater inflater) (if you want to do something different in your Fragment's menu) and onOptionsItemSelected(MenuItem item) methods in your Fragment.
Inside your onOptionsItemSelected(MenuItem item) Activity's method, make sure you return false when the menu item action would be implemented in onOptionsItemSelected(MenuItem item) Fragment's method.
An example:
Activity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Do Activity menu item stuff here
return true;
case R.id.fragment_menu_item:
// Not implemented here
return false;
default:
break;
}
return false;
}
Fragment
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
....
}
#Override
public void onCreateOptionsMenu(Menu menu) {
// Do something that differs the Activity's menu here
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Not implemented here
return false;
case R.id.fragment_menu_item:
// Do Fragment menu item stuff here
return true;
default:
break;
}
return false;
}
I hope this will be helpful.
Cheers.
If you have an activity and a fragment that each loads menu items then you need to take special care of which overrides you use.
Activities can override onOptionsItemSelected and onMenuItemSelected, however fragments can only override onOptionsItemSelected.
If you override onMenuItemSelected in your activity and onOptionsItemSelected in your fragment, your fragment override will never get triggered.
Instead, use onOptionsItemSelected in both activity and fragment.
You need to make sure you call setHasOptionsMenu(true); onCreate or onCreateView is called in your fragment.
You also need to implement the override of onCreateOptionsMenu inside your fragment.
Another possible case is when you use a common id for a common action in each fragment; for instance R.id.action_add
Today I had such situation: hitting the option menu [add] was invoked the "wrong" onOptionItemSelected because each fragment (replaced dynamically using a DrawerLayout) had the same R.id.action_add.
Short story, if you have such situation always check that your fragment is visible:
if (!isVisible()) return false;
Long story, pay attention at the onOptionItemSelected chain!
MainActivity
|
| onOptionItemSelected
+-----------------------
| return false
|
MyCoolFragment1
|
| onOptionItemSelected
+-----------------------
| return false
|
MyCoolFragment2
|
| onOptionItemSelected
+-----------------------
| return true
|
[item selection handled]
If you add your fragments with (something like) this:
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, MyCoolFragment1.newInstance())
.commit()
and you have defined the same id for a common action (let's say R.id.action_add) in each fragment;
don't forget to add this line to each: if (!isVisible()) return false;
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!isVisible()) return false; // <-- Not visible? skip!
if (item.getItemId() == R.id.action_add) {
//.TODO whatever
return true; //.Handled!
}
return false; //.Skip
}
I had this problem when I was using the ViewPagerIndicator in conjunction with ActionBarSherlock. Although it appeared this was fixed I still ran into the problem. The work around I found was to call into the fragment manually.
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
Toast.makeText(this, "From activity", Toast.LENGTH_SHORT).show(); // TODO
Fragment currentFragment = mAdapter.getItem(mPager.getCurrentItem());
if (currentFragment != null && currentFragment instanceof SherlockFragment)
{
((SherlockFragment)currentFragment).onOptionsItemSelected(item);
}
return super.onOptionsItemSelected(item);
}
I've found the problem. The AndroidManifest is targeting SDK 11, this seems to hide the menu button and prevent from the callbacks being called. I assume that this breaks the compatibility of the menu button that seems to be replaced by the action bar in Android 3.0
I think you have implemented onCreateOptionsMenu, onPrepareOptionsMenu and onOptionsItemSelected in the class that extends Fragment. Try by doing that in your Activity class where you are loading this fragment
From the android developer site - link
Note: If you inflate menu items from a fragment, via the Fragment
class's onCreateOptionsMenu() callback, the system calls
onOptionsItemSelected() for that fragment when the user selects one of
those items. However, the activity gets a chance to handle the event
first, so the system first calls onOptionsItemSelected() on the
activity, before calling the same callback for the fragment. To ensure
that any fragments in the activity also have a chance to handle the
callback, always pass the call to the superclass as the default
behavior instead of returning false when you do not handle the item.
Therefore Marco HC is the best answer of all.
If your toolbar is defined in the parent activity xml, make sure you do this in your fragment
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
....
Toolbar toolbar = (Toolbar)getActivity().findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
setHasOptionsMenu(true);
}
And then of course, override onCreateOptionsMenu like below
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.edit_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
This is the only solution that worked for me!
I had same problem and solution that worked for me is:
Remove or comment any onOptionsItemSelected() ,onMenuItemSelected() even onPrepareOptionMenu() and leave in Activity onCreateOptionsMenu() only:
#Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.layout.menu, menu);
return true;
}
In Fragment class, in onCreateView(), put:
setHasOptionsMenu(true);
In Fragment class add :
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu,inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.action_insert:
//doing stuff
return true;
}
return false;
}
Tested and worked on Android 4.4

Categories

Resources