ViewPager и фрагменты - как правильно сохранить состояние фрагмента? - PullRequest
477 голосов
/ 31 октября 2011

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

442 голосов
/ 10 марта 2012

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

35 голосов
/ 26 марта 2015

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

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

Как видно из исходного кода 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")
16 голосов
/ 04 декабря 2012

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

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

8 голосов
/ 17 апреля 2013

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

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

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

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

2 голосов
/ 05 февраля 2017

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

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

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

0 голосов
/ 29 января 2017

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

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

Это код адаптера (здесь нет ничего странного, за исключением того факта, что 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 Действия.

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

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

0 голосов
/ 24 марта 2014

добавьте:

   @SuppressLint("ValidFragment")

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

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

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