Подтвердить что ты не робот

ViewPager и фрагменты - какой правильный способ хранения состояния фрагмента?

Фрагменты кажутся очень приятными для разделения логики пользовательского интерфейса на некоторые модули. Но вместе с 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 helper

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... */
}

Решение

Немым решением является сохранение фрагментов внутри onSaveInstanceState (активности хоста) с помощью putFragment и получение их внутри onCreate через getFragment. Но у меня все еще странное чувство, что все должно работать не так... Смотрите код ниже:

    @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();
    ...
}
4b9b3361

Ответ 1

Когда FragmentPagerAdapter добавляет фрагмент в FragmentManager, он использует специальный тег, основанный на конкретной позиции, в которой будет размещен фрагмент. FragmentPagerAdapter.getItem(int position) вызывается только тогда, когда фрагмент для этой позиции не существует. После ротации Android заметит, что он уже создал/сохранил фрагмент для этой конкретной позиции и поэтому пытается просто подключиться к нему с помощью FragmentManager.findFragmentByTag() вместо создания нового. Все это освобождается при использовании FragmentPagerAdapter и поэтому обычно имеет код инициализации фрагмента внутри метода getItem(int).

Даже если мы не использовали 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 мы отказываемся от управления фрагментами с адаптером и не должны выполнять вышеуказанные шаги. По умолчанию он будет предварительно загружать один фрагмент спереди и сзади текущей позиции (хотя он не уничтожит их, если вы не используете FragmentStatePagerAdapter). Это контролируется ViewPager.setOffscreenPageLimit(int). Из-за этого прямое указание методов на фрагменты вне адаптера не гарантируется, поскольку они могут даже не быть живыми.

Чтобы сократить длинную историю, ваше решение использовать putFragment, чтобы иметь возможность получить ссылку впоследствии, не настолько сумасшедшее и не похоже на обычный способ использования фрагментов в любом случае (см. выше). Трудно получить ссылку иначе, потому что фрагмент добавляется адаптером, а не вы лично. Просто убедитесь, что offscreenPageLimit достаточно высок, чтобы всегда загружать нужные фрагменты, поскольку вы полагаетесь на его присутствие. Это обходит ленивые возможности загрузки ViewPager, но, похоже, это то, что вы хотите для своего приложения.

Другой подход заключается в переопределении FragmentPageAdapter.instantiateItem(View, int) и сохранении ссылки на фрагмент, возвращенный из супервызова, перед его возвратом (у него есть логика для поиска фрагмента, если он уже присутствует).

Для более полной картины взгляните на источник FragmentPagerAdapter (короткий) и ViewPager (длинный).

Ответ 2

Я хочу предложить решение, которое расширяется на 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, так как при создании Fragments не устанавливается tags.

@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...
}

Ответ 3

Я нашел другое относительно простое решение для вашего вопроса.

Как вы можете видеть из FragmentPagerAdapter исходного кода, фрагменты, управляемые FragmentPagerAdapter, хранятся в FragmentManager под сгенерированный с помощью:

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

viewId - это container.getId(), container - ваш экземпляр ViewPager. index - это положение фрагмента. Следовательно, вы можете сохранить идентификатор объекта в 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);

Если вы хотите общаться с этим фрагментом, вы можете получить, если из FragmentManager, например:

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

Ответ 4

Я хочу предложить альтернативное решение, возможно, в немного другом случае, поскольку многие из моих поисков ответов продолжали приводить меня к этой теме.

Мое дело - Я создаю/добавляю страницы динамически и перемещаю их в ViewPager, но при повороте (onConfigurationChange) я получаю новую страницу, потому что, конечно, OnCreate вызывается снова. Но я хочу сохранить ссылку на все страницы, созданные до поворота.

Проблема - У меня нет уникальных идентификаторов для каждого создаваемого фрагмента, поэтому единственным способом ссылки было как-то сохранить ссылки в массиве, который будет восстановлен после изменения вращения/конфигурации.

Обход - Основная идея заключалась в том, чтобы Activity (который отображает фрагменты) также управляет массивом ссылок на существующие фрагменты, поскольку эта активность может использовать Bundles в onSaveInstanceState

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);

Я все еще в процессе обертывания вокруг фрагментов и жизненного цикла Android, поэтому здесь может быть оговорка, что в этом методе могут быть сокращения/неэффективности. Но это работает для меня, и я надеюсь, что это будет полезно для других людей, похожих на мои.

Ответ 5

Мое решение очень грубо, но работает: будучи моими фрагментами, динамически создаваемыми из сохраненных данных, я просто удаляю весь фрагмент из 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 ) );  

Ответ 6

Что это за BasePagerAdapter? Вы должны использовать один из стандартных пейджеров-адаптеров - либо FragmentPagerAdapter, либо FragmentStatePagerAdapter, в зависимости от того, хотите ли вы, чтобы фрагменты, которые больше не нужны ViewPager, либо поддерживаются (первые), либо сохраняются в их состоянии (последним) и снова создаваться при необходимости.

Пример кода для использования ViewPager можно найти здесь

Верно, что управление фрагментами в представлении пейджера по экземплярам активности немного сложнее, потому что FragmentManager в структуре заботится о сохранении состояния и восстановлении любых активных фрагментов, которые сделал пейджер. Все это на самом деле означает, что адаптер при инициализации должен убедиться, что он снова подключается к любым восстановленным фрагментам. Вы можете посмотреть код для FragmentPagerAdapter или FragmentStatePagerAdapter, чтобы узнать, как это делается.

Ответ 7

Если у кого-то возникают проблемы с их FragmentStatePagerAdapter, они не восстанавливают состояние своих фрагментов... т.е.... Fragments создаются FragmentStatePagerAdapter вместо того, чтобы восстанавливать их из состояния...

Убедитесь, что вы вызываете ViewPager.setOffscreenPageLimit() ПЕРЕД вызовом ViewPager.setAdapeter(fragmentStatePagerAdapter)

При вызове ViewPager.setOffscreenPageLimit()... ViewPager будет сразу смотреть на свой адаптер и попытаться получить его фрагменты. Это может произойти до того, как у ViewPager появилась возможность восстановить фрагменты из savedInstanceState (таким образом создавая новые фрагменты, которые нельзя повторно инициализировать из SavedInstanceState, потому что они новы).

Ответ 8

Я придумал это простое и элегантное решение. Он предполагает, что активность отвечает за создание фрагментов, и адаптер просто служит им.

Это код адаптера (здесь нет ничего странного, кроме факта, что 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);
            }
        }
    }

Конечно, вы можете дополнительно настроить этот код, если это необходимо, например, убедившись, что фрагменты являются экземплярами определенного класса.

Ответ 9

Чтобы получить фрагменты после изменения ориентации, вы должны использовать .getTag().

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

Для немного большей обработки я написал свой собственный ArrayList для моего PageAdapter, чтобы получить фрагмент с помощью 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));

Ответ 10

добавить:

   @SuppressLint("ValidFragment")

перед вашим классом.

он не работает, чтобы сделать что-то вроде этого:

@SuppressLint({ "ValidFragment", "HandlerLeak" })