Viewpager初始片段中的操作项未显示

时间:2012-02-18 03:01:24

标签: android android-fragments android-actionbar android-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

8 个答案:

答案 0 :(得分:21)

您应该阅读此内容(通过xcolw ...)

通过实验,似乎根本原因是invalidateOptionsMenu在主线程上不间断地被调用多个来处理排队的作业。一个猜测 - 如果菜单创建的某些关键部分是通过帖子延迟,将操作栏保持在错误状态直到它运行,这就很重要了。

这可能会发生一些不明显的地方:

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

  2. 在活动的onCreate中调用viewPager.setCurrentItem。 setCurrentItem导致选项菜单无效,紧接着活动的选项菜单无效

  3. 我为每个

    找到的解决方法
    1. 保护对viewPager.setCurrentItem的调用

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

      中的viewPager.setCurrentItem
      public void onCreate(...) {
          ...
          view.post(new Runnable() {
              public void run() {
                  // guarded viewPager.setCurrentItem
              }
          }
      }
      
    3. 在这些更改之后,视图寻呼机内的选项菜单似乎按预期工作。我希望有人能够对此有所了解。

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

答案 1 :(得分:8)

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

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

答案 2 :(得分: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);
        }
    });

答案 3 :(得分: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;
 }

现在覆盖主要行动中的以下方法

@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从activity调用到片段

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

当我使用 Horizo​​ntalScrollView 显示标签但我更改为 PagerTitleStrip 时问题已解决,我在使用操作栏项时出现此问题。

也许这些信息可以帮助其他人。

答案 6 :(得分:0)

在我的情况下,我将问题的根本原因追溯到我认为FragmentStatePagerAdapter中的一个错误,该错误将Fragment#setMenuVisibility调用为false并且在恢复它时未能将其正确地设置为true。状态。

解决方法:

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

答案 7 :(得分:0)

我对此问题的解决方案是仅在片段当前可见时才膨胀片段菜单。此解决方案对于您的目的而言可能过于具体,但可能会对某人有所帮助。

在主要活动中:

boolean isFragmentVisible(int fragmentIndex) { ... }

在片段的 onCreateOptionsMenu() 中:

if ( getActivity().isFragmentVisible(HOME_FRAGMENT_POS) ) {
    inflater.inflate(R.menu.menu_home_fragment, menu);
}