首页 文章

作为ViewPager的一部分更新ListFragment中的数据

提问于
浏览
138

我正在使用Android中的v4兼容性ViewPager . 我的FragmentActivity有一堆数据,可以在我的ViewPager中的不同页面上以不同的方式显示 . 到目前为止,我只有3个相同的ListFragment实例,但将来我将有3个不同的ListFragments实例 . ViewPager位于垂直电话屏幕上,列表不是并排的 .

现在,ListFragment上的一个按钮启动一个单独的整页活动(通过FragmentActivity),它返回并且FragmentActivity修改数据,保存数据,然后尝试更新其ViewPager中的所有视图 . 就在这里,我被卡住了 .

public class ProgressMainActivity extends FragmentActivity
{
    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    ...
        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager) findViewById(R.id.viewpager);
        mPager.setAdapter(mAdapter);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        ...
        updateFragments();
        ...
    }
    public void updateFragments()
    {
        //Attempt 1:
        //mAdapter.notifyDataSetChanged();
        //mPager.setAdapter(mAdapter);

        //Attempt 2:
        //HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentById(mAdapter.fragId[0]);
        //fragment.updateDisplay();
    }

    public static class MyAdapter extends FragmentPagerAdapter implements
         TitleProvider
    {
      int[] fragId = {0,0,0,0,0};
      public MyAdapter(FragmentManager fm)
      {
         super(fm);
      }
      @Override
      public String getTitle(int position){
         return titles[position];
      }
      @Override
      public int getCount(){
         return titles.length;
      }

      @Override
      public Fragment getItem(int position)
      {

         Fragment frag = HomeListFragment.newInstance(position);
         //Attempt 2:
         //fragId[position] = frag.getId();
         return frag;
      }

      @Override
      public int getItemPosition(Object object) {
         return POSITION_NONE; //To make notifyDataSetChanged() do something
     }
   }

    public class HomeListFragment extends ListFragment
    {
    ...
        public static HomeListFragment newInstance(int num)
        {
            HomeListFragment f = new HomeListFragment();
            ...
            return f;
        }
   ...

现在你可以看到,我的第一次尝试是整个FragmentPagerAdapter上的notifyDataSetChanged,这有时会显示更新数据,但是其他人我得到了IllegalStateException:在onSaveInstanceState之后无法执行此操作 .

我的第二次尝试调用我的ListFragment中调用更新函数,但getItem中的getId返回0.根据我尝试过的文档

使用findFragmentById()或findFragmentByTag()从FragmentManager获取对Fragment的引用

但我不知道我的碎片的标签或ID!我有一个用于ViewPager的android:id =“@ id / viewpager”,以及一个用于ListFragment布局中我的ListView的android:id =“@ android:id / list”,但我不认为这些是有用的 .

那么,我怎么能:a)一次性安全地更新整个ViewPager(理想情况下将用户返回到之前的页面) - 用户可以看到视图更改 . 或者最好是,b)在每个受影响的ListFragment中调用一个函数来手动更新ListView .

任何帮助都将被感激地接受!

10 回答

  • 33

    每次实例化Fragement时尝试记录标记 .

    public class MPagerAdapter extends FragmentPagerAdapter {
        private Map<Integer, String> mFragmentTags;
        private FragmentManager mFragmentManager;
    
        public MPagerAdapter(FragmentManager fm) {
            super(fm);
            mFragmentManager = fm;
            mFragmentTags = new HashMap<Integer, String>();
        }
    
        @Override
        public int getCount() {
            return 10;
        }
    
        @Override
        public Fragment getItem(int position) {
            return Fragment.instantiate(mContext, AFragment.class.getName(), null);
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Object obj = super.instantiateItem(container, position);
            if (obj instanceof Fragment) {
                // record the fragment tag here.
                Fragment f = (Fragment) obj;
                String tag = f.getTag();
                mFragmentTags.put(position, tag);
            }
            return obj;
        }
    
        public Fragment getFragment(int position) {
            String tag = mFragmentTags.get(position);
            if (tag == null)
                return null;
            return mFragmentManager.findFragmentByTag(tag);
        }
    }
    
  • 2

    Barkside的答案适用于 FragmentPagerAdapter ,但不适用于 FragmentStatePagerAdapter ,因为它没有在传递给 FragmentManager 的片段上设置标签 .

    使用 FragmentStatePagerAdapter 似乎我们可以使用 instantiateItem(ViewGroup container, int position) 调用 . 它返回位于 position 位置的片段的引用 . 如果 FragmentStatePagerAdapter 已经存在对相关片段的引用, instantiateItem 只返回对该片段的引用,并且不会再调用 getItem() 来实例化它 .

    所以,假设我正在查看片段#50,并希望访问片段#49 . 由于它们很接近,#49很可能已经被实例化 . 所以,

    ViewPager pager = findViewById(R.id.viewpager);
    FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();
    MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49)
    
  • 150

    好吧,我想我会分享其他人的利益 . ViewPager中的片段标记采用 "android:switcher:VIEWPAGER_ID:INDEX" 格式,其中 VIEWPAGER_ID 是XML布局中的 R.id.viewpager ,INDEX是viewpager中的位置 . 因此,如果位置已知(例如0),我可以在 updateFragments() 中执行:

    HomeListFragment fragment = 
              (HomeListFragment) getSupportFragmentManager().findFragmentByTag(
                           "android:switcher:"+R.id.viewpager+":0");
          if(fragment != null)  // could be null if not instantiated yet
          {
             if(fragment.getView() != null) 
             {
                // no need to call if fragment's onDestroyView() 
                //has since been called.
                fragment.updateDisplay(); // do what updates are required
             }
          }
    

    我不知道这是否是一种有效的方式,但它会做到更好的建议 .

  • 1

    如果你问我,下面的第二个解决方案,跟踪所有"active"片段页面,更好:http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

    来自barkside的答案对我来说太难了 .

    您可以跟踪所有“活动”片段页面 . 在这种情况下,您将跟踪ViewPager使用的FragmentStatePagerAdapter中的片段页面 .

    private final SparseArray<Fragment> mPageReferences = new SparseArray<Fragment>();
    
    public Fragment getItem(int index) {
        Fragment myFragment = MyFragment.newInstance();
        mPageReferences.put(index, myFragment);
        return myFragment;
    }
    

    为了避免保留对“非活动”片段页面的引用,我们需要实现FragmentStatePagerAdapter的destroyItem(...)方法:

    public void destroyItem(View container, int position, Object object) {
        super.destroyItem(container, position, object);
        mPageReferences.remove(position);
    }
    

    ...当您需要访问当前可见的页面时,您可以调用:

    int index = mViewPager.getCurrentItem();
    MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
    MyFragment fragment = adapter.getFragment(index);
    

    ... MyAdapter的getFragment(int)方法如下所示:

    public MyFragment getFragment(int key) {
        return mPageReferences.get(key);
    }
    

  • 0

    好的,在上面的@barkside测试方法后,我无法使用我的应用程序 . 然后我记得IOSched2012 app也使用 viewpager ,这就是我找到解决方案的地方 . 它不使用任何片段ID或标签,因为这些不是由 viewpager 以易于访问的方式存储的 .

    这是来自IOSched应用程序HomeActivity的important parts . 要特别注意 comment ,其中有关键:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    
        // Since the pager fragments don't have known tags or IDs, the only way to persist the
        // reference is to use putFragment/getFragment. Remember, we're not persisting the exact
        // Fragment instance. This mechanism simply gives us a way to persist access to the
        // 'current' fragment instance for the given fragment (which changes across orientation
        // changes).
        //
        // The outcome of all this is that the "Refresh" menu button refreshes the stream across
        // orientation changes.
        if (mSocialStreamFragment != null) {
            getSupportFragmentManager().putFragment(outState, "stream_fragment",
                    mSocialStreamFragment);
        }
    }
    
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        if (mSocialStreamFragment == null) {
            mSocialStreamFragment = (SocialStreamFragment) getSupportFragmentManager()
                    .getFragment(savedInstanceState, "stream_fragment");
        }
    }
    

    并将 Fragments 中的实例存储在_1077612中,如下所示:

    private class HomePagerAdapter extends FragmentPagerAdapter {
        public HomePagerAdapter(FragmentManager fm) {
            super(fm);
        }
    
        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return (mMyScheduleFragment = new MyScheduleFragment());
    
                case 1:
                    return (mExploreFragment = new ExploreFragment());
    
                case 2:
                    return (mSocialStreamFragment = new SocialStreamFragment());
            }
            return null;
        }
    

    另外,请记住像这样保护你的 Fragment 电话:

    if (mSocialStreamFragment != null) {
            mSocialStreamFragment.refresh();
        }
    
  • 1

    您可以复制 FragmentPagerAdapter 并修改一些源代码,添加 getTag() 方法

    例如

    public abstract class AppFragmentPagerAdapter extends PagerAdapter {
    private static final String TAG = "FragmentPagerAdapter";
    private static final boolean DEBUG = false;
    
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;
    private Fragment mCurrentPrimaryItem = null;
    
    public AppFragmentPagerAdapter(FragmentManager fm) {
        mFragmentManager = fm;
    }
    
    
    public abstract Fragment getItem(int position);
    
    @Override
    public void startUpdate(ViewGroup container) {
    }
    
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
    
        final long itemId = getItemId(position);
    
    
        String name = getTag(position);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
    
            mCurTransaction.add(container.getId(), fragment,
                    getTag(position));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }
    
        return fragment;
    }
    
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                + " v=" + ((Fragment) object).getView());
        mCurTransaction.detach((Fragment) object);
    }
    
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }
    
    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
        }
    }
    
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }
    
    @Override
    public Parcelable saveState() {
        return null;
    }
    
    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
    }
    
    
    public long getItemId(int position) {
        return position;
    }
    
    private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }
    
    protected abstract String getTag(int position);
    }
    

    然后扩展它,覆盖这些抽象方法,不需要害怕Android Group的改变

    FragmentPageAdapter 将来的源代码

    class TimeLinePagerAdapter extends AppFragmentPagerAdapter {
    
    
        List<Fragment> list = new ArrayList<Fragment>();
    
    
        public TimeLinePagerAdapter(FragmentManager fm) {
            super(fm);
            list.add(new FriendsTimeLineFragment());
            list.add(new MentionsTimeLineFragment());
            list.add(new CommentsTimeLineFragment());
        }
    
    
        public Fragment getItem(int position) {
            return list.get(position);
        }
    
        @Override
        protected String getTag(int position) {
            List<String> tagList = new ArrayList<String>();
            tagList.add(FriendsTimeLineFragment.class.getName());
            tagList.add(MentionsTimeLineFragment.class.getName());
            tagList.add(CommentsTimeLineFragment.class.getName());
            return tagList.get(position);
        }
    
    
        @Override
        public int getCount() {
            return list.size();
        }
    
    
    }
    
  • 55

    也没有问题的工作:

    页面片段布局中的某个位置:

    <FrameLayout android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" android:id="@+id/fragment_reference">
         <View android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/>
    </FrameLayout>
    

    在片段的onCreateView()中:

    ...
    View root = inflater.inflate(R.layout.fragment_page, container, false);
    ViewGroup ref = (ViewGroup)root.findViewById(R.id.fragment_reference);
    ref.setTag(this);
    ref.getChildAt(0).setTag("fragment:" + pageIndex);
    return root;
    

    以及从ViewPager返回Fragment的方法(如果存在):

    public Fragment getFragment(int pageIndex) {        
            View w = mViewPager.findViewWithTag("fragment:" + pageIndex);
            if (w == null) return null;
            View r = (View) w.getParent();
            return (Fragment) r.getTag();
    }
    
  • 251

    或者,您可以像这样覆盖 setPrimaryItem 中的 setPrimaryItem 方法:

    public void setPrimaryItem(ViewGroup container, int position, Object object) { 
        if (mCurrentFragment != object) {
            mCurrentFragment = (Fragment) object; //Keep reference to object
            ((MyInterface)mCurrentFragment).viewDidAppear();//Or call a method on the fragment
        }
    
        super.setPrimaryItem(container, position, object);
    }
    
    public Fragment getCurrentFragment(){
        return mCurrentFragment;
    }
    
  • 12

    我想提出我的方法,以防它可以帮助其他人:

    这是我的寻呼机适配器:

    public class CustomPagerAdapter extends FragmentStatePagerAdapter{
        private Fragment[] fragments;
    
        public CustomPagerAdapter(FragmentManager fm) {
            super(fm);
            fragments = new Fragment[]{
                    new FragmentA(),
                    new FragmentB()
            };
        }
    
        @Override
        public Fragment getItem(int arg0) {
            return fragments[arg0];
        }
    
        @Override
        public int getCount() {
            return fragments.length;
        }
    
    }
    

    在我的活动中,我有:

    public class MainActivity {
            private ViewPager view_pager;
            private CustomPagerAdapter adapter;
    
    
            @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            adapter = new CustomPagerAdapter(getSupportFragmentManager());
            view_pager = (ViewPager) findViewById(R.id.pager);
            view_pager.setAdapter(adapter);
            view_pager.setOnPageChangeListener(this);
              ...
             }
    
    }
    

    然后获取当前片段我所做的是:

    int index = view_pager.getCurrentItem();
    Fragment currentFragment = adapter.getItem(index);
    
  • 0

    这是我的解决方案,因为我不需要跟踪我的标签,无论如何都需要刷新它们 .

    Bundle b = new Bundle();
        b.putInt(Constants.SharedPreferenceKeys.NUM_QUERY_DAYS,numQueryDays);
        for(android.support.v4.app.Fragment f:getSupportFragmentManager().getFragments()){
            if(f instanceof HomeTermFragment){
                ((HomeTermFragment) f).restartLoader(b);
            }
        }
    

相关问题