I have list items onlcick of list item I take to DetailActivity.
DetailActivity is swipable using viewpager and Fragments. The issue is when DetailActivity is opened and Fragment is loaded no menu options are displayed however if i swipe left or right i get the menu options
then when i return to the initial fragment the menu items are visible.
I am inflating the menu options in fragment.
DetailActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_article_new);
mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
int currentItemIndex = mNewsItemList.indexOf(newsItem);
mArticleSwipeMap = new HashMap<>();
mPagerAdapter = new ArticlePagerAdapter(getSupportFragmentManager());
mPagerAdapter.swapList(mNewsItemList);
mViewPager.setOnSwipeOutListener(this);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(currentItemIndex);
mViewPager.addOnPageChangeListener(this);
}
Adapter class
private class ArticlePagerAdapter extends FragmentStatePagerAdapter{
private Map<Integer, ArticleFragment> mPageReferenceMap = new HashMap<>();
private List<NewsItem> newsItemList;
public ArticlePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
ArticleFragment articleFragment = ArticleFragment.newInstance(newsItemList.get(position));
mPageReferenceMap.put(position, articleFragment);
return articleFragment;
}
#Override
public int getCount() {
return newsItemList.size();
}
public void swapList(List<NewsItem> newsItemList){
this.newsItemList = newsItemList;
notifyDataSetChanged();
}
}
DetailFragment
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.toolbar_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch(itemId) {
case R.id.action_more:
View menuItemView = mRootView.findViewById(R.id.action_more);
onMenuClick(menuItemView);
break;
case R.id.action_share:
shareTextUrl();
break;
}
return true;
}
Update 1 : When I swipe left, menu item are not shown when i swipe right menu items are shown, now if i swipe left menu item are shown.
Has it got to do anything with setting up viewpager
This is how I am setting up viewpager in onCrete of Activity
mPagerAdapter = new ArticlePagerAdapter(getSupportFragmentManager());
mPagerAdapter.swapList(mNewsItemList);
mViewPager.setOnSwipeOutListener(this);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(currentItemIndex);
mViewPager.addOnPageChangeListener(this);
Update 2 :
The issue is when I use custom toolbar it is not working. When i use with the DarkActionBar it works fine but when I use with NoActionBar with my custom toolbar it creates prob.
Here is the sample proj I created which has same issue
link to github https://github.com/KumarVelu/viewPagerDemo
The problem is that you are trying to set more than one main ActionBar because each fragment has its own Toolbar. So, when ViewPager loads the current page and preloads the next page, you have 2 fragments trying to set their own Toolbar as the main ActionBar and they are conflicting.
If you want to use a Toolbar as main Action Bar, move the Toolbar in the Activity layout instead of the fragments layout and initialize it once in the Activity.
If you want a separate Toolbar in each fragment, don't set any Toolbar as main action bar and use Toolbar.inflateMenu() and Toolbar.setOnMenuItemClickListener() instead. Each fragment will then inflate its own menu items directly in its own Toolbar, without involving the Activity.
void method(){
int currentItemIndex = mNewsItemList.indexOf(newsItem);
mArticleSwipeMap = new HashMap<>();
mPagerAdapter = new ArticlePagerAdapter(getSupportFragmentManager());
mPagerAdapter.swapList(mNewsItemList);
mViewPager.setOnSwipeOutListener(this);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(currentItemIndex);
mViewPager.addOnPageChangeListener(this);
}
Before
Call in onresume your fragment.
See you
Related
The following bug will happen on an 2.3 device, my setup works fine on 4.x devices.
I have a ViewPager with some Fragments in it (they're all of the same class).
Every Fragment inflates it's own Menu, because the Menu Items may vary from Fragment to Fragment.
For test purposes, I have set up a Menu Item in the ActionBar (the ActionBar is shown on the bottom in the pic because it's a split ActionBar). When the Item is tapped, a TextView in the Fragment should be set to "clicked". This works in the beginning, but after flicking around a bit, this happens:
When the Menu Item is tapped, nothing happens. Instead, as soon as I swipe to the next Fragment, the next Fragment sets its TextView to "clicked". It seems like the ActionBar and it's Menu are associated with the next Fragment.
Heres a pic
And heres some code:
My Activity:
public class MyActivity extends SherlockFragmentActivity implements
MyFragment.InvalidateListener {
ViewPager viewPager;
SectionsPagerAdapter pagerAdapter;
public void invalidate() {
ActivityCompat.invalidateOptionsMenu(act);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.empty_viewpager);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(pagerAdapter);
viewPager.setCurrentItem(initialIndex);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = new MyFragment();
fragment.setHasOptionsMenu(true);
return fragment;
}
// ...
}
My Fragment:
public class MyFragment extends SherlockFragment {
HashSet<ImageView> runningImageTasks = new HashSet<ImageView>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_expose, null);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_grundstueckexpose, menu);
// ...
}
#Override
public boolean onOptionsItemSelected(MenuItem mitem) {
switch (mitem.getItemId()) {
case android.R.id.home:
getActivity().finish();
return true;
case R.id.myitem:
textView.setText("clicked");
return true;
default:
return super.onOptionsItemSelected(mitem);
}
}
}
Has anyone else experienced something like this or has an idea on what could be the problem here?
The problem is that the MotionEvent does not handled correctly by internal class ActionMenuItemView (actually, there is no any specific behavior in this class).
So, I do not resolve initial problem, but I find workaround solution. I just override ActionMenuItemView.dispatchTouchEvent() and handle click and long-click manually using GestureDetector.
You can check this solution on github.
I don't know this exact problem, but I had a problem when flipping the device. The app was crashing. Finally, I've found that was problem of the Pager classes, because I was implementing them like you've implemented your SectionsPagerAdapter class.
I put the public classes that were on the main class on separated classes and the layouts worked well in vertical and horizontal position.
I don't know if this could be the problem, but you could try to create the corresponding classes instead of leaving them on the main class. Anyway, you pass to the SectionsPagerAdapter the FragmentManager, so you will not have any extra problem putting the public classes in their respective files.
Good luck!
The following bug will happen on an 2.3 device, my setup works fine on 4.x devices.
I have a ViewPager with some Fragments in it (they're all of the same class).
Every Fragment inflates it's own Menu, because the Menu Items may vary from Fragment to Fragment.
For test purposes, I have set up a Menu Item in the ActionBar (the ActionBar is shown on the bottom in the pic because it's a split ActionBar). When the Item is tapped, a TextView in the Fragment should be set to "clicked". This works in the beginning, but after flicking around a bit, this happens:
When the Menu Item is tapped, nothing happens. Instead, as soon as I swipe to the next Fragment, the next Fragment sets its TextView to "clicked". It seems like the ActionBar and it's Menu are associated with the next Fragment.
Heres a pic
And heres some code:
My Activity:
public class MyActivity extends SherlockFragmentActivity implements
MyFragment.InvalidateListener {
ViewPager viewPager;
SectionsPagerAdapter pagerAdapter;
public void invalidate() {
ActivityCompat.invalidateOptionsMenu(act);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.empty_viewpager);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(pagerAdapter);
viewPager.setCurrentItem(initialIndex);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = new MyFragment();
fragment.setHasOptionsMenu(true);
return fragment;
}
// ...
}
My Fragment:
public class MyFragment extends SherlockFragment {
HashSet<ImageView> runningImageTasks = new HashSet<ImageView>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_expose, null);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_grundstueckexpose, menu);
// ...
}
#Override
public boolean onOptionsItemSelected(MenuItem mitem) {
switch (mitem.getItemId()) {
case android.R.id.home:
getActivity().finish();
return true;
case R.id.myitem:
textView.setText("clicked");
return true;
default:
return super.onOptionsItemSelected(mitem);
}
}
}
Has anyone else experienced something like this or has an idea on what could be the problem here?
The problem is that the MotionEvent does not handled correctly by internal class ActionMenuItemView (actually, there is no any specific behavior in this class).
So, I do not resolve initial problem, but I find workaround solution. I just override ActionMenuItemView.dispatchTouchEvent() and handle click and long-click manually using GestureDetector.
You can check this solution on github.
I don't know this exact problem, but I had a problem when flipping the device. The app was crashing. Finally, I've found that was problem of the Pager classes, because I was implementing them like you've implemented your SectionsPagerAdapter class.
I put the public classes that were on the main class on separated classes and the layouts worked well in vertical and horizontal position.
I don't know if this could be the problem, but you could try to create the corresponding classes instead of leaving them on the main class. Anyway, you pass to the SectionsPagerAdapter the FragmentManager, so you will not have any extra problem putting the public classes in their respective files.
Good luck!
I want to change the visibility of menu items of a fragment activity (abs) when ever I change the fragment in the activity. The fragments are SherlockListFragments.
The menu items I want to show/hide are spinners I create on menu creation:
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
MenuInflater inflater = getSupportMenuInflater();
IcsSpinner herbSortSpinner = new IcsSpinner(this, null, R.attr.actionDropDownStyle);
SpinnerAdapter herbSortSpinnerAdapter = ArrayAdapter.createFromResource(this, R.array.herb_sort_elements, android.R.layout.simple_spinner_dropdown_item);
herbSortSpinner.setAdapter(herbSortSpinnerAdapter);
herbSortSpinner.setOnItemSelectedListener(this);
herbSortSpinner.setId(HERB_SPINNER_ID);
menu.add(R.string.menu_sort).setActionView(herbSortSpinner).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
IcsSpinner noteSortSpinner = new IcsSpinner(this, null, R.attr.actionDropDownStyle);
SpinnerAdapter noteSortSpinnerAdapter = ArrayAdapter.createFromResource(this, R.array.note_sort_elements, android.R.layout.simple_spinner_dropdown_item);
noteSortSpinner.setAdapter(noteSortSpinnerAdapter);
noteSortSpinner.setOnItemSelectedListener(this);
noteSortSpinner.setId(NOTE_SPINNER_ID);
menu.add(R.string.menu_sort).setActionView(noteSortSpinner).setVisible(false).setEnabled(false).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
inflater.inflate(R.menu.activity_lexicon, menu);
menuHolder.setMenu(menu);
return true;
}
My logic for switching the fragments is:
public boolean onNavigationItemSelected(int position, long itemId) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
switch(position) {
case 0:
List<Herb> herbList = findHerbsByIntent(getHerbLocale());
HerbListFragment herbListFragment = new HerbListFragment();
herbListFragment.setListAdapter(new HerbListAdapter(this, getHerbLocale(), herbList));
fragmentTransaction.replace(R.id.fragment_container, herbListFragment, HERB_LIST_FRAGMENT_TAG);
//menuHolder.showHerbsSpinner();
break;
case 1:
SymptomListFragment symptomListFragment = new SymptomListFragment();
symptomListFragment.setListAdapter(new SymptomListAdapter(this, getDefaultSymptomLocale()));
fragmentTransaction.replace(R.id.fragment_container, symptomListFragment, SYMPTOM_LIST_FRAGMENT_TAG);
//menuHolder.showHerbsSpinner();
break;
case 2:
NoteRepository noteRepository = new NoteRepository(this);
List<Note> notes = noteRepository.getNoticables(ReferedType.HERB);
NoteListFragment noteListFragment = new NoteListFragment();
noteListFragment.setListAdapter(new NoteListAdapter(this, getHerbLocale(), notes));
fragmentTransaction.replace(R.id.fragment_container, noteListFragment, NOTE_LIST_FRAGMENT_TAG);
//menuHolder.showNotesSpinner();
break;
case 3:
FavoriteRepository favoriteRepository = new FavoriteRepository(this);
Set<Integer> favoriteHerbs = favoriteRepository.getFavorables(ReferedType.HERB);
List<Herb> favoriteHerbList = herbRepository.getHerbsByIds(favoriteHerbs, getHerbLocale());
FavoriteHerbListFragment favoriteHerbListFragment = new FavoriteHerbListFragment();
favoriteHerbListFragment.setListAdapter(new HerbListAdapter(this, getHerbLocale(), favoriteHerbList));
fragmentTransaction.replace(R.id.fragment_container, favoriteHerbListFragment, HERB_LIST_FRAGMENT_TAG);
//menuHolder.showHerbsSpinner();
break;
}
fragmentTransaction.commit();
return true;
}
My first idea was to hold the menu object in a holder class and manipulate it there whenever I switch the fragment (in every case statement above).
static class MenuHolder {
private Menu mMenu;
void setMenu(Menu menu) {
mMenu = menu;
}
void showNotesSpinner() {
if (mMenu != null) {
mMenu.findItem(HERB_SPINNER_ID).setVisible(false).setEnabled(false);
mMenu.findItem(NOTE_SPINNER_ID).setVisible(true).setEnabled(true);
}
}
void showHerbsSpinner() {
if (mMenu != null) {
mMenu.findItem(NOTE_SPINNER_ID).setVisible(false).setEnabled(false);
mMenu.findItem(HERB_SPINNER_ID).setVisible(true).setEnabled(true);
}
}
}
My problem is that there is no menu item with the given ID which are activity local constants. I get an NPE here. Does anybody have an idea how I can fix that? Is there a better way to change the menu on switching fragments?
Best regards
Carsten
Is there a better way to change the menu on switching fragments?
May be yes :).
In your fragment onCreate, add this :
setHasOptionsMenu (true);
The doc :
Report that this fragment would like to participate in populating the options menu by receiving a call to onCreateOptionsMenu(Menu, MenuInflater) and related methods.
Override onPrepareOptionMenu method in your fragment class.
Prepare the Screen's standard options menu to be displayed. This is called right before the menu is shown, every time it is shown. You can use this method to efficiently enable/disable items or otherwise dynamically modify the contents.
Then, in this method, try to find your menu items by ID, and make them visible/unvisible, enabled/disabled, like this :
#Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(HERB_SPINNER_ID).setVisible(false).setEnabled(false);
}
Read More
In my case, I have 2 fragments that has a different menu item.
On my MainActivity:
FragmentA fragmentA = new FragmentA();
fragmentA.setTargetFragment(fragmentA, 0);
FragmentB fragmentB = new FragmentB();
fragmentB.setTargetFragment(fragmentB, 1);
and FragmentA and FragmentB has:
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if(hidden) {
getTargetFragment().setMenuVisibility(false);
}else{
getTargetFragment().setMenuVisibility(true);
}
}
this will let menu from fragment A be visible when B is leaving. If it’s going back from B to A old menus from A can be visible.
Reference: here
I have an ActionBar with mode ActionBar.NAVIGATION_MODE_TABS, I want to change icons which appear on ActionBar when I choose different tabs.
I did it successfully with an Activity, but it acted weird when I applied it again on another Activity.
Let's say I have 2 tabs: Tab1 and Tab2, and I want a Camera icon to appear when Tab2 is being chosen and disappear when Tab1 is being chosen. Moreover, Tab1 is the first tab will be chosen when the activity start.
So I did something like this:
Because I don't want the camera icon to appear at the first time HomeActivity start, so I set android:visible="false", then when I run below code: when HomeActivity starts, camera icon doesn't appear on ActionBar -> correct, but when I switch to tab2, camera icon doesn't appear on ActionBar too, but if I press "Menu" button -> a MenuItem which is "Camera" appears as an overflow actionbar item.
And if I set android:visible="true" -> except the camera icon appears at the first time first time HomeActivity start (incorrect!!!) -> then camera icon disappears/appears correctly when I change tab1/tab2.
Can anyone explain and give me a solution to make it works correctly.
Thanks!
This is my code:
actionbar_home.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/actionbar_home_camera"
android:title="#string/actionbar_rc_camera"
android:icon="#drawable/ic_action_camera"
android:showAsAction="always"
android:visible="false"/>
</menu>
HomeActivity.java
public class HomeActivity extends SherlockFragmentActivity {
private ActionBar actionBar;
private Menu menu;
private Tab1Fragment tab1Fragment;
private Tab2Fragment tab2Fragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
this.configActionBar();
this.createTabs();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.actionbar_home, menu);
this.menu = menu;
// pass objects to tabs
tab1Fragment.setOptionMenu(this.menu);
tab2Fragment.setOptionMenu(this.menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
private void configActionBar()
{
actionBar = this.getSupportActionBar();
// below line makes application icon in top-left corner become a button
actionBar.setHomeButtonEnabled(true);
// below line add an arrow "<" before the top-left icon
//ab.setDisplayHomeAsUpEnabled(true);
//actionBar.setDisplayShowTitleEnabled(true);
}
private void createTabs()
{
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = actionBar.newTab().setText(
Const.TAB_TITLE_HOME_TAB1);
ActionBar.Tab tab2 = actionBar.newTab().setText(
Const.TAB_TITLE_HOME_TAB2);
// create the two fragments we want to use for display content
tab1Fragment = new Tab1Fragment();
tab2Fragment = new Tab2Fragment();
// add listener
tab1.setTabListener(
new HomeTabListener(tab1Fragment));
tab2.setTabListener(
new HomeTabListener(tab2Fragment));
actionBar.addTab(tab1);
actionBar.addTab(tab2);
}
}
BaseTabFragment.java
public class BaseTabFragment extends SherlockFragment{
protected View root;
protected Menu optionMenu;
protected SherlockFragmentActivity container;
public Menu getOptionMenu() {
return optionMenu;
}
public void setOptionMenu(Menu optionMenu) {
this.optionMenu = optionMenu;
}
public SherlockFragmentActivity getContainer() {
return container;
}
public void setContainer(SherlockFragmentActivity container) {
this.container = container;
}
}
Tab1Fragment.java
public class Tab1Fragment extends BaseTabFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
root = inflater.inflate(R.layout.tab1_layout,
container, false);
this.changeActionBarLayout();
return root;
}
private void changeActionBarLayout()
{
try {
this.optionMenu.findItem(R.id.actionbar_home_camera)
.setVisible(false);
} catch (NullPointerException e)
{
// e will be thrown at the first time MainActivity is called
}
}
}
Tab2Fragment.java
public class Tab2Fragment extends BaseTabFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
root = inflater.inflate(R.layout.tab2_layout,
container, false);
this.changeActionBarLayout();
return root;
}
private void changeActionBarLayout()
{
try {
this.optionMenu.findItem(R.id.actionbar_home_camera)
.setVisible(true);
// Camera Icon still doesn't appear on ActionBar even when I set it as SHOW_AS_ACTION_ALWAYS
this.optionMenu.findItem(R.id.actionbar_home_camera)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} catch (NullPointerException e)
{
// e will be thrown at the first time MainActivity is called
}
}
}
I had the same problem and found an easy solution for it: Just implement onPreparePanel in your Activity and return true even if all items are invisible.
public class HomeActivity extends SherlockFragmentActivity {
[...]
#Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
super.onPreparePanel(featureId, view, menu); // this returns false if all items are hidden
return true; // return true to prevent the menu's deletion
}
[...]
}
After some investigation, I think this is a bug, but I don't know it's ActionBarSherlock's bug or Android ActionBar's bug.
Reason
The reason is because of there's ONLY ONE MenuItem in option menu, let's say the MenuItem is mItem. When I set mItem.setVisible(false), there will be no item left on ActionBar, then apparently, the space which contains ActionBar MenuItems is removed. After that, when I set mItem.setVisible(true), the space is not restored, in that case, my mItem is considered as an overflow MenuItem -> it's only revealed when use Menu hardware button of devices.
Solution
Use more than one MenuItem
Subtly, use combination of setEnable(false) and setIcon(null) and setTitle("") instead of setVisible(false)
I faced the same problem and solved in this way:
Firstly I save the com.actionbarsherlock.view.Menu when onCreateOptionsMenu(Menu menu)
#Override
public boolean onCreateOptionsMenu ( Menu menu )
{
ShowOrEditTaskActivity.this.getSupportMenuInflater().inflate(R.menu.edit_or_show_task_menu, menu);
setOptionsMenu ( menu );
optionsMenu.findItem ( R.id.save_action ).setVisible ( false );
return super.onCreateOptionsMenu ( menu ) ;
}
Then I check in every
public boolean onOptionsItemSelected ( MenuItem item )
call like this:
#Override
public boolean onOptionsItemSelected ( MenuItem item )
{
if ( item.getItemId ( ) == R.id.edit_action )
{
isEditMode ( true ) ;
optionsMenu.findItem ( R.id.edit_action ).setVisible ( false );
optionsMenu.findItem ( R.id.save_action ).setVisible ( true );
}...
Hope this helps..
I had the same problem and I found out that setVisible(true) works when there is at least another MenuItem visible. I hope this can be helpful to someone.
In the application I am developing I am using a ViewPager with fragments and each fragment constructs its own menu independently of all of the other fragments in the ViewPager.
The issue is that sometimes the fragments that are initialised by the ViewPager by default (i.e in it's initial state) are not having their items populated into the action items menu. What's worse is that this issue only occurs intermittently. If I swipe through the ViewPager enough so that the fragments are forced to re-initialise them selves, when I swipe back, the menu populates correctly.
Activity code:
package net.solarnz.apps.fragmentsample;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
public class FragmentSampleActivity extends Activity {
private ViewPagerAdapter mViewPagerAdapter;
private ViewPager mViewPager;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (mViewPagerAdapter == null) {
mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
}
mViewPager = (ViewPager) findViewById(R.id.log_pager);
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setCurrentItem(0);
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 8;
}
#Override
public Fragment getItem(int position) {
Fragment f = Fragment1.newInstance(position);
// f.setRetainInstance(true);
f.setHasOptionsMenu(true);
return f;
}
}
}
Fragment code:
package net.solarnz.apps.fragmentsample;
import android.app.Fragment;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
public class Fragment1 extends Fragment {
int mNum;
static Fragment newInstance(int num) {
Fragment1 f = new Fragment1();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mNum = getArguments() != null ? getArguments().getInt("num") : 0;
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_list, menu);
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<android.support.v4.view.ViewPager
android:id="#+id/log_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_refresh"
android:title="Refresh"
android:icon="#android:drawable/ic_delete"
android:showAsAction="ifRoom|withText" />
</menu>
Action menu being populated:
http://i.stack.imgur.com/QFMDd.png
Action menu not being populated:
http://i.stack.imgur.com/sH5Pp.png
You should read this (by xcolw...)
Through experimentation it seems like the root cause is invalidateOptionsMenu getting called more than one without a break on the main thread to process queued up jobs. A guess - this would matter if some critical part of menu creation was deferred via a post, leaving the action bar in a bad state until it runs.
There are a few spots this can happen that aren't obvious:
calling viewPager.setCurrentItem multiple times for the same item
calling viewPager.setCurrentItem in onCreate of the activity. setCurrentItem causes an option menu invalidate, which is immediately followed by the activity's option menu invalidate
Workarounds I've found for each
Guard the call to viewPager.setCurrentItem
if (viewPager.getCurrentItem() != position)
viewPager.setCurrentItem(position);
Defer the call to viewPager.setCurrentItem in onCreate
public void onCreate(...) {
...
view.post(new Runnable() {
public void run() {
// guarded viewPager.setCurrentItem
}
}
}
After these changes options menu inside the view pager seems to work as expected. I hope someone can shed more light into this.
source http://code.google.com/p/android/issues/detail?id=29472
The simple answer is to not use menus within fragments in the ViewPager.
If you do need to use menus within the fragments, what I suggest is loading the menu's through the onCreateOptionsMenu method in the parent Activity. Obviously you will need to be able to determine which menu to show.
I was able to achieve this by using class reflection.
You will also need to use the invalidateOptionsMenu method each time you switch pages. You will need a OnPageChangeListener to call this when the ViewPager changes pages.
I also had same issue. In my case I have one activity with viewpager that contains two fragments, every fragment inflate its own action menu but fragment actions menu not shown.
View pager adapter code
public class ScreensAdapter extends FragmentPagerAdapter {
public TrackerScreensAdapter(Context context, FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 2;
}
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position){
case 0:
fragment = new Fragment1();
break;
case 1:
fragment = new Fragment2();
break;
}
return fragment;
}
}
Activity on create
screensAdapter = new ScreensAdapter(this, getFragmentManager());
viewPager.setAdapter(screensAdapter);
This way my viewPager has two fragments, every fragment fire its own task in onActivityCreated, obtain data and draw its layout based on obtained data. Also every fragment has onCreateOptionsMenu
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
MyTask task = new MyTask();
task.setTaskListener(this);
task.execute();
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_menu, menu);
}
Spent many times to solve this problem and figure out why fragment menu not shows.
All that I was need is
screenAdapter = new ScreenAdapter(this, getFragmentManager());
viewPager.post(new Runnable() {
public void run() {
viewPager.setAdapter(screenAdapter);
}
});
In my case I traced the root cause of the issue to what I believe is a bug in FragmentStatePagerAdapter which is calling Fragment#setMenuVisibility to false and failing to properly set it back to true when it restores it's state.
Workarounds:
Use FragmentPagerAdapter instead of FragmentStatePagerAdapter
If you must use FragmentStatePagerAdapter, in your adapter subclass override setPrimaryItem like so:
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
//This is a workaround for a bug in FragmentStatePagerAdapter
Fragment currentItem = getItem(position);
if (currentItem != null) {
currentItem.setMenuVisibility(true);
currentItem.setUserVisibleHint(true);
}
}
First create a method in the sub class of FragmentPagerAdapter to get the current fragment
public SherlockFragment getFragment() {
return currentFragment;
}
#Override
public void onTabSelected(final Tab tab, FragmentTransaction ft) {
((SherlockFragmentActivity) mContext).invalidateOptionsMenu();
Fragment f = ((SherlockFragmentActivity) mContext)
.getSupportFragmentManager().findFragmentByTag(
makeFragmentName(tab.getPosition()));
currentFragment=(SherlockFragment) f;
}
Now override below methods in Main Actvity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mTabsAdapter.getPositionOfTabSelected() != 0) {
menu.add("Read").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Write").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Clear").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Factory data reset").setShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
return super.onCreateOptionsMenu(menu);
}
Now call the onOptionItemSelected from activity to fragment
#Override
public boolean onOptionsItemSelected(MenuItem item) {
mTabsAdapter.getFragment().onOptionsItemSelected(item);
return super.onOptionsItemSelected(item);
}
I solved a very similar issue in which the Action bar icons assigned by the fragment inside of a ViewPager were disappearing onPause(). They would reappear when the Fragment came back into view and the user swiped left or right, but not immediately. The solution was calling notifyDataSetChanged() on the PagerAdapter in the onResume() method of the fragment.
#Override
public void onResume() {
mPagerAdapter.notifyDataSetChanged();
}
I was having this problem with the Action Bar Items while I was using HorizontalScrollView to show the tabs but I changed to PagerTitleStrip and the problem was solved.
Perhaps this information can help someone else.
My solution to this problem was to only inflate fragment menus if the fragment is currently visible. This solution may be too specific for your purposes, but it might help someone.
In the main activity:
boolean isFragmentVisible(int fragmentIndex) { ... }
In onCreateOptionsMenu() in your fragment:
if ( getActivity().isFragmentVisible(HOME_FRAGMENT_POS) ) {
inflater.inflate(R.menu.menu_home_fragment, menu);
}