Ссылка на фрагменты внутри ViewPager

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

В настоящее время у меня есть что-то вроде этого:

public static class MyPagerAdapter extends FragmentPagerAdapter {

    private static final String TAG = "MyPagerAdapter";
    private static HashMap<Integer, EventListFragment> mPageReferenceMap = new HashMap<Integer, EventListFragment>();

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        Log.i(TAG, "getItem: "+position);
        int dateOffset = position-1;
        EventListFragment mFragment = EventListFragment.newInstance(dateOffset);
        mPageReferenceMap.put(position, mFragment);
        return mFragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Log.i(TAG, "destroyItem: "+position);
        mPageReferenceMap.remove(position);
        super.destroyItem(container, position, object);
    }


    public EventListFragment getFragment(int key) {
        Log.i(TAG, "Size of pager references: "+mPageReferenceMap.size());
        return mPageReferenceMap.get(key);
    }
}

Проблема в том, что destroyItem() получает вызов чаще, чем getItem(), поэтому я остаюсь с нулевыми ссылками. Если я не использую destroyItem(), чтобы очистить ссылки на уничтоженные фрагменты... ну, я ссылаюсь на фрагменты, которые не существуют.

Есть ли хороший способ ссылки на фрагменты, созданные с помощью EventListFragment mFragment = EventListFragment.newInstance(dateOffset);? Или что мне делать, чтобы обновить фрагмент внутри ViewPager из моей активности (из меню опций, чтобы быть точным)?

Ответ 1

Мне удалось это решить. Хитрость заключалась в том, чтобы сделать список ссылок внутри Activity, а не PagerAdapter. Это происходит следующим образом:

List<WeakReference<EventListFragment>> fragList = new ArrayList<WeakReference<EventListFragment>>();

@Override
public void onAttachFragment (Fragment fragment) {
    Log.i(TAG, "onAttachFragment: "+fragment);
    if(fragment.getClass()==EventListFragment.class){
        fragList.add(new WeakReference<EventListFragment>((EventListFragment)fragment));
    }
}
public EventListFragment getFragmentByPosition(int position) {

    EventListFragment ret = null;
    for(WeakReference<EventListFragment> ref : fragList) {
        EventListFragment f = ref.get();
        if(f != null) {
            if(f.getPosition()==position){
                ret = f;
            }
        } else { //delete from list
            fragList.remove(f);
        }
    }
    return ret;

}

Конечно, ваш фрагмент должен реализовать функцию getPosition(), но мне все равно нужно что-то подобное, поэтому это не проблема.

Спасибо Alex Lockwood за ваше предложение с WeakReference!

Ответ 2

Две вещи:

  • Добавьте следующую строку в свой метод Activity onCreate (или где бы вы не инициализировали ViewPager):

    mPager.setOffscreenPageLimit(NUM_ITEMS-1);
    

    Это приведет к тому, что дополнительные дополнительные страницы в памяти (т.е. не будут уничтожены), даже если они не отображаются на экране.

  • Возможно, вы захотите реализовать свой HashMap, чтобы он содержал WeakReference<Fragment> вместо самих Fragment. Обратите внимание, что для этого вам потребуется изменить свой метод getFragment следующим образом:

    WeakReference<Fragment> weakRef = mPageReferenceMap.get(position);
    return (weakRef != null) ? weakRef.get() : null;
    

    Это не имеет ничего общего с вашей проблемой... это просто что-то, что я заметил, и подумал, что приму вашему вниманию. Сохранение WeakReferences вашего Fragment позволит вам использовать способность сборщика мусора определять доступность для вас, поэтому вам не нужно делать это самостоятельно.