将ListFragment中的数据更新为ViewPager的一部分
我在Android中使用v4兼容性ViewPager。 我的FragmentActivity有一堆数据,这些数据将以不同的方式显示在ViewPager的不同页面上。 到目前为止,我只有3个相同的ListFragment实例,但将来我会有3个不同的ListFragments实例。 ViewPager在垂直电话屏幕上,列表不是并排的。
现在,ListFragment上的一个button启动一个单独的整页活动(通过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上通知DataSetChanged,并且这有时会更新数据,但是其他的我有一个IllegalStateException:在onSaveInstanceState之后不能执行这个动作。
我的第二次尝试involed试图调用我的ListFragment更新函数,但getId getItem返回0.根据我试过的文档
使用findFragmentById()或findFragmentByTag()获取对FragmentManager的片段的引用
但我不知道我的碎片的标签或ID! 我有ListPragment的android:id =“@ + id / viewpager”,ListFragment布局中的ListView的android:id =“@ android:id / list”,但我不认为这些是有用的。
那么,我该怎么做:a)一次性安全地更新整个ViewPager(理想情况下将用户返回到之前的页面) – 用户可以看到视图更改。 或者,最好是b)在每个受影响的ListFragment中调用一个函数来手动更新ListView。
任何帮助将被感激地接受!
尝试每次实例化Fragement时logging标签。
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); } }
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)
好吧,我想我已经find了一个方法来执行请求b)在我自己的问题,所以我会分享他人的利益。 ViewPager中的片段标签的forms为"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 } }
我不知道这是否是一种有效的方法,但是它会一直做下去,直到有更好的build议。
如果你问我,下面的第二个解决scheme,跟踪所有的“活动”片段页面,是更好的: http : //tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part -ii.html
从树皮的答案对我来说太过于粗鲁。
您跟踪所有“活动”片段页面。 在这种情况下,您将跟踪由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); }
“
好吧,经过@barkside上面的方法testing,我不能得到它与我的应用程序工作。 然后我记得IOSched2012应用程序也使用了一个viewpager
,那就是我find我的解决scheme的地方。 它不使用任何片段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"); } }
并将Fragments
实例存储在FragmentPagerAdapter
如下所示:
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(); }
您可以复制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(); } }
也没有问题的工作:
页面片段布局中的某处:
<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(); }
或者,你可以覆盖FragmentPagerAdapter
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; }
我想给我的方法,以防万一它可以帮助别人:
这是我的寻呼机适配器:
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);