Android 使用FragmentPagerAdapter时如何获取现有片段
我无法通过Android 使用FragmentPagerAdapter时如何获取现有片段,android,android-fragments,fragmentpageradapter,Android,Android Fragments,Fragmentpageradapter,我无法通过活动使片段相互通信,该活动将FragmentPagerAdapter用作辅助类,该类实现选项卡的管理以及将ViewPager与关联的TabHost连接的所有详细信息。我已经实现了FragmentPagerAdapter,与Android示例项目Support4Demos提供的一样 主要问题是,当我既没有Id也没有标记时,如何从FragmentManager获取特定片段FragmentPagerAdapter正在创建片段并自动生成Id和标签。我根据以下帖子找到了问题的答案: 我学到的东西
活动
使片段相互通信,该活动将FragmentPagerAdapter
用作辅助类,该类实现选项卡的管理以及将ViewPager
与关联的TabHost
连接的所有详细信息。我已经实现了FragmentPagerAdapter
,与Android示例项目Support4Demos提供的一样
主要问题是,当我既没有Id也没有标记时,如何从
FragmentManager
获取特定片段FragmentPagerAdapter
正在创建片段并自动生成Id和标签。我根据以下帖子找到了问题的答案:
我学到的东西很少:
FragmentPagerAdapter
中的getItem(int-position)
对于该方法的实际作用是一个相当误导性的名称。它创建新片段,而不是返回现有片段。因此,在Android SDK中,应该将该方法重命名为类似于createItem(int-position)
。所以这个方法不能帮助我们得到碎片FragmentPagerAdapter
,这意味着您没有提及片段或其标签。如果您有片段标记,则可以通过调用findFragmentByTag()
从FragmentManager
轻松检索对它的引用。我们需要一种方法来找出给定页面位置的片段标签findframentbytag()
方法
private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}
注意!这与
FragmentPagerAdapter
在创建新片段时使用的方法相同。查看此链接请继续尝试此代码
public class MYFragmentPAdp extends FragmentPagerAdapter {
public MYFragmentPAdp(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return 2;
}
@Override
public Fragment getItem(int position) {
if (position == 0)
Fragment fragment = new Fragment1();
else (position == 1)
Fragment fragment = new Fragment2();
return fragment;
}
}
请参见从FragmentPagerAdapter返回碎片。确实依赖于您知道片段的索引-但是这将在getItem()中设置(仅在实例化时)我创建了这个方法,该方法用于获取对当前片段的引用
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;
}
我这样做的方式是定义WeakReference的哈希表,如下所示:
protected Hashtable<Integer, WeakReference<Fragment>> fragmentReferences;
技巧,因为它不依赖于FragmentPagerAdapter的实现方式。
当然,如果片段已由FragmentPagerAdapter释放,或者尚未创建,则getFragment将返回null
如果有人发现这种方法有问题,欢迎评论。由@personne3000建议的解决方案很好,但它有一个问题:当活动进入后台并被系统杀死(为了获得一些可用内存)然后恢复时,
碎片引用将为空,因为不会调用getItem
下面的类处理这种情况:
public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {
public static final String FRAGMENT_SAVE_PREFIX = "holder";
private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.
public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
super(fm);
fragmentManager = fm;
}
private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();
protected void holdFragment(F fragment) {
holdFragment(holder.size(), fragment);
}
protected void holdFragment(int position, F fragment) {
if (fragment != null)
holder.put(position, new WeakReference<F>(fragment));
}
public F getHoldedItem(int position) {
WeakReference<F> ref = holder.get(position);
return ref == null ? null : ref.get();
}
public int getHolderCount() {
return holder.size();
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
super.restoreState(state, loader);
Bundle bundle = (Bundle) state;
for (String key : bundle.keySet()) {
if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
Fragment f = fragmentManager.getFragment(bundle, key);
holdFragment(index, (F) f);
}
}
}
@Override
public Parcelable saveState() {
Bundle state = (Bundle) super.saveState();
if (state == null)
state = new Bundle();
for (int i = 0; i < holder.size(); i++) {
int id = holder.keyAt(i);
final F f = getHoldedItem(i);
String key = FRAGMENT_SAVE_PREFIX + id;
fragmentManager.putFragment(state, key, f);
}
return state;
}
}
公共抽象类AbstractHolderFragmentPagerAdapter扩展了FragmentPagerAdapter{
公共静态最终字符串片段\u SAVE\u PREFIX=“holder”;
private final FragmentManager FragmentManager;//我们需要自己存储片段管理器,因为父字段是私有的,没有getter。
公共摘要持有者FragmentPageRadapter(碎片管理器fm){
超级(fm);
碎片管理器=fm;
}
私人SparseArray持有人=新SparseArray();
受保护的空碎片(F碎片){
holdFragment(holder.size(),fragment);
}
受保护的void holdFragment(内部位置,F片段){
if(片段!=null)
保持器放置(位置,新WeakReference(碎片));
}
公共F getHoldedItem(内部位置){
WeakReference ref=保持架获取(位置);
return ref==null?null:ref.get();
}
public int getHolderCount(){
返回保持架。大小();
}
@凌驾
public void restoreState(Parcelable state,ClassLoader){//源于Google的FragmentStatePagerAdapter实现的代码
super.restoreState(州、装载机);
Bundle=(Bundle)状态;
for(字符串键:bundle.keySet()){
if(key.startsWith(片段\保存\前缀)){
int index=Integer.parseInt(key.substring(FRAGMENT\u SAVE\u PREFIX.length());
Fragment f=fragmentManager.getFragment(bundle,key);
保持片段(索引,(F)F);
}
}
}
@凌驾
公共包裹存储状态(){
Bundle state=(Bundle)super.saveState();
if(state==null)
state=newbundle();
对于(int i=0;i
问题摘要
注意:在这个答案中,我将参考FragmentPagerAdapter
及其源代码。但一般解决方案也应适用于片段StatePageRadapter
如果您正在阅读本文,您可能已经知道,FragmentPagerAdapter
/FragmentStatePagerAdapter
旨在为您的ViewPager
创建片段,但在活动重新创建时(无论是通过设备旋转还是通过系统关闭应用程序以恢复内存)这些片段
不会被再次创建,而是它们的片段。现在假设您的活动
需要获得对这些片段的引用
,才能对它们进行操作。对于这些创建的片段
您没有id
或标记
,因为FragmentPagerAdapter
。所以问题是如何在没有这些信息的情况下获得对它们的引用
当前解决方案的问题:依赖内部代码
我在这方面看到了很多解决方案
public Fragment getFragment(int fragmentId) {
WeakReference<Fragment> ref = fragmentReferences.get(fragmentId);
return ref == null ? null : ref.get();
}
"android:switcher:" + viewId + ":" + position
public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {
public static final String FRAGMENT_SAVE_PREFIX = "holder";
private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.
public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
super(fm);
fragmentManager = fm;
}
private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();
protected void holdFragment(F fragment) {
holdFragment(holder.size(), fragment);
}
protected void holdFragment(int position, F fragment) {
if (fragment != null)
holder.put(position, new WeakReference<F>(fragment));
}
public F getHoldedItem(int position) {
WeakReference<F> ref = holder.get(position);
return ref == null ? null : ref.get();
}
public int getHolderCount() {
return holder.size();
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
super.restoreState(state, loader);
Bundle bundle = (Bundle) state;
for (String key : bundle.keySet()) {
if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
Fragment f = fragmentManager.getFragment(bundle, key);
holdFragment(index, (F) f);
}
}
}
@Override
public Parcelable saveState() {
Bundle state = (Bundle) super.saveState();
if (state == null)
state = new Bundle();
for (int i = 0; i < holder.size(); i++) {
int id = holder.keyAt(i);
final F f = getHoldedItem(i);
String key = FRAGMENT_SAVE_PREFIX + id;
fragmentManager.putFragment(state, key, f);
}
return state;
}
}
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();
}
}
}
@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;
}
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...
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object value = super.instantiateItem(container, position);
if (position == 0) {
someFragment = (SomeFragment) value;
} else if (position == 1) {
anotherFragment = (AnotherFragment) value;
}
return value;
}
@Override
public void onAttach(Context context){
super.onAttach(context);
MainActivity.fragId = getId();
}
Fragment f = getSupportFragmentManager.findFragmentById(fragId);
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
}
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnListInteractionListener) activity;
mListener.setListFrag(this);
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void setListFrag(MyListFragment lf) {
if (mListFragment == null) {
mListFragment = lf;
}
}
if (savedInstanceState != null) {
if (mListFragment != null)
mListFragment.setListItems(items);
}
public interface Callbacks {
public void addFragment (Fragment fragment);
public void removeFragment (Fragment fragment);
}
public class HostingActivity extends AppCompatActivity implements ViewPagerFragment.Callbacks {
private LinkedHashSet<Fragment> mFragments = new LinkedHashSet<>();
@Override
public void addFragment (Fragment fragment) {
mFragments.add(fragment);
}
@Override
public void removeFragment (Fragment fragment) {
mFragments.remove(fragment);
}
}
public class ViewPagerFragment extends Fragment {
private Callbacks mCallbacks;
public interface Callbacks {
public void addFragment (Fragment fragment);
public void removeFragment (Fragment fragment);
}
@Override
public void onAttach (Context context) {
super.onAttach(context);
mCallbacks = (Callbacks) context;
// Add this fragment to the HashSet in the hosting activity
mCallbacks.addFragment(this);
}
@Override
public void onDetach() {
super.onDetach();
// Remove this fragment from the HashSet in the hosting activity
mCallbacks.removeFragment(this);
mCallbacks = null;
}
}
public class ViewPagerAdapter extends FragmentPagerAdapter {
private final Map<Integer, Reference<Fragment>> fragments = new HashMap<>();
private final List<Callable0<Fragment>> initializers = new ArrayList<>();
private final List<String> titles = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
void addFragment(Callable0<Fragment> initializer, String title) {
initializers.add(initializer);
titles.add(title);
}
public Optional<Fragment> getFragment(int position) {
return Optional.ofNullable(fragments.get(position).get());
}
@Override
public Fragment getItem(int position) {
Fragment fragment = initializers.get(position).execute();
return fragment;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
fragments.put(position, new WeakReference<>(fragment));
return fragment;
}
@Override
public int getCount() {
return initializers.size();
}
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
}