Why when I click on some button in AppBar, first is called onOptionsItemSelected from Activity and then (if I return false) the onOptionsItemSelected on Fragment is called?
I thought that if there is override for onOptionsItemSelected on Fragment, it gets called first and calls Activity onOptionsItemSelected only if I return false.
It is possible that someone changed the order in some configuration? I am working on quite large project and I am not sure if someone not altered default behavior.
Related
I have a SearchView in the Toolbar of an Activity, which hosts a Fragment which has a ViewPager of 4 tabs, each tab consisting of another Fragment (there is a reason for this structure so I don't want to dwell on it unless someone thinks this is the reason for my problem). When the user originally searches something, everything works fine. However, when the user goes and edits the search query, and hits enter, the tab that was already selected doesn't refresh (it will refresh when we navigate away from it to other tabs and then come back).
The following are screenshots of what I mean (the first image is after the original search where everything is all and well, the second image is me editing the query, the third image is after I hit enter - the results stay the same and nothing is updated since isVisible() in the code below returns false when the fragment to me seems clearly visible).
When a query is submitted essentially what happens is the activity sends the search query to the fragment that contains the ViewPager and TabLayout, which passes it to the current Tab. This is the code that is called in the Fragment that contains the ViewPager after the query is submitted:
public void setSearchQuery (String query) {
//mTabLayout.requestFocus();
mSearchQuery = query;
Fragment fragment = mPagerAdapter.getItem(mViewPager.getCurrentItem()); // should theoretically get the current fragment
mTabLayout.getTabAt(mViewPager.getCurrentItem()).select();
if (fragment != null && fragment.isVisible()) {
((ResultsPagerAdapter.QuerySubmitCallback) fragment).submitQuery(query);
}
}
The submitQuery() line at the bottom is never called because isVisible returns false. However, when logging the lifecycle methods of the current tab the last calls are onStart() followed by onResume(). When I click on the toolbar and edit the query and hit enter, this doesn't change, and the current tab is visible, so I have no idea why this returns false.
I should add in the hosting Activity after the user submits their query, I remove focus from the Toolbar and SearchView so that the keyboard collapses after the search is entered.
When you switch the tabs,the fragment will call setUserVisibleHint(boolean isVisibleToUser).And then,you can get the value of isVisibleToUser by getUserVisibleHint().
If you want to get isVisible of all kinds of fragment,you should update the value of isVisible in such methods:
onAttach and onDetach()
onStart() and onStop()
onHiddenChanged
setUserVisibleHint
onViewAttachedToWindow and onViewDetachedFromWindow
I'm storing one of the menu items from my activity as a class field, when inside onCreateOptionsMenu().
_myMenuItem = (MenuItem) findViewById(R.id.menu_item);
But in onCreate() I call an async method, and the result of that dictates whether I should make the visibility true or false on said menu item; for example:
if (someCondition) {
_myMenuItem.setVisible(true);
}
The problem is sometimes the async method completes (and that above code is called) before the menu is inflated, causing a null pointer exception.
How can I resolve this timing issue? Ideally without putting the async call inside onCreateOptionsMenu() if anyone knows of a good way. Thanks
Declare a boolean property in the Activity. After completing the background work set that property to true if menu item is to be shown, to false otherwise and force the menu to be refreashed (call invalidateOptionsMenu). Refreshing options menu causes onPrepareOptionsMenu to be called. In onPrepareOptionsMenu check the value of your property and set the visibility of menu item.
I was reading the help section on google's android page on OptionsMenus and ActionsBars:
http://developer.android.com/guide/topics/ui/actionbar.html
And they included a note that stated that when using fragments, the activity's onOptionsItemSelected method would be called beforethe fragment's is called, their by making it necessary to include the default: return super.onOptionsItemSelected at the end of the onOptionsItemSelected method definition. They included the following method example but did not state if this was meant to be an example within an Activity definition or a Fragment definition. I was a little confused on this and wanted to ask for clearification. based on the use of "super" it would suggest it's inside the fragment getting passed up to the Activity, but this disagrees with their statement that the Activity gets called first. If it's meant to be an example in the Activity and "super" refers to the parent Application class, then I am not clear on how it gets referred back to the Fragment. Any notes of clearification would be appreciated.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_search:
openSearch();
return true;
case R.id.action_compose:
composeMessage();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Your MyActivity extends Activity and thus inherits its methods, one of which is onOptionsItemSelected() that you are overriding.
Calling super.whatever() says: I want to override this method whatever()from the superclass Activity, but still run the method as defined there. Basically, you are adding something to that method. It's what you typically do onCreate, for example.
In this case, returning false would mean that, if ID is different from the mentioned twos, we're done - menu managing can stop here. Obviously we are not, as we want the fragment to receive its call.
So: your activity overrides the superclass method to manage the first two menu items, then calls the superclass method to keep things running and say hey, there might be something that has not be managed here.
I have a SearchView in my onCreateOptionsMenu() that I hide using:
if(mState == 0){
msearchMenuItem.setVisible(false);
}
I want it to be true after I access a custom Fragment that is part of a ViewPager in:
setMenuVisibility(boolean menuVisible){
}
I understand that I cannot pass a reference of the MenuItem to my Fragment because the ActionBar is not a layout, and all of its process needs to be from the MainActivity.
My option was to change the value of mState from setMenuVisibility() in my Fragment, but It shows the SearchView after I interact with my DrawerLayout. If I don't touch the home button, it would never show the SearchView.
Now, what can I do to load it exactly when I am at the Fragment?
you create a callback to the activity that enables the menu option again when you are in the fragment.
See Communicating with an Activity
Now, what can I do to load it exactly when I am at the Fragment?
You can use the setMenuVisibility() callback from your Fragment's onCreateView() method.
The actual answer is overriding onCreateOptionsMenu() from my Fragment. But first, I had to call setHasOptionsMenu(true); in onCreate(), so the override could take effect.
I put a couple of breakpoints in onCreate (one at the beginning, and one at the end of the method), and I also put one at the beginning of onCreateOptionsMenu. The onCreate method is called first, and before it finishes onCreateOptionsMenu is called.
I'm trying to separate the Fragment navigation code in my app, so I have a couple of objects that I delegate onCreateOptionsMenu to depending on if the app is running on phone/tablet (I'm using screen size to determine this, my layout file for large screens has a View I check for after the layout is inflated). The problem I'm having is, I create these objects in onCreate, and I'm getting a null pointer exception when I reference the object in onCreateOptionsMenu.
The onCreate method is called first, and before it finishes onCreateOptionsMenu is called.
That will be true on devices and apps with an official Honeycomb-style action bar. If there is no action bar, onCreateOptionsMenu() should not get called until the user calls up the menu, typically by pressing the MENU button.
(I'm using screen size to determine this, my layout file for large screens has a View I check for after the layout is inflated)
That test will break very shortly, once Ice Cream Sandwich ships. From what I can tell, ICS phones will have action bars (though perhaps not system bars).
In my case on Android 2.3 and with FragmentActivity from v4-support library the order of life-cycle methods invoke is following:
07-18 18:29:21.629 20183-20183/? I/onCreate:
07-18 18:29:21.719 20183-20183/? I/onStart:
07-18 18:29:21.719 20183-20183/? I/onResume:
07-18 18:29:21.739 20183-20183/? I/onCreateOptionsMenu:
I found if in onResume() I call
invalidateOptionsMenu();
then onCreateOptionsMenu(Menu menu) is called afterward - as per the activity life cycle (I think that's the correct term here), as indicated by #tir38
#Override
public void onResume() {
super.onResume();
invalidateOptionsMenu();
}
Addition in above answer,
In case of ICS and Honeycomb onCreateOptionsMenu is called after onCreate and onPostCreate while in Gingerbread and earlier versions it is called after onCreate but before onPostCreate. Thats the only difference I found.
In my experience ActionBarActivity from support v7 onCreateOptionsMenu() called in setContentView() method in the middle of onCreate() it is appear on 4.1.1.
But on 4.4 another story onCreateOptionMenu() called after onCreate(). Also I don't know it may be immediately after, maybe not. But is fact after. I didn't test on other versions but 4.1.1 is first where I had have a trouble with init order.
i suggest to create a callback-function in your fragment to avoid timing issues with onResume() and onCreateOptionsMenu().
doing the following works flawless for me:
create and add your fragment to your activity
leave a reference of this fragment in your activity
create a public method doSomethingWithTheMenu() in your fragment
call this method from within your activity when onCreateOptionsMenu(Menu menu) is called.
example:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (this.myFragment != null) {
this.myFragment.doSomethingWithTheMenu(menu);
}
return true;
}