Answers:
主要答案取决于框架生成的名称。如果那改变了,那么它将不再起作用。
这个怎么样的解决方案,覆盖instantiateItem()
和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())
这将不会返回null
。
我不知道此解决方案的任何其他缺点。如果有,我想知道。
MyPagerAdapter
由于生命周期(即旋转)而被破坏而不会registerdFragments
丢失时会发生什么?请问Activity
/ Fragment
使用MyPagerAdapter
必须将它保存onSaveInstanceState
,然后需要用新的更新它FragmentManager
裁判?
getItem
不会对旋转再次调用(对于已创建的任何碎片),因为FragmentManager
恢复了Fragments
它在寻呼机中所处的状态。如果instantiateItem
在每个Fragment
恢复时都被调用,那么该解决方案实际上比我的解决方案或公认的答案更安全,更可靠。我会考虑自己尝试一下。
要从ViewPager中获取片段,可以在此处以及其他相关的SO线程/博客中找到很多答案。我见过的每个人都很沮丧,他们通常似乎属于以下两种类型之一。还有一些其他的解决方案有效,如果你只想要抓住当前片段,像这样在此线程的其他答案。
如果使用,FragmentPagerAdapter
请参见下文。如果使用FragmentStatePagerAdapter
它的价值看这个。捕获不是FragmentStateAdapter中的当前索引的索引并没有它的本质那样有用,因为这些索引将被完全删除,从而超出了视图范围/超出了ScreenScreenLimit的范围。
错误:保持碎片自己的内部列表,添加时FragmentPagerAdapter.getItem()
被调用
SparseArray
或Map
getItem
仅在页面中第一次滚动浏览(或在您的ViewPager.setOffscreenPageLimit(x)
> 0时获得)那样ViewPager
,如果托管Activity
/ Fragment
被终止或重新启动,则在SpaseArray
重新创建自定义FragmentPagerActivity时内部将被擦除,但是在后台ViewPagers内部片段将被重新创建,并且getItem
将不被要求任何的指标,所以从指数得到片段的能力将永远消失。您可以通过保存并通过FragmentManager.getFragment()
和还原这些片段引用来解决putFragment
此问题, 但这会变得混乱IMHO。错误:构造与引擎盖下使用的标签ID匹配的标签ID,FragmentPagerAdapter
并使用此ID检索页面中的FragmentsFragmentManager
ViewPager
随时更改或针对任何OS版本进行更改。为此解决方案重新创建的方法是
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
ViewPager.instantiateItem()
与getItem()
上述方法类似,但不中断生命周期的方法是将其插入,instantiateItem()
而不是getItem()
因为每次创建/访问索引时都会调用前者。看到这个答案
FragmentViewPager
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;
}
这是一个简单的解决方案,解决了网络上到处可见的其他两个解决方案中的问题
instantiateItem()
您要访问在生命周期事件发生后尚未访问且在生命周期事件之前已访问过的片段。我知道一个很小的差异,但是它导致了我的程序中的一个细微错误,因此我对此进行了研究。
getItemId(pos)
内心FragmentStatePagerAdapter
将以下方法添加到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中实现的解决方案。如果发生故障,您将看到良好的崩溃日志。
另一个简单的解决方案:
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);
}
}
setPrimaryItem()
调用:-( ViewPager.OnPageChangeListener#onPageSelected()
我知道这有一些答案,但这也许会对某人有所帮助。当我需要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
每个对象时才会被a调用,此后,似乎使用进行了回收。这样一来,许多的实现创造新的以s 。使用我的上述方法,这意味着当我们遍历中的所有项目时,每次调用new 时都将创建new 。因此,我找到了一种更好的方法,使用来代替每个(使用公认的答案)。这是一个更完整的解决方案,对我来说一直很好。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;
}
ViewPager
本身设置那些标签。该解决方案非常脆弱,因为它依赖于了解的“隐藏”命名约定ViewPager
。波士顿街头的答案是一个更为全面的解决方案,我现在在我的项目中使用它(而不是这种方法)。
为了从ViewPager获取当前的Visible片段。我正在使用这个简单的语句,它工作正常。
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
我首先列出了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();
}
但这只是我的hack和slash方法,但对我有用,当按下后退按钮时,我确实使用它确实使更改更改为当前显示的片段。
这是基于上述史蒂文的答案。这将返回已附加到父活动的片段的实际实例。
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
}
}
}
}
我找不到简单,干净的方法来执行此操作。但是,ViewPager小部件只是另一个ViewGroup,它承载您的片段。ViewPager将这些片段作为直接子代。这样,您就可以遍历它们(使用.getChildCount()和.getChildAt()),然后查看您要查找的片段实例当前是否已加载到ViewPager中并获得对其的引用。例如,您可以使用一些静态唯一ID字段将片段分开。
请注意,ViewPager可能没有加载您要查找的片段,因为它是像ListView这样的虚拟化容器。
FragmentPagerAdapter是片段的工厂。要根据片段的位置(如果仍在内存中)查找片段,请使用以下命令:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
v4支持api的示例代码。
你并不需要呼叫getItem()
或在稍后阶段一些其他的方法来获取的基准Fragment
托管内ViewPager
。如果要更新内部的某些数据,请Fragment
使用以下方法:动态更新ViewPager?
关键是在内部设置新数据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);
}
getItemPosition()
是适配器中的一种方法。您不会直接调用它。您有适配器的引用,因此您可以调用adapter.notifyDataSetChanged()
,而后者将getItemPosition()
通过传递Fragment
s的引用来调用适配器。你可以看到在行动全面实施这里stackoverflow.com/questions/19891828/...
Fragment
但是如何获得参考却是有争议的。其他的解决方案,从获得参考FragmentManager
使用字符串相似 "android:switcher:"+id
或返回POSITION_NONE
从getItemosition()
导致所有片段意趣盎然。
必须扩展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;
嘿,我在这里回答了这个问题。基本上,您需要覆盖
public Object InstantiateItem(ViewGroup container,int position)
FragmentStatePagerAdapter的方法。
最好的解决方案是使用我们在CodePath创建的扩展名为SmartFragmentStatePagerAdapter。遵循该指南,这使得从ViewPager检索片段和当前选择的片段变得非常容易。它还可以更好地管理适配器中嵌入的片段的内存。
最简单,最简洁的方法。如果您所有的片段ViewPager
都属于不同的类,则可以按以下方式检索和区分它们:
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
在/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;
}
在片段管理器中迭代片段的简单方法。查找具有节位置参数的viewpager,将其放置在 公共静态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;
}
片段中
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();
}
}
在TabLayout中,有多个用于Fragment的选项卡。您可以使用片段的索引按Tag查找片段。
对于前。Fragment1的索引为0,因此在findFragmentByTag()
方法中,传递Viewpager的标签。使用fragmentTransaction之后,您可以添加,替换片段。
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
好的,FragmentStatePagerAdapter
我为适配器提供了一个解决方案:
在您的FragmentActivity中:
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中创建一个方法-这样,该方法就可以访问Fragment,您只需要为其指定所需片段的位置即可:
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;
}
}
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
是适配器中片段的位置,就像您fragment1
首先添加的那样,后退fragment1
通过index
为0,以此类推
Fragment
在WeakReference
保证你不阻止垃圾收集销毁片段?似乎似乎是正确的做法……