Вид фрагмента 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 59

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

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

Я надеюсь, что вы найдете мое предложение полезным!

для меня, я изменил getChildFragmentManager() вместо getFragmentManager() и работает хорошо. Ex:

pagerAdapt = new PagerAdapt(getChildFragmentManager());