Ism*_*mic 96 android android-fragments fragmentpageradapter
我有问题使我的片段通过Activity
使用FragmentPagerAdapter
,作为帮助类来实现对标签的管理以及连接ViewPager
关联的所有细节TabHost
.我的实现FragmentPagerAdapter
与Android示例项目Support4Demos提供的一样.
主要问题是FragmentManager
当我没有Id或Tag时,如何从特定片段中获取特定片段?FragmentPagerAdapter
正在创建片段并自动生成Id和标签.
Ton*_*han 187
注意:在这个答案中,我将引用FragmentPagerAdapter
它的源代码.但一般的解决方案也应该适用FragmentStatePagerAdapter
.
如果您正在阅读本文,您可能已经知道FragmentPagerAdapter
/ FragmentStatePagerAdapter
意在Fragments
为您创建/ ViewPager
但是在Activity重新创建时(无论是从设备轮换还是从系统中删除您的App以重新获得内存)这些Fragments
都不会再次创建,而是他们的从中检索的实例FragmentManager
.现在说你Activity
需要获得这些参考来Fragments
对它们进行工作.您没有创建id
或tag
创建这些,Fragments
因为FragmentPagerAdapter
在内部设置它们.所以问题是如何在没有这些信息的情况下获得对它们的引用......
我在这个和类似问题上看到的很多解决方案都依赖于Fragment
通过调用FragmentManager.findFragmentByTag()
和模仿内部创建的标记"android:switcher:" + viewId + ":" + id
来获取对现有的引用:.这样做的问题在于你依赖于内部源代码,我们都知道并不能保证它们永远保持不变.Google的Android工程师可以轻松决定更改tag
会破坏您的代码的结构,从而无法找到对现有代码的引用Fragments
.
tag
这是一个简单的例子,说明如何获取对Fragments
返回的引用FragmentPagerAdapter
,而不依赖于内部tags
集Fragments
.关键是instantiateItem()
在那里覆盖和保存引用而不是在getItem()
.
public class SomeActivity extends Activity {
private FragmentA m1stFragment;
private FragmentB m2ndFragment;
// other code in your Activity...
private class CustomPagerAdapter extends FragmentPagerAdapter {
// other code in your custom FragmentPagerAdapter...
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// Do NOT try to save references to the Fragments in getItem(),
// because getItem() is not always called. If the Fragment
// was already created then it will be retrieved from the FragmentManger
// and not here (i.e. getItem() won't be called again).
switch (position) {
case 0:
return new FragmentA();
case 1:
return new FragmentB();
default:
// This should never happen. Always account for each position above
return null;
}
}
// Here we can finally safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger). Simply save the returned Fragment from
// super.instantiateItem() into an appropriate reference depending
// on the ViewPager position.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// save the appropriate reference depending on position
switch (position) {
case 0:
m1stFragment = (FragmentA) createdFragment;
break;
case 1:
m2ndFragment = (FragmentB) createdFragment;
break;
}
return createdFragment;
}
}
public void someMethod() {
// do work on the referenced Fragments, but first check if they
// even exist yet, otherwise you'll get an NPE.
if (m1stFragment != null) {
// m1stFragment.doWork();
}
if (m2ndFragment != null) {
// m2ndFragment.doSomeWorkToo();
}
}
}
Run Code Online (Sandbox Code Playgroud)
或者如果您更喜欢使用tags
而不是使用类成员变量/引用,Fragments
您也可以tags
通过FragmentPagerAdapter
相同的方式获取该集:注意:这不适用于FragmentStatePagerAdapter
因为它tags
在创建时没有设置Fragments
.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// get the tags set by FragmentPagerAdapter
switch (position) {
case 0:
String firstTag = createdFragment.getTag();
break;
case 1:
String secondTag = createdFragment.getTag();
break;
}
// ... save the tags somewhere so you can reference them later
return createdFragment;
}
Run Code Online (Sandbox Code Playgroud)
请注意,此方法不依赖于模仿内部tag
集合FragmentPagerAdapter
,而是使用适当的API来检索它们.这种方式即使你tag
未来版本的变化SupportLibrary
仍然是安全的.
不要忘记,根据您的设计Activity
,Fragments
您正在尝试的工作可能存在,也可能不存在,因此您必须null
在使用引用之前通过检查来解释这一点.
另外,如果您正在使用FragmentStatePagerAdapter
,那么您不希望保留对您的硬引用,Fragments
因为您可能有许多引用,并且硬引用会不必要地将它们保留在内存中.而是将Fragment
引用保存在WeakReference
变量而不是标准变量中.像这样:
WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
// reference hasn't been cleared yet; do work...
}
Run Code Online (Sandbox Code Playgroud)
Ism*_*mic 80
我根据以下帖子找到了我的问题的答案:在fragmentpageradapter中重用片段
我学到的东西很少:
getItem(int position)
在FragmentPagerAdapter
这个方法实际上做的是相当误导性的名称.它创建新片段,而不是返回现有片段.在这个意义上,该方法应该重命名为createItem(int position)
Android SDK中的方法.所以这种方法无法帮助我们获取片段.FragmentPagerAdapter
,并在这样意味着你有没有参考片段或它们的标签.如果你有片段标签,你可以FragmentManager
通过调用轻松检索对它的引用findFragmentByTag()
.我们需要一种方法来在给定的页面位置找出片段的标签.解
在类中添加以下辅助方法以检索片段标记并将其发送到findFragmentByTag()
方法.
private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}
Run Code Online (Sandbox Code Playgroud)
注意!这是FragmentPagerAdapter
在创建新片段时使用的相同方法.请参阅此链接http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104
per*_*000 12
我这样做的方法是定义WeakReferences的Hashtable,如下所示:
protected Hashtable<Integer, WeakReference<Fragment>> fragmentReferences;
Run Code Online (Sandbox Code Playgroud)
然后我写了这样的getItem()方法:
@Override
public Fragment getItem(int position) {
Fragment fragment;
switch(position) {
case 0:
fragment = new MyFirstFragmentClass();
break;
default:
fragment = new MyOtherFragmentClass();
break;
}
fragmentReferences.put(position, new WeakReference<Fragment>(fragment));
return fragment;
}
Run Code Online (Sandbox Code Playgroud)
然后你可以写一个方法:
public Fragment getFragment(int fragmentId) {
WeakReference<Fragment> ref = fragmentReferences.get(fragmentId);
return ref == null ? null : ref.get();
}
Run Code Online (Sandbox Code Playgroud)
这似乎运作良好,我发现它比它更少hacky
"android:switcher:" + viewId + ":" + position
Run Code Online (Sandbox Code Playgroud)
技巧,因为它不依赖于FragmentPagerAdapter的实现方式.当然,如果片段已由FragmentPagerAdapter释放,或者尚未创建,则getFragment将返回null.
如果有人发现这种方法有问题,那么评论非常受欢迎.
mor*_*wai 12
您不需要覆盖instantiateItem
也不需要依赖于使用内部makeFragmentName
方法创建片段标记的兼容性.instantiateItem
是一种公共方法,因此您可以(实际上应该)在onCreate
您的活动方法中调用它以获取对片段实例的引用,并在需要时将它们存储在本地变量中.请记住instantiateItem
用javadoc中描述的方法startUpdate
和finishUpdate
方法包围一组调用
:PagerAdapter
对PagerAdapter方法startUpdate(ViewGroup)的调用表明ViewPager的内容即将更改.将跟随对instantiateItem(ViewGroup,int)和/或destroyItem(ViewGroup,int,Object)的一个或多个调用,并且将通过调用finishUpdate(ViewGroup)来发出更新结束信号.
例如,这是在方法中存储对选项卡片段的引用的onCreate
方法:
public class MyActivity extends AppCompatActivity {
Fragment0 tab0; Fragment1 tab1;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);
adapter.startUpdate(viewPager);
tab0 = (Fragment0) adapter.instantiateItem(viewPager, 0);
tab1 = (Fragment1) adapter.instantiateItem(viewPager, 1);
adapter.finishUpdate(viewPager);
}
class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager manager) {super(manager);}
@Override public int getCount() {return 2;}
@Override public Fragment getItem(int position) {
if (position == 0) return new Fragment0();
if (position == 1) return new Fragment1();
return null; // or throw some exception
}
@Override public CharSequence getPageTitle(int position) {
if (position == 0) return getString(R.string.tab0);
if (position == 1) return getString(R.string.tab1);
return null; // or throw some exception
}
}
}
Run Code Online (Sandbox Code Playgroud)
instantiateItem
将首先尝试从中获取对现有片段实例的引用FragmentManager
.只有当它们不存在时,它才会使用getItem
适配器中的方法创建新的并将它们"存储"以FragmentManager
供将来使用.
一些额外的信息:
如果你没有在你的方法instantiateItem
中用startUpdate
/ 包围调用那么你就会冒险将你的片段实例永远不会被提交到:当你的活动变为前景时,会自动调用你的片段,但是/ 可能没有(取决于关于实现细节)他们基本上做的是开始/提交a .
这可能导致对创建的片段实例的引用非常快速地丢失(例如,当您旋转屏幕时)并且比必要时更频繁地重新创建.根据您的碎片"重"的程度,它可能会产生不可忽视的性能影响.
但更重要的是,在这种情况下,存储在本地变量上的片段实例将变得陈旧:因为android平台无法从中获取相同的实例,因此可能会创建和使用新的实例,而您的变量仍然会引用旧的变量.finishUpdate
onCreate
FragmentManager
instantiateItem
startUpdate
finishUpdate
FragmentTransaction
FragmentManager
Pep*_*ijn 10
我创建了这个方法,它可以让我获得对当前片段的引用.
public static Fragment getCurrentFragment(ViewPager pager, FragmentPagerAdapter adapter) {
try {
Method m = adapter.getClass().getSuperclass().getDeclaredMethod("makeFragmentName", int.class, long.class);
Field f = adapter.getClass().getSuperclass().getDeclaredField("mFragmentManager");
f.setAccessible(true);
FragmentManager fm = (FragmentManager) f.get(adapter);
m.setAccessible(true);
String tag = null;
tag = (String) m.invoke(null, pager.getId(), (long) pager.getCurrentItem());
return fm.findFragmentByTag(tag);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return null;
}
Run Code Online (Sandbox Code Playgroud)
小智 10
FragmentStateAdapter
有createFragment()
代替getItem()
. 并且没有这样的方法instantiateView()
。
因此,当配置更改后重新创建托管Activity
/时,不会被调用。这意味着适配器中的片段不会再次创建,而是从. 因此,如果您需要对片段进行一些工作,您可以简单地从 获取它们。Fragment
createFragment()
FragmentManager
FragmentManager
适配器创建:
CustomFragmentAdapter adapter = new CustomFragmentAdapter(getChildFragmentManager(), getLifecycle());
Run Code Online (Sandbox Code Playgroud)
FragmentManager
从after Activity
/ Fragment
reload中检索片段:
FragmentManager manager = getChildFragmentManager();
ArrayList<Fragment> fragments = new ArrayList<>(manager.getFragments());
for (Fragment fr : fragments) {
// do something
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
72899 次 |
最近记录: |