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

Фрагменты, кажется, очень хороши для разделения логики пользовательского интерфейса на некоторые модули. Но наряду с ViewPager его жизненный цикл все еще туманен для меня. Поэтому мысли Гуру крайне необходимы!

редактировать

Смотрите тупое решение ниже;-)

Объем

Основная деятельность имеет ViewPager с фрагментами. Эти фрагменты могут реализовывать несколько иную логику для других (подосновных) действий, поэтому данные фрагментов заполняются через интерфейс обратного вызова внутри действия. И все отлично работает при первом запуске, но!...

проблема

Когда действие воссоздается (например, при смене ориентации), ViewPagerфрагменты. Код (вы найдете ниже) говорит, что каждый раз, когда создается действие, я пытаюсь создать новый ViewPager Адаптер фрагментов аналогичен фрагментам (возможно, это проблема), но FragmentManager уже хранит все эти фрагменты где-то (где?) и запускает для них механизм восстановления. Таким образом, механизм восстановления вызывает "старый" фрагмент onAttach, onCreateView и т. Д. С помощью моего вызова интерфейса обратного вызова для инициации данных через реализованный метод Activity. Но этот метод указывает на вновь созданный фрагмент, который создается с помощью метода onCreate в Activity.

вопрос

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

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 (хоста Activity) с помощью 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();
    ...
}

10 ответов

Решение

Когда 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 (длинный).

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

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

Как видно из исходного кода 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")

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

Мой случай - я создаю / добавляю страницы динамически и помещаю их в 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, поэтому предостережение здесь может заключаться в избыточности / неэффективности в этом методе. Но это работает для меня, и я надеюсь, что может быть полезным для других в случаях, подобных моему.

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

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

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

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

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

Убедитесь, что вы звоните ViewPager.setOffscreenPageLimit() ДО того, как вы позвоните ViewPager.setAdapeter(fragmentStatePagerAdapter)

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

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

//make sure you have the right FragmentManager 
//getSupportFragmentManager or getChildFragmentManager depending on what you are using to manage this stack of fragments
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null) {
   int count = fragments.size();
   for (int x = 0; x < count; x++) {
       Fragment fragment = fragments.get(x);
       //check if this is the fragment we want, 
       //it may be some other inspection, tag etc.
       if (fragment instanceof MyFragment) {
           //do whatever we need to do with it
       }
   }
}

Если у вас много фрагментов и стоимость проверки instanceof может быть не такой, какой вы хотите, но хорошо иметь в виду, что FragmentManager уже ведет учет фрагментов.

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

Это код адаптера (здесь нет ничего странного, за исключением того факта, что mFragments это список фрагментов, поддерживаемый Деятельностью)

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

Вся проблема этого потока заключается в получении ссылки на "старые" фрагменты, поэтому я использую этот код в onCreate Activity.

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

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

Чтобы получить фрагменты после изменения ориентации, вы должны использовать.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));

Добавлять:

   @SuppressLint("ValidFragment")

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

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

@SuppressLint({ "ValidFragment", "HandlerLeak" })
Другие вопросы по тегам