ViewPager和片段—存储片段状态的正确方法是什么?


492

片段对于将UI逻辑分为一些模块来说似乎非常好。但是随着ViewPager它的生命周期,我仍然迷茫。因此,非常需要大师的想法!

编辑

请参阅下面的愚蠢解决方案;-)

范围

主要活动有一个ViewPager带有片段。这些片段可以为其他(子主)活动实现一些不同的逻辑,因此片段的数据通过活动内部的回调接口填充。并且所有功能在首次启动时都正常,但是!

问题

重新创建活动时(例如,在方向更改时),ViewPager的片段也是如此。代码(您将在下面找到)说,每次创建活动时,我都会尝试创建一个ViewPager与片段相同的新片段适配器(也许是问题所在),但是FragmentManager已经将所有这些片段存储在某个地方(哪里?),并且为这些人启动娱乐机制。因此,重新创建机制通过我的回调接口调用“旧的”片段的onAttach,onCreateView等,以通过Activity的实现方法来初始化数据。但是此方法指向通过Activity的onCreate方法创建的新创建的片段。

问题

也许我使用的是错误的模式,但即使是Android 3 Pro书籍也没有太多相关信息。因此,给我一两次拳,并指出正确的方法。非常感谢!

主要活动

public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {

private MessagesFragment mMessagesFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    setContentView(R.layout.viewpager_container);
    new DefaultToolbar(this);

    // create fragments to use
    mMessagesFragment = new MessagesFragment();
    mStreamsFragment = new StreamsFragment();

    // set titles and fragments for view pager
    Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
    screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
    screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);

    // instantiate view pager via adapter
    mPager = (ViewPager) findViewById(R.id.viewpager_pager);
    mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);

    // set title indicator
    TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
    indicator.setViewPager(mPager, 1);

}

/* set of fragments callback interface implementations */

@Override
public void onMessageInitialisation() {

    Logger.d("Dash onMessageInitialisation");
    if (mMessagesFragment != null)
        mMessagesFragment.loadLastMessages();
}

@Override
public void onMessageSelected(Message selectedMessage) {

    Intent intent = new Intent(this, StreamActivity.class);
    intent.putExtra(Message.class.getName(), selectedMessage);
    startActivity(intent);
}

BasePagerActivity aka助手

public class BasePagerActivity extends FragmentActivity {

BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}

适配器

public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {

private Map<String, Fragment> mScreens;

public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {

    super(fm);
    this.mScreens = screenMap;
}

@Override
public Fragment getItem(int position) {

    return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}

@Override
public int getCount() {

    return mScreens.size();
}

@Override
public String getTitle(int position) {

    return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}

// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {

    // TODO Auto-generated method stub
}

}

分段

public class MessagesFragment extends ListFragment {

private boolean mIsLastMessages;

private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;

private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;

// define callback interface
public interface OnMessageListActionListener {
    public void onMessageInitialisation();
    public void onMessageSelected(Message selectedMessage);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // setting callback
    mListener = (OnMessageListActionListener) activity;
    mIsLastMessages = activity instanceof DashboardActivity;

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    inflater.inflate(R.layout.fragment_listview, container);
    mProgressView = inflater.inflate(R.layout.listrow_progress, null);
    mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // instantiate loading task
    mLoadMessagesTask = new LoadMessagesTask();

    // instantiate list of messages
    mMessagesList = new ArrayList<Message>();
    mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
    setListAdapter(mAdapter);
}

@Override
public void onResume() {
    mListener.onMessageInitialisation();
    super.onResume();
}

public void onListItemClick(ListView l, View v, int position, long id) {
    Message selectedMessage = (Message) getListAdapter().getItem(position);
    mListener.onMessageSelected(selectedMessage);
    super.onListItemClick(l, v, position, id);
}

/* public methods to load messages from host acitivity, etc... */
}

愚蠢的解决方案是使用putFragment将片段保存在(主机活动的)onSaveInstanceState内部,并通过getFragment将其保存在onCreate中。但是我仍然有一种奇怪的感觉,就是事情不应该那样工作……请参见下面的代码:

    @Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    getSupportFragmentManager()
            .putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}

protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    ...
    // create fragments to use
    if (savedInstanceState != null) {
        mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
                savedInstanceState, MessagesFragment.class.getName());
                StreamsFragment.class.getName());
    }
    if (mMessagesFragment == null)
        mMessagesFragment = new MessagesFragment();
    ...
}

1
我现在想知道:我应该使用一种非常不同的方法还是尝试通过onSavedInstancestate保存主要活动(仪表板)的片段以在onCreate()中使用它们。是否有保存这些片段并从onCreate中的捆绑包中获取片段的正确方法?它们似乎无法包裹……
Oleksii Malovanyi 2011年

2
第二种方法有效-参见“解决方案”。但这似乎是一段难看的代码,不是吗?
Oleksii Malovanyi,2011年

1
为了清理Android标签(在此处进行详细说明:meta.stackexchange.com/questions/100529/…),您是否介意将您的解决方案发布为答案并将其标记为选定的解决方案?这样,它就不会显示为未解决的问题:)
亚历山大·卢卡斯

1
是的,认为还可以。希望比我更好的……
Oleksii Malovanyi 2011年

1
愚蠢的解决方案是否有效?它给了我一个空指针异常..
刘珍

Answers:


451

FragmentPagerAdapter增加一个片段到FragmentManager,它使用基于特定位置,该片段将被置于一个特殊的标记。FragmentPagerAdapter.getItem(int position)仅在该位置的片段不存在时才调用。旋转后,Android将注意到它已经为该特定位置创建/保存了一个片段,因此它只是尝试使用进行重新连接FragmentManager.findFragmentByTag(),而不是创建一个新片段。使用时,所有这些都是免费的,这FragmentPagerAdapter就是为什么通常在方法中包含片段初始化代码的原因getItem(int)

即使我们没有使用a FragmentPagerAdapter,也不是每次在中创建一个新片段都是一个好主意Activity.onCreate(Bundle)。如您所知,将片段添加到FragmentManager时,将在旋转后为您重新创建它,而无需再次添加。这样做是处理片段时出错的常见原因。

使用片段的常用方法是:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

当使用时FragmentPagerAdapter,我们将片段管理交给适配器,而不必执行上述步骤。默认情况下,它只会在当前位置的前后预先加载一个Fragment(尽管除非使用,否则它不会销毁它们FragmentStatePagerAdapter)。这由ViewPager.setOffscreenPageLimit(int)控制。因此,不能保证在适配器外部的片段上直接调用方法是有效的,因为它们甚至可能没有生命。

长话短说,您使用的解决方案putFragment可以随后获得参考,这并不是那么疯狂,并且与通常使用片段的常规方法没什么不同(上图)。否则很难获得参考,因为该片段是由适配器而不是您本人添加的。只需确保它offscreenPageLimit足够高就可以始终加载所需的片段,因为您依赖于它的存在。这绕过了ViewPager的延迟加载功能,但似乎正是您对应用程序所期望的。

另一种方法是FragmentPageAdapter.instantiateItem(View, int)在返回之前覆盖并保存对从超级调用返回的片段的引用(它具有查找该片段(如果已经存在)的逻辑)。

要获得更完整的图片,请查看FragmentPagerAdapter(短)和ViewPager(长)的某些来源。


7
喜欢最后一部分。为片段缓存了一个缓存,并将放置在缓存逻辑中的内容移到了FragmentPageAdapter.instantiateItem(View, int)。最终修复了一个长期存在的错误,该错误仅出现在旋转/配置更改中,并令我发疯……
Carlos Sobrinho 2012年

2
这解决了我在轮换上遇到的一个问题。也许我只是看不见,但这有文件记录吗?即使用标签从以前的状态中恢复?这也许是一个很明显的答案,但是我对Android开发还是很陌生。

1
@ Industrial-antidepressant这是要向其中添加Fragment的容器(例如FrameLayout)的ID。
antonyt

1
顺便说一句,对我来说FragmentPageAdapter.instantiateItem(ViewGroup, int)不是FragmentPageAdapter.instantiateItem(View, int)
显示名称

1
顺便说一句,当片段从屏幕上划掉时,您知道调用哪个(如果有)片段生命周期方法吗?是onDetach()还是其他?
ygesher 2014年

36

我想提供一个解决方案,该解决方案扩展了antonyt出色答案,并提到了覆盖FragmentPageAdapter.instantiateItem(View, int)以保存对创建的引用的覆盖,Fragments以便您以后可以对它们进行处理。这也应该与FragmentStatePagerAdapter;有关详细信息,请参见注释。


这是一个简单的示例,该示例说明如何获取对的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();
        }
    }
}

或者,如果您希望使用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;
}

请注意,此方法不依赖于模仿内部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...
}

关于内存不足,Android可能会要求FragmentManager销毁未使用的片段,对吗?如果我是对的,则您的第二个版本可以使用,第一个版本可能会失败。(不知道当前的android版本是否可以做到这一点,但似乎我们必须期待这一点。)
Moritz都

1
如何FragmetPagerAdapter在每次屏幕旋转时在inCreate中创建Activity?这是错误的,因为它可能会绕过在重用已经添加片段FragmentPagerAdapter
巴曼

覆盖instantiateItem()是必经之路;这有助于我处理屏幕旋转,并在恢复活动和适配器后检索我现有的Fragment实例;我在代码中留下了自己的注释,以提醒您:旋转后,getItem()不会被调用;仅此方法instantiateItem()被调用。超级实现instantiateItem()实际上是在旋转后重新附加片段(根据需要),而不是实例化新实例!
HelloImKevo

Fragment createdFragment = (Fragment) super.instantiateItem..在第一个解决方案中启用了空指针 。
AlexS

在搜寻了该问题的所有重复/变量迭代之后,这是最好的解决方案。(交叉引用来自另一个带有更相关标题的Q,所以谢谢您!)
MandisaW

18

我为您的问题找到了另一个相对简单的解决方案。

FragmentPagerAdapter源代码中可以看到,由所管理的片段FragmentPagerAdapter存储在使用FragmentManager以下命令生成的标记下:

String tag="android:switcher:" + viewId + ":" + index;

viewIdcontainer.getId(),该container是你的ViewPager实例。的index是该片段的位置。因此,您可以将对象ID保存到outState

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("viewpagerid" , mViewPager.getId() );
}

@Override
    protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null)
        viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );  

    MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);        
    mViewPager = (ViewPager) findViewById(R.id.pager);
    if (viewpagerid != -1 ){
        mViewPager.setId(viewpagerid);
    }else{
        viewpagerid=mViewPager.getId();
    }
    mViewPager.setAdapter(titleAdapter);

如果要与此片段进行通信,则可以从获取if FragmentManager,例如:

getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewpagerid + ":0")

21
嗯,我不认为这是一个好方法,因为您对内部标签命名约定的答复绝不会永远保持不变。
Dori

1
对于那些寻求不依赖内部解决方案的人tag,请考虑尝试我的答案
东尼·陈

16

我想为可能略有不同的情况提供一个替代解决方案,因为我对答案的许多搜索一直将我引向这个话题。

我的情况 -我正在动态创建/添加页面并将它们滑动到ViewPager中,但是当旋转(onConfigurationChange)时,我会得到一个新页面,因为当然会再次调用OnCreate。但我想保留对轮换之前创建的所有页面的引用。

问题 -我为创建的每个片段没有唯一的标识符,因此引用的唯一方法是在旋转/配置更改后以某种方式将引用存储在要还原的数组中。

解决方法 -关键概念是让Activity(显示Fragments)也管理对现有Fragments的引用数组,因为此活动可以利用onSaveInstanceState中的Bundles

public class MainActivity extends FragmentActivity

因此,在此活动中,我声明一个私有成员来跟踪打开的页面

private List<Fragment> retainedPages = new ArrayList<Fragment>();

每次调用onSaveInstanceState并在onCreate中恢复时都会更新

@Override
protected void onSaveInstanceState(Bundle outState) {
    retainedPages = _adapter.exportList();
    outState.putSerializable("retainedPages", (Serializable) retainedPages);
    super.onSaveInstanceState(outState);
}

...因此一旦存储起来,就可以检索...

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState != null) {
        retainedPages = (List<Fragment>) savedInstanceState.getSerializable("retainedPages");
    }
    _mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
    _adapter = new ViewPagerAdapter(getApplicationContext(), getSupportFragmentManager());
    if (retainedPages.size() > 0) {
        _adapter.importList(retainedPages);
    }
    _mViewPager.setAdapter(_adapter);
    _mViewPager.setCurrentItem(_adapter.getCount()-1);
}

这些是对主活动的必要更改,因此,我需要FragmentPagerAdapter中的成员和方法才能使其正常工作,因此在

public class ViewPagerAdapter extends FragmentPagerAdapter

相同的构造(如上面在MainActivity中所示)

private List<Fragment> _pages = new ArrayList<Fragment>();

方法特别支持这种同步(如上面onSaveInstanceState中所使用的)

public List<Fragment> exportList() {
    return _pages;
}

public void importList(List<Fragment> savedPages) {
    _pages = savedPages;
}

最后,在片段类中

public class CustomFragment extends Fragment

为了使所有这些工作正常进行,首先进行了两项更改

public class CustomFragment extends Fragment implements Serializable

然后将其添加到onCreate中,这样就不会破坏片段

setRetainInstance(true);

我仍在努力应对Fragments和Android生命周期,因此请注意,此方法可能存在冗余/效率低下的问题。但这对我有用,希望对其他与我类似的案件有所帮助。


2
为setRetainInstance(true)+1 –我一直在寻找的黑魔法!
歪曲2014年

使用FragmentStatePagerAdapter(v13)甚至更加容易。哪个为您处理状态还原和释放。
屠夫

清晰的说明和非常好的解决方案。谢谢!
布鲁诺·比耶里

8

我的解决方案很粗鲁,但可以起作用:作为我的片段,它是从保留的数据中动态创建的,我只需PageAdapter在调用之前从中删除所有片段super.onSaveInstanceState(),然后在创建活动时重新创建它们:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("viewpagerpos", mViewPager.getCurrentItem() );
    mSectionsPagerAdapter.removeAllfragments();
    super.onSaveInstanceState(outState);
}

您无法将它们删除 onDestroy(),否则会遇到以下异常:

java.lang.IllegalStateException: 之后无法执行此操作 onSaveInstanceState

这里是页面适配器中的代码:

public void removeAllfragments()
{
    if ( mFragmentList != null ) {
        for ( Fragment fragment : mFragmentList ) {
            mFm.beginTransaction().remove(fragment).commit();
        }
        mFragmentList.clear();
        notifyDataSetChanged();
    }
}

onCreate()创建片段后,我仅保存当前页面并将其还原到中。

if (savedInstanceState != null)
    mViewPager.setCurrentItem( savedInstanceState.getInt("viewpagerpos", 0 ) );  

我不知道为什么,但是notifyDataSetChanged(); 导致应用崩溃
Malachiasz 2013年

4

那是BasePagerAdapter什么 您应该使用标准的寻呼机适配器之一- FragmentPagerAdapterFragmentStatePagerAdapter,取决于您是否希望ViewPager保留不再需要的Fragment (保留前者)或保存其状态(后者)并在以下情况下重新创建它们:再次需要。

使用示例代码ViewPager可以在这里找到

的确,跨活动实例的视图分页器中片段的管理有些复杂,因为FragmentManager框架中的负责保存状态并恢复分页器创建的任何活动片段。这实际上意味着适配器在初始化时需要确保它与已还原的片段重新连接。您可以查看FragmentPagerAdapterFragmentStatePagerAdapter查看其代码。


1
我的问题中有BasePagerAdapter代码。可以看到,它只是出于实现TitleProvider的目的而扩展了FragmentPagerAdapter 。因此,一切都已经使用android dev建议的方法进行了。
Oleksii Malovanyi,2011年

我不知道“ FragmentStatePagerAdapter”。你真的救了我几个小时。谢谢。
克诺索斯

2

如果有人因其FragmentStatePagerAdapter出现问题而无法正确还原其碎片的状态...即...新的碎片由FragmentStatePagerAdapter创建,而不是从状态中还原它们...

确保致电 ViewPager.setOffscreenPageLimit()之前致电ViewPager.setAdapter(fragmentStatePagerAdapter)

调用后ViewPager.setOffscreenPageLimit(),ViewPager将立即查找其适配器并尝试获取其片段。这可能在ViewPager有机会从saveInstanceState还原片段之前发生(因此,创建了无法从SavedInstanceState重新初始化的新片段,因为它们是新的)。


0

我想出了这个简单而优雅的解决方案。它假定活动负责创建片段,而适配器仅为它们提供服务。

这是适配器的代码(这里没什么奇怪的,除了mFragments是由Activity维护的片段列表的事实)

class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        TabFragment fragment = (TabFragment)mFragments.get(position);
        return fragment.getTitle();
    }
} 

该线程的整个问题是获取“旧”片段的引用,因此我在Activity的onCreate中使用此代码。

    if (savedInstanceState!=null) {
        if (getSupportFragmentManager().getFragments()!=null) {
            for (Fragment fragment : getSupportFragmentManager().getFragments()) {
                mFragments.add(fragment);
            }
        }
    }

当然,如果需要,您可以进一步微调此代码,例如,确保片段是特定类的实例。


0

要在方向更改后获取片段,您必须使用.getTag()。

    getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewPagerId + ":" + positionOfItemInViewPager)

为了进一步处理,我为自己的PageAdapter编写了自己的ArrayList,以在任何位置通过viewPagerId和FragmentClass获取片段:

public class MyPageAdapter extends FragmentPagerAdapter implements Serializable {
private final String logTAG = MyPageAdapter.class.getName() + ".";

private ArrayList<MyPageBuilder> fragmentPages;

public MyPageAdapter(FragmentManager fm, ArrayList<MyPageBuilder> fragments) {
    super(fm);
    fragmentPages = fragments;
}

@Override
public Fragment getItem(int position) {
    return this.fragmentPages.get(position).getFragment();
}

@Override
public CharSequence getPageTitle(int position) {
    return this.fragmentPages.get(position).getPageTitle();
}

@Override
public int getCount() {
    return this.fragmentPages.size();
}


public int getItemPosition(Object object) {
    //benötigt, damit bei notifyDataSetChanged alle Fragemnts refrehsed werden

    Log.d(logTAG, object.getClass().getName());
    return POSITION_NONE;
}

public Fragment getFragment(int position) {
    return getItem(position);
}

public String getTag(int position, int viewPagerId) {
    //getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.shares_detail_activity_viewpager + ":" + myViewPager.getCurrentItem())

    return "android:switcher:" + viewPagerId + ":" + position;
}

public MyPageBuilder getPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
    return new MyPageBuilder(pageTitle, icon, selectedIcon, frag);
}


public static class MyPageBuilder {

    private Fragment fragment;

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    private String pageTitle;

    public String getPageTitle() {
        return pageTitle;
    }

    public void setPageTitle(String pageTitle) {
        this.pageTitle = pageTitle;
    }

    private int icon;

    public int getIconUnselected() {
        return icon;
    }

    public void setIconUnselected(int iconUnselected) {
        this.icon = iconUnselected;
    }

    private int iconSelected;

    public int getIconSelected() {
        return iconSelected;
    }

    public void setIconSelected(int iconSelected) {
        this.iconSelected = iconSelected;
    }

    public MyPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
        this.pageTitle = pageTitle;
        this.icon = icon;
        this.iconSelected = selectedIcon;
        this.fragment = frag;
    }
}

public static class MyPageArrayList extends ArrayList<MyPageBuilder> {
    private final String logTAG = MyPageArrayList.class.getName() + ".";

    public MyPageBuilder get(Class cls) {
        // Fragment über FragmentClass holen
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return super.get(indexOf(item));
            }
        }
        return null;
    }

    public String getTag(int viewPagerId, Class cls) {
        // Tag des Fragment unabhängig vom State z.B. nach bei Orientation change
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return "android:switcher:" + viewPagerId + ":" + indexOf(item);
            }
        }
        return null;
    }
}

因此,只需创建带有片段的MyPageArrayList即可:

    myFragPages = new MyPageAdapter.MyPageArrayList();

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_data_frag),
            R.drawable.ic_sd_storage_24dp,
            R.drawable.ic_sd_storage_selected_24dp,
            new WidgetDataFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_color_frag),
            R.drawable.ic_color_24dp,
            R.drawable.ic_color_selected_24dp,
            new WidgetColorFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_textsize_frag),
            R.drawable.ic_settings_widget_24dp,
            R.drawable.ic_settings_selected_24dp,
            new WidgetTextSizeFrag()));

并将它们添加到viewPager中:

    mAdapter = new MyPageAdapter(getSupportFragmentManager(), myFragPages);
    myViewPager.setAdapter(mAdapter);

在此之后,您可以在定向之后通过使用其类来更改正确的片段:

        WidgetDataFrag dataFragment = (WidgetDataFrag) getSupportFragmentManager()
            .findFragmentByTag(myFragPages.getTag(myViewPager.getId(), WidgetDataFrag.class));

-30

加:

   @SuppressLint("ValidFragment")

在上课之前。

这样做是行不通的:

@SuppressLint({ "ValidFragment", "HandlerLeak" })
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.