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

bar*_*ide 141 android android-adapter android-fragments android-viewpager android-listfragment

我正在使用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;
        }
   ...
Run Code Online (Sandbox Code Playgroud)

现在你可以看到,我的第一次尝试是整个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.

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

Pēt*_*une 253

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)
Run Code Online (Sandbox Code Playgroud)

  • ...以防万一有人想知道,ViewPager会跟踪被查看片段的索引(ViewPager.getCurrentItem()),这是上例中的49 ...但我必须说我令人惊讶的是,ViewPager API不会直接返回对实际Fragment的引用. (12认同)
  • 使用`FragmentStatePagerAdapter`,使用上面的代码与`a.instantiateItem(viewPager,viewPager.getCurrentItem());`(以摆脱49)@ mblackwell8提出的工作完美. (6认同)
  • 我只需将"a"设为PagerAdapter(PagerAdapter a = pager.getAdapter();)即可简化.甚至是MyFragment f49 =(MyFragment)pager.getAdapter().instantiateItem(pager,49); instantiateItem()来自PagerAdapter,因此您不必将其类型化以调用instantiateItem() (5认同)

bar*_*ide 153

好吧,我想我已经找到了一种在我自己的问题中执行请求b)的方法,所以我会分享其他人的利益.一个ViewPager内部片段的标记的形式为"android:switcher:VIEWPAGER_ID:INDEX",其中VIEWPAGER_IDR.id.viewpager在XML布局,并且指数为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
         }
      }
Run Code Online (Sandbox Code Playgroud)

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

  • @dgmltn,`makeFragmentName`是`private static`方法,因此你无法访问它. (8认同)
  • 由于这不在官方文档中,我不会使用它.如果他们在将来的版本中更改标签怎么办? (5认同)
  • 我登录后只是给你的答案和问题+1. (4认同)
  • FragmentPagerAdapter带有一个静态方法`makeFragmentName`,用于生成Tag,你可以使用它代替一个稍微不那么hacky的方法:`HomeListFragment fragment =(HomeListFragment)getSupportFragmentManager().findFragmentByTag(FragmentPagerAdapter.makeFragmentName(R.id.viewpager) ,0));` (4认同)

fay*_*lon 58

每次实例化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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 它适用于`FragmentPagerAdaper`,但对于`FragmentStatePagerAdaper`失败(`Fragment.getTag()`总是返回`null`. (5认同)

Fra*_*ank 35

如果你问我,下面的第二个解决方案,跟踪所有"活动"片段页面,更好: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;
}
Run Code Online (Sandbox Code Playgroud)

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

public void destroyItem(View container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferences.remove(position);
}
Run Code Online (Sandbox Code Playgroud)

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

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);
Run Code Online (Sandbox Code Playgroud)

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

public MyFragment getFragment(int key) {
    return mPageReferences.get(key);
}
Run Code Online (Sandbox Code Playgroud)

"

  • 如果您使用SparseArray而不是地图,那就更好了 (3认同)
  • 这将破坏生命周期事件.请参见http://stackoverflow.com/a/24662049/236743 (2认同)

Rya*_*n R 13

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

以下是IOSched应​​用HomeActivity 的重要部分.特别注意评论,其中有关键:

@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

并将您的实例存储FragmentsFragmentPagerAdapter类似的内容中:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

此外,请记住保护您的Fragment电话:

    if (mSocialStreamFragment != null) {
        mSocialStreamFragment.refresh();
    }
Run Code Online (Sandbox Code Playgroud)