29

在我正在开发的应用程序中,我使用带有片段的ViewPager,每个片段独立于ViewPager中的所有其他片段构建自己的菜单。Viewpager初始片段中的操作项不被显示

问题是有时默认情况下由ViewPager初始化(即处于初始状态)的片段没有将其项目填充到操作项目菜单中。更糟糕的是,这个问题只是间歇性地发生。如果我滑过ViewPager足够多,以便片段被迫重新初始化它们,当我向后滑动时,菜单正确填充。

活动码:

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; 
     } 
    } 
} 

片段代码:

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); 
    } 

} 

布局:

<?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> 

菜单:

<?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> 

操作菜单被填充:不被填充 http://i.stack.imgur.com/QFMDd.png

操作菜单: http://i.stack.imgur.com/sH5Pp.png

+0

与Solarnz同意似乎没有明显的解决方法。我创建了一个[演示程序](https://github.com/snodnipper/page-flipper-race-condition/),可以让您可靠地重现问题。同样的问题似乎是[在ActionBarSherlock中报告](https://github.com/JakeWharton/ActionBarSherlock/issues/351),我提出了这个问题(http://code.google.com/p/android/issues/detail?id = 29472)在android问题跟踪器中。 – snodnipper 2012-04-25 21:27:46

回答

21

你应该阅读(由xcolw ...)

通过实验好像根本原因invalidateOptionsMenu获取调用多个无主线程依靠休息来处理排队的作业。猜测 - 如果菜单创建的某些关键部分通过帖子被延期,这会很重要,使操作栏处于不良状态,直到它运行。

有几个点会发生这种情况是不是很明显:

  1. 调用viewPager.setCurrentItem多次对同一项目

  2. 调用viewPager.setCurrentItem在活动的onCreate。 setCurrentItem导致选项菜单无效,这是紧跟活动的选项菜单无效

解决办法,我发现每个

  1. 卫队调用viewPager。setCurrentItem

    if (viewPager.getCurrentItem() != position) 
        viewPager.setCurrentItem(position); 
    
  2. 推迟调用viewPager.setCurrentItem中的onCreate

    public void onCreate(...) { 
        ... 
        view.post(new Runnable() { 
         public void run() { 
          // guarded viewPager.setCurrentItem 
         } 
        } 
    } 
    

之后,这些更改选项视图寻呼机菜单里面似乎按预期方式工作。我希望有人能够更多地了解这一点。

http://code.google.com/p/android/issues/detail?id=29472

+0

非常好。谢谢。 – 2012-11-28 08:00:24

+0

请提供完整的snipet如何使用viewpager无标签,这对我来说不是很清楚。谢谢。 – 2013-07-30 23:23:24

+0

它没有为我工作。 mViewPager.post(新的Runnable(){@Override 公共 无效的run(){ mViewPager.setAdapter(新FragPagerAdapter(getSupportFragmentManager(),MainActivity.this));! 如果(mViewPager.getCurrentItem()= 1){ mViewPager.setCurrentItem(1); } } }); – 2017-03-29 21:17:55

8

简单的答案是在ViewPager段内未使用菜单。 如果您确实需要使用片段内的菜单,我建议通过父活动中的onCreateOptionsMenu方法加载菜单。显然你需要能够确定显示哪个菜单。

我能够通过使用类反射来实现这一点。 每次切换页面时,您还需要使用invalidateOptionsMenu方法。当ViewPager更改页面时,您将需要一个OnPageChangeListener来调用此方法。

+0

你可以请分享代码吗? – 2013-05-15 13:19:06

0

首先创建在子类FragmentPagerAdapter的方法来获得当前片段

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; 
} 

现在重写以下方法主要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); 
} 

现在打电话onOptionItemSelected从活动到片段

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    mTabsAdapter.getFragment().onOptionsItemSelected(item); 
    return super.onOptionsItemSelected(item); 
} 
3

我也有同样的问题。在我的情况下,我有一个viewpager活动,其中包含两个片段,每个片段膨胀自己的操作菜单,但片段操作菜单未显示。

查看传呼机适配器代码

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; 
    } 

} 

活动上创建

screensAdapter = new ScreensAdapter(this, getFragmentManager()); 
    viewPager.setAdapter(screensAdapter); 

这样我viewPager有两个片段,每个片段火在onActivityCreated自己的任务,获取数据并绘制基于其布局获得的数据。同时每个片段具有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); 
} 

花了很多时间来解决这个问题,并找出原因片段菜单中未显示。 的一切,我需要的是

screenAdapter = new ScreenAdapter(this, getFragmentManager()); 
    viewPager.post(new Runnable() { 
     public void run() { 
      viewPager.setAdapter(screenAdapter); 
     } 
    }); 
+0

谢谢!设置适配器里面的runnable解决它对我来说 – 2015-06-25 09:32:58

0

我解决了一个非常类似的问题,其中由片段ViewPager的内部分配操作栏图标正在消失的onPause()。当片段重新出现并且用户向左或向右滑动时,他们会再次出现,但不会立即。解决方案是在片段的onResume()方法的PagerAdapter上调用notifyDataSetChanged()。

@Override 
public void onResume() { 
    mPagerAdapter.notifyDataSetChanged(); 
} 
0

,而我是用Horizo​​ntalScrollView显示的分页,但我改PagerTitleStrip,问题就解决了我是有这个问题,操作栏项目。

也许这个信息可以帮助别人。

0

在我来说,我跟踪这个问题的根源是什么,我相信是在FragmentStatePagerAdapter一个错误,这是调用Fragment#setMenuVisibility为false,并没有正确设置回真实的,当它恢复它的状态。

解决方法:

  1. 使用FragmentPagerAdapter而不是FragmentStatePagerAdapter

  2. 如果必须使用FragmentStatePagerAdapter,在适配器中的子类覆盖setPrimaryItem像这样:

    @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); 
        } 
    } 
    
+0

这些方法都无法工作。 – Proverbio 2016-11-01 18:13:45