Вид фрагмента ViewPager теряется, когда родительский фрагмент ViewPager скрыт, а затем показан
Я видел какое-то странное поведение с моим ViewPager вместе с моим собственным FragmentStatePagerAdapter.
иерархия моего представления выглядит так:
-> (1) Fragment root view (RelativeLayout)
-> (2) ViewPager
-> (3) ViewPager's current fragment view
когда фрагмент, который отвечает за корневое представление фрагмента (1), скрывается (с помощью.скрыть() в транзакции фрагмента), а затем показано (С.show ()), представление фрагмента, которое в настоящее время показывалось в ViewPager (3), становится нулевым, хотя фрагмент все еще существует. В принципе, мой ViewPager будет полностью пустой / прозрачный.
единственный способ, который я нашел, чтобы исправить это, чтобы позвонить
int current = myViewPager.getCurrentItem();
myViewPager.setAdapter(myAdapter);
myViewPager.setCurrentItem(current);
после того, как родительский фрагмент будет показан. Это каким-то образом вызывает представления, которые будут воссозданы и появятся на экране. К сожалению, это иногда вызывает исключения, связанные с вызовом адаптера пейджера unregisterDataSetObserver()
дважды на старого наблюдателя.
есть ли лучший способ сделать это? Я думаю, что я спрашиваю:
почему мои фрагментные представления внутри мой ViewPager разрушается, когда родительский фрагмент ViewPager скрыт?
обновление: это также происходит, когда приложение "свернуто", а затем "восстановлено" (нажав клавишу home action, а затем вернувшись).
по запросу, вот мой класс адаптера пейджера:
public class MyInfoSlidePagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<MyInfo> infos = new ArrayList<MyInfo>();
public MyInfoSlidePagerAdapter (FragmentManager fm) {
super(fm);
}
public MyInfoSlidePagerAdapter (FragmentManager fm, MyInfo[] newInfos) {
super(fm);
setInfos(newInfos);
}
@Override
public int getItemPosition(Object object) {
int position = infos.indexOf(((MyInfoDetailsFragment)object).getMyInfo());
return position > 0 ? position : POSITION_NONE;
}
@Override
public CharSequence getPageTitle(int position) {
return infos.get(position).getName();
}
@Override
public Fragment getItem(int i) {
return infos.size() > 0 ? MyInfoDetailsFragment.getNewInstance(infos.get(i)) : null;
}
@Override
public int getCount() {
return infos.size();
}
public Location getMyInfoAtPosition(int i) {
return infos.get(i);
}
public void setInfos(MyInfo[] newInfos) {
infos = new ArrayList<MyInfo>(Arrays.asList(newInfos));
}
public int getPositionOfMyInfo(MyInfo info) {
return infos.indexOf(info);
}
}
я переименовал некоторые переменные, но это именно то, что я есть.
5 ответов:
вы не предоставляете достаточно информации для вашей конкретной проблемы, поэтому я построил пример проекта, который пытается воспроизвести вашу проблему: приложение имеет действие, которое содержит фрагмент (
PagerFragment
) в относительном макете и ниже этого макета у меня есть кнопка, которая скрывает и показывает вышеPagerFragment
.PagerFragment
естьViewPager
и каждый фрагмент в адаптере пейджера просто отображает метку - этот фрагмент называетсяDataFragment
. Список меток создается в Родительском действии и передается в PagerFragment, а затем через его адаптер к каждомуDataFragment
. ИзменениеPagerFragment
видимость делается без проблем, и каждый раз, когда он становится видимым снова он показывает предыдущий показанный ярлык.ключевой вопрос: Используйте фрагмент#getChildFragmentManager() когда вы создаете адаптер viewpager, а не getFragmentManager!
может быть, вы можете сравнить этот простой проект с тем, что у вас есть, и проверить, где есть различия. Так вот (сверху-вниз):
PagerActivity (только в проекте):
public class PagerActivity extends FragmentActivity { private static final String PAGER_TAG = "PagerActivity.PAGER_TAG"; @Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstance); setContentView(R.layout.pager_activity); if (savedInstance == null) { PagerFragment frag = PagerFragment.newInstance(buildPagerData()); FragmentManager fm = getSupportFragmentManager(); fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit(); } findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { changeFragmentVisibility(); } }); } private List<String> buildPagerData() { ArrayList<String> pagerData = new ArrayList<String>(); pagerData.add("Robert de Niro"); pagerData.add("John Smith"); pagerData.add("Valerie Irons"); pagerData.add("Metallica"); pagerData.add("Rammstein"); pagerData.add("Zinedine Zidane"); pagerData.add("Ronaldo da Lima"); return pagerData; } protected void changeFragmentVisibility() { Fragment frag = getSupportFragmentManager().findFragmentByTag(PAGER_TAG); if (frag == null) { Toast.makeText(this, "No PAGER fragment found", Toast.LENGTH_SHORT).show(); return; } boolean visible = frag.isVisible(); Log.d("APSampler", "Pager fragment visibility: " + visible); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (visible) { ft.hide(frag); } else { ft.show(frag); } ft.commit(); getSupportFragmentManager().executePendingTransactions(); } }
макет файла pager_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="4dp" > <Button android:id="@+id/btnFragments" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Hide/Show fragments" /> <RelativeLayout android:id="@+id/layout_fragments" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/btnFragments" android:layout_marginBottom="4dp" > </RelativeLayout> </RelativeLayout>
обратите внимание, что я добавляю
PagerFragment
когда активность впервые показана-иPagerFragment
класс:public class PagerFragment extends Fragment { private static final String DATA_ARGS_KEY = "PagerFragment.DATA_ARGS_KEY"; private List<String> data; private ViewPager pagerData; public static PagerFragment newInstance(List<String> data) { PagerFragment pagerFragment = new PagerFragment(); Bundle args = new Bundle(); ArrayList<String> argsValue = new ArrayList<String>(data); args.putStringArrayList(DATA_ARGS_KEY, argsValue); pagerFragment.setArguments(args); return pagerFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); data = getArguments().getStringArrayList(DATA_ARGS_KEY); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.pager_fragment, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { pagerData = (ViewPager) view.findViewById(R.id.pager_data); setupPagerData(); } private void setupPagerData() { PagerAdapter adapter = new LocalPagerAdapter(getChildFragmentManager(), data); pagerData.setAdapter(adapter); } }
его макет (только ViewPager, который принимает полный размер):
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager_data" android:layout_width="match_parent" android:layout_height="match_parent" />
и его адаптера:
public class LocalPagerAdapter extends FragmentStatePagerAdapter { private List<String> pagerData; public LocalPagerAdapter(FragmentManager fm, List<String> pagerData) { super(fm); this.pagerData = pagerData; } @Override public Fragment getItem(int position) { return DataFragment.newInstance(pagerData.get(position)); } @Override public int getCount() { return pagerData.size(); } }
этот адаптер создает
DataFragment
для каждой страницы:public class DataFragment extends Fragment { private static final String DATA_ARG_KEY = "DataFragment.DATA_ARG_KEY"; private String localData; public static DataFragment newInstance(String data) { DataFragment df = new DataFragment(); Bundle args = new Bundle(); args.putString(DATA_ARG_KEY, data); df.setArguments(args); return df; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); localData = getArguments().getString(DATA_ARG_KEY); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.data_fragment, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { view.findViewById(R.id.btn_page_action).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getActivity(), localData, Toast.LENGTH_SHORT).show(); } }); ((TextView) view.findViewById(R.id.txt_label)).setText(localData); } }
и
DataFragment
'макет с:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="4dp" > <Button android:id="@+id/btn_page_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Interogate" /> <TextView android:id="@+id/txt_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textAppearance="?android:attr/textAppearanceLarge" /> </RelativeLayout>
наслаждайтесь кодирования!
может это поможет mViewPager.setOffscreenPageLimit (5)
установите количество страниц, которые должны быть сохранены с обеих сторон текущая страница в иерархии представлений находится в состоянии ожидания. Страницы за этим лимит будет воссоздан из адаптера при необходимости.
Это предлагается в качестве оптимизации. Если вы заранее знаете номер страниц вам нужно будет поддерживать или иметь механизмы ленивой загрузки в место на ваших страницах, настройка этого параметра может иметь преимущества в воспринимается плавность подкачки анимации и взаимодействия. Если у вас есть небольшое количество страниц (3-4), которые вы можете сохранить активными все сразу, меньше времени будет потрачено на макет для вновь созданных поддеревьев вида как пользователь страницы вперед и назад.
вы должны держать этот лимит низкий, особенно, если ваши страницы имеют сложные макеты. Этот параметр по умолчанию равен 1.
View Pager довольно непреклонен в сохранении своих фрагментов свежими всегда и, таким образом, оптимизирует производительность, освобождая память, когда фрагмент не используется. Очевидно, что это действительно полезная черта в мобильной системе. Но из-за этого постоянного освобождения ресурсов фрагмент создается каждый раз, когда он получает фокус.
mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS);
здесь документация.
этой Старый Пост интересное решение для вашей проблемы.. Пожалуйста, Обратитесь
у меня была та же проблема. Мое приложение (FragmentActivity) имеет пейджер (ViewPager) с 3 framgents. Во время прокрутки между фрагментами они разрушаются и воссоздаются все время. На самом деле это не создает проблем в функциональности (ожидайте незамкнутые курсоры), но мне также было интересно об этом вопросе.
Я не знаю, есть ли обходной путь для изменения поведения ViewPager, но я предлагаю иметь объект конфигурации( возможно, статический) и перед уничтожением сохранить ваш
myViewPager
объект в объекте конфигурации.public class App extends FragmentActivity { static MyData data; @Override protected void onCreate(Bundle savedInstanceState) { data = (MyData) getLastCustomNonConfigurationInstance(); if (data == null) { data = new MyData(); data.savedViewPager = myViewPager; } else { myViewPager = data.savedViewPager; } } @Override public Object onRetainCustomNonConfigurationInstance() { Log.d("onRetainCustomNonConfigurationInstance", "Configuration call"); return data; } } public class MyData { public ViewPager savedViewPager; }
таким образом, вы можете сохранить ссылку на объект, который не будет уничтожен, следовательно, есть ссылка на него, и вы можете перезагрузить все ваши важные объекты.
Я надеюсь, что вы найдете мое предложение полезным!