我正在使用ViewPager
和FragmentStatePagerAdapter
来托管三个不同的片段:
当我想从Fragment1
的{{1}}获取ViewPager
时。
问题是什么,我该如何解决?
答案 0 :(得分:495)
主要答案依赖于框架生成的名称。如果这种情况发生了变化,那么它将不再起作用。
此解决方案如何,覆盖instantiateItem()
的{{1}}和destroyItem()
:
Fragment(State)PagerAdapter
在处理可用的碎片时,这似乎对我有用。尚未实例化的片段在调用public class MyPagerAdapter extends FragmentStatePagerAdapter {
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return ...;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(...);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
时将返回null。但我一直在使用此功能主要是从getRegisteredFragment
Fragment
获取当前ViewPager
,这不会返回adapater.getRegisteredFragment(viewPager.getCurrentItem())
。
我不知道此解决方案的任何其他缺点。如果有的话,我想知道。
答案 1 :(得分:85)
为了从ViewPager中抓取片段,这里和其他相关的SO线程/博客上有很多答案。我见过的每个人都被打破了,他们通常似乎属于下面列出的两种类型之一。如果您只想获取当前片段,还有一些其他有效的解决方案,例如this此线程上的其他答案。
如果使用 FragmentPagerAdapter
,请参阅下文。如果使用 FragmentStatePagerAdapter
值得查看this。在FragmentStateAdapter中抓取不是当前索引的索引并不像它的性质那样有用,这些索引将完全拆除掉在offScreenLimit边界之外。
错误:维护您自己的内部片段列表,在调用FragmentPagerAdapter.getItem()
时添加
SparseArray
或Map
getItem
仅在ViewPager.setOffscreenPageLimit(x)
中滚动到(或获得ViewPager
&gt; 0时)页面时调用Activity
,如果托管Fragment
/ {{ 1}}被杀死或重新启动,然后在重新创建自定义FragmentPagerActivity时将清除内部SpaseArray
,但在幕后将重新创建ViewPagers内部片段,getItem
将 NOT < / strong>为任何索引调用,因此从索引中获取片段的能力将永远丢失。您可以通过FragmentManager.getFragment()
和putFragment
保存并恢复这些片段引用来解决这个问题,但这开始变得混乱恕我直言。 错误:根据FragmentPagerAdapter
中隐藏的内容构建您自己的代码ID,并使用此代码从FragmentManager
ViewPager
内部的私有方法,可随时更改或任何操作系统版本。为此解决方案重新创建的方法是
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
ViewPager.instantiateItem()
对getItem()
以上但非生命周期中断的类似方法是挂钩instantiateItem()
而不是getItem()
,因为前者将是每次创建/访问索引时调用。见this answer
FragmentViewPager
从source of the latest support lib构建您自己的FragmentViewPager
类,并更改内部使用的方法以生成片段标记。您可以使用以下内容替换它。这样做的好处是,您知道标签创建永远不会改变,而且您不依赖于私有API /方法,这总是很危险。
/**
* @param containerViewId the ViewPager this adapter is being supplied to
* @param id pass in getItemId(position) as this is whats used internally in this class
* @return the tag used for this pages fragment
*/
public static String makeFragmentName(int containerViewId, long id) {
return "android:switcher:" + containerViewId + ":" + id;
}
然后正如文档所说,当你想要获取一个用于索引的片段时,只需调用类似这个方法的东西(可以放入自定义FragmentPagerAdapter
或子类中)知道结果如果尚未为该页面调用getItem,则可能为null ,即尚未创建它。
/**
* @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
* yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
* the current positions fragment.
*/
public @Nullable Fragment getFragmentForPosition(int position)
{
String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
return fragment;
}
这是一个简单的解决方案,可以解决网络上随处可见的其他两种解决方案中的问题
答案 2 :(得分:70)
将以下方法添加到FragmentPagerAdapter:
public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return mFragmentManager.findFragmentByTag(name);
}
private static String makeFragmentName(int viewId, int index) {
return "android:switcher:" + viewId + ":" + index;
}
getActiveFragment(0)必须工作。
以下是ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d中实施的解决方案。如果出现故障,您将看到良好的崩溃日志。
答案 3 :(得分:38)
另一个简单的解决方案:
public class MyPagerAdapter extends FragmentPagerAdapter {
private Fragment mCurrentFragment;
public Fragment getCurrentFragment() {
return mCurrentFragment;
}
//...
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (getCurrentFragment() != object) {
mCurrentFragment = ((Fragment) object);
}
super.setPrimaryItem(container, position, object);
}
}
答案 4 :(得分:21)
我知道这有几个答案,但也许这会对某人有所帮助。当我需要从Fragment
获得ViewPager
时,我使用了一个相对简单的解决方案。在Activity
或Fragment
持有ViewPager
的情况下,您可以使用此代码循环显示它所持有的每个Fragment
。
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
if(viewPagerFragment != null) {
// Do something with your Fragment
// Check viewPagerFragment.isResumed() if you intend on interacting with any views.
}
}
如果您知道Fragment
中ViewPager
的位置,则可以致电getItem(knownPosition)
。
如果您不知道Fragment
中ViewPager
的位置,您可以让您的孩子Fragments
使用getUniqueId()
等方法实现界面},用它来区分它们。或者,您可以遍历所有Fragments
并检查班级类型,例如if(viewPagerFragment instanceof FragmentClassYouWant)
<强> !!!编辑!!!
我发现getItem
只有当FragmentPagerAdapter
需要创建第一次时才被Fragment
调用,之后,它会出现使用Fragments
回收FragmentManager
。这样,FragmentPagerAdapter
的许多实现都会在Fragment
中创建新 getItem
。使用我的上述方法,这意味着每次调用Fragment
时我们都会创建新的getItem
,因为我们会审核FragmentPagerAdapter
中的所有项目。因此,我找到了一种更好的方法,使用FragmentManager
代替每个Fragment
(使用已接受的答案)。这是一个更完整的解决方案,对我来说一直很好。
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
String name = makeFragmentName(mViewPager.getId(), i);
Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
// OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
if(viewPagerFragment != null) {
// Do something with your Fragment
if (viewPagerFragment.isResumed()) {
// Interact with any views/data that must be alive
}
else {
// Flag something for update later, when this viewPagerFragment
// returns to onResume
}
}
}
你需要这种方法。
private static String makeFragmentName(int viewId, int position) {
return "android:switcher:" + viewId + ":" + position;
}
答案 5 :(得分:10)
就我而言,上述解决方案均无效。
但是,由于我在片段中使用子片段管理器,因此使用了以下内容:
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
仅当Manager中的片段对应于viewpager项目时才会起作用。
答案 6 :(得分:6)
从ViewPager获取当前可见片段。我正在使用这个简单的陈述,它工作正常。
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
答案 7 :(得分:3)
这是基于Steven的上述答案。这将返回已附加到父活动的片段的实际实例。
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
if(viewPagerFragment != null && viewPagerFragment.isAdded()) {
if (viewPagerFragment instanceof FragmentOne){
FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
if (oneFragment != null){
oneFragment.update(); // your custom method
}
} else if (viewPagerFragment instanceof FragmentTwo){
FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;
if (twoFragment != null){
twoFragment.update(); // your custom method
}
}
}
}
答案 8 :(得分:2)
答案 9 :(得分:2)
我找不到一个简单,干净的方法来做到这一点。但是,ViewPager小部件只是另一个ViewGroup,它托管您的片段。 ViewPager将这些片段作为直接子节点。所以你可以迭代它们(使用.getChildCount()和.getChildAt()),看看你正在寻找的片段实例当前是否已加载到ViewPager并获得对它的引用。例如。您可以使用一些静态唯一ID字段来区分碎片。
请注意,ViewPager可能尚未加载您正在寻找的片段,因为它是像ListView这样的虚拟化容器。
答案 10 :(得分:2)
必须将FragmentPagerAdapter
扩展到ViewPager适配器类中
如果您使用FragmentStatePagerAdapter
,那么您将无法通过Fragment
ID
public static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
如何使用此方法: -
Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
答案 11 :(得分:2)
您不需要在稍后阶段调用getItem()
或其他方法,以获取Fragment
在ViewPager
内托管的引用。如果您想更新Fragment
中的某些数据,请使用以下方法:Update ViewPager dynamically?
关键是在Adaper
内设置新数据并致电notifyDataSetChanged()
,然后拨打getItemPosition()
,并为您提供Fragment
的参考,并为您提供机会更新它。所有其他方式都要求您继续引用自己或其他一些不太好的解决方案。
@Override
public int getItemPosition(Object object) {
if (object instanceof UpdateableFragment) {
((UpdateableFragment) object).update(xyzData);
}
//don't return POSITION_NONE, avoid fragment recreation.
return super.getItemPosition(object);
}
答案 12 :(得分:2)
我首先制作了一份我将要使用的所有片段(List<Fragment> fragments;
)的列表,然后将它们添加到寻呼机,以便更容易处理当前查看的片段。
所以:
@Override
onCreate(){
//initialise the list of fragments
fragments = new Vector<Fragment>();
//fill up the list with out fragments
fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));
//Set up the pager
pager = (ViewPager)findViewById(R.id.pager);
pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
pager.setOffscreenPageLimit(4);
}
所以这可以叫做:
public Fragment getFragment(ViewPager pager){
Fragment theFragment = fragments.get(pager.getCurrentItem());
return theFragment;
}
所以我可以把它放在if语句中,只有在正确的片段
上才会运行Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
//then you can do whatever with the fragment
theFrag.costomFunction();
}
但这只是我的黑客和斜线方法,但它对我有用,我使用它会在按下后退按钮时对我当前显示的片段进行相关更改。
答案 13 :(得分:2)
FragmentPagerAdapter是片段的工厂。要在仍然在内存中时根据其位置查找片段,请使用:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
v4支持api的示例代码。
答案 14 :(得分:1)
最佳解决方案是使用我们在名为CodePath的SmartFragmentStatePagerAdapter处创建的扩展程序。在该指南之后,这使得从ViewPager检索片段和当前选择的片段变得更加容易。它还可以更好地管理嵌入在适配器中的片段的内存。
答案 15 :(得分:0)
最简单,最简洁的方式。如果ViewPager
中的所有片段属于不同的类别,您可以检索并区分它们如下:
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
答案 16 :(得分:0)
我用一种不同的方法实现了这一点。
我的自定义FragmentAdapter.getItem方法返回的不是新的MyFragment(),而是在FragmentAdapter构造函数中创建的MyFragment实例。
在我的活动中,我从适配器获取片段,检查它是否是instanceOf需要片段,然后转换并使用所需的方法。
答案 17 :(得分:0)
在/values/integers.xml
中创建整数资源ID<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>
然后在PagerAdapter getItem函数中:
public Fragment getItem(int position) {
Fragment fragment = null;
if (position == 0) {
fragment = FragmentOne.newInstance();
mViewPager.setTag(R.integer.page1,fragment);
}
else if (position == 1) {
fragment = FragmentTwo.newInstance();
mViewPager.setTag(R.integer.page2,fragment);
} else if (position == 2) {
fragment = FragmentThree.newInstance();
mViewPager.setTag(R.integer.page3,fragment);
}
return fragment;
}
然后在活动中编写此函数以获取片段引用:
private Fragment getFragmentByPosition(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = (Fragment) mViewPager.getTag(R.integer.page1);
break;
case 1:
fragment = (Fragment) mViewPager.getTag(R.integer.page2);
break;
case 2:
fragment = (Fragment) mViewPager.getTag(R.integer.page3);
break;
}
return fragment;
}
通过调用上面的函数获取片段引用,然后将其转换为自定义片段:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
答案 18 :(得分:0)
在片段管理器中迭代片段的简便方法。查找具有节位置参数的viewpager,放置在 public static PlaceholderFragment newInstance(int sectionNumber)。
public PlaceholderFragment getFragmentByPosition(Integer pos){
for(Fragment f:getChildFragmentManager().getFragments()){
if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
return (PlaceholderFragment) f;
}
}
return null;
}
答案 19 :(得分:0)
在片段中
public int getArgument(){
return mPage;
{
public void update(){
}
在FragmentActivity中
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
if((f instanceof PageFragment)&&(!f.isDetached())){
PageFragment pf = (PageFragment)f;
if(pf.getArgument()==pager.getCurrentItem())pf.update();
}
}
答案 20 :(得分:0)
有片段的多个标签。你可以使用片段的索引通过Tag找到片段。
对于前。 Fragment1的索引是0,所以在findFragmentByTag()
方法中,传递Viewpager的标签。使用fragmentTransaction你可以添加,替换片段。
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
答案 21 :(得分:-1)
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
是适配器中片段的位置,就像您先添加fragment1
一样,以便fragment1
将index
传递为0,依此类推,以便休息
答案 22 :(得分:-1)
适用于适配器FragmentStatePagerAdapter
我为解决方案提供资金:
:
ActionBar mActionBar = getSupportActionBar();
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));
viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);
并在你的类FragmentActivity中创建一个方法 - 所以这个方法可以让你访问你的片段,你只需要给它你想要的片段的位置:
public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}
适配器中的:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private final ActionBar actionBar;
private final FragmentManager fragmentManager;
public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}
@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}
@Override
public int getCount() {
return this.actionBar.getTabCount();
}
@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}
private static String makeFragmentName(int viewId, int index) {
return "android:fragment:" + index;
}
}