Элементы действий из начального фрагмента Viewpager не отображаются

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

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

Код активности:

package net.solarnz.apps.fragmentsample;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;

public class FragmentSampleActivity extends Activity {
    private ViewPagerAdapter mViewPagerAdapter;
    private ViewPager mViewPager;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        if (mViewPagerAdapter == null) {
            mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
        }

        mViewPager = (ViewPager) findViewById(R.id.log_pager);
        mViewPager.setAdapter(mViewPagerAdapter);
        mViewPager.setCurrentItem(0);
    }


    private class ViewPagerAdapter extends FragmentStatePagerAdapter {
        public ViewPagerAdapter(FragmentManager fm) {
            super(fm);
        }

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

        @Override
        public Fragment getItem(int position) {
            Fragment f = Fragment1.newInstance(position);
            // f.setRetainInstance(true);
            f.setHasOptionsMenu(true);
            return f;
        }
    }
}

Код фрагмента:

package net.solarnz.apps.fragmentsample;

import android.app.Fragment;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;

public class Fragment1 extends Fragment {
    int mNum;

    static Fragment newInstance(int num) {
        Fragment1 f = new Fragment1();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        mNum = getArguments() != null ? getArguments().getInt("num") : 0;
    }

    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_list, menu);
    }

}

Макет:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

            <android.support.v4.view.ViewPager
                android:id="@+id/log_pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
</LinearLayout>

Меню:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_refresh"
          android:title="Refresh"
          android:icon="@android:drawable/ic_delete"
          android:showAsAction="ifRoom|withText" />
</menu>

Заполняется меню действий: https://i.stack.imgur.com/QFMDd.png

Меню действий не заполняется: http://i.stack.imgur.com/sH5Pp.png


person solarnz    schedule 18.02.2012    source источник
comment
Согласитесь с Solarnz, что, похоже, нет очевидного обходного пути. Я создал демонстрационное приложение, которое позволяет надежно воспроизвести проблему. Та же проблема, по-видимому, сообщается в ActionBarSherlock, и я поднял этот вопрос проблема в системе отслеживания проблем Android.   -  person snodnipper    schedule 26.04.2012


Ответы (8)


Вы должны прочитать это (от xcolw...)

В ходе экспериментов кажется, что основная причина заключается в том, что invalidateOptionsMenu вызывается более чем один раз без перерыва в основном потоке для обработки поставленных в очередь заданий. Предположение - это имело бы значение, если бы какая-то важная часть создания меню была отложена через сообщение, оставив панель действий в плохом состоянии до тех пор, пока она не запустится.

Это может произойти в нескольких неочевидных местах:

  1. вызов viewPager.setCurrentItem несколько раз для одного и того же элемента

  2. вызов viewPager.setCurrentItem в onCreate действия. setCurrentItem вызывает недействительность меню параметров, за которым сразу же следует недействительность меню параметров действия.

Обходные пути, которые я нашел для каждого

  1. Охраняйте вызов viewPager.setCurrentItem

    if (viewPager.getCurrentItem() != position)
        viewPager.setCurrentItem(position);
    
  2. Отложите вызов viewPager.setCurrentItem в onCreate

    public void onCreate(...) {
        ...
        view.post(new Runnable() {
            public void run() {
                // guarded viewPager.setCurrentItem
            }
        }
    }
    

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

источник http://code.google.com/p/android/issues/detail?id=29472

person Wayne    schedule 21.08.2012
comment
Пожалуйста, предоставьте полный фрагмент, как использовать это с просмотрщиком без вкладок, мне не ясно. Спасибо. - person Georgy Gobozov; 31.07.2013
comment
Это не сработало для меня. mViewPager.post(new Runnable() { @Override public void run() { mViewPager.setAdapter(new FragPagerAdapter(getSupportFragmentManager(), MainActivity.this)); if (mViewPager.getCurrentItem()!= 1) { mViewPager.setCurrentItem( 1); } } }); - person bernardo.g; 30.03.2017
comment
куда я должен добавить эти вещи? может предоставить какой-нибудь фрагмент? - person Pouya Danesh; 30.07.2017

Простой ответ — не использовать меню внутри фрагментов в ViewPager. Если вам нужно использовать меню внутри фрагментов, я предлагаю загрузить меню с помощью метода onCreateOptionsMenu в родительском действии. Очевидно, вам нужно будет определить, какое меню показывать.

Я смог добиться этого, используя отражение класса. Вам также нужно будет использовать метод invalidateOptionsMenu каждый раз, когда вы переключаете страницы. Вам понадобится OnPageChangeListener для вызова этого, когда ViewPager меняет страницы.

person solarnz    schedule 23.03.2012

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

Посмотреть код адаптера пейджера

 public class ScreensAdapter extends FragmentPagerAdapter {

    public TrackerScreensAdapter(Context context, FragmentManager fm) {
        super(fm);
    }

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

    public Fragment getItem(int position) {
        Fragment fragment = null;
        switch (position){
            case 0:
                fragment = new Fragment1();
                break;
            case 1:
                fragment = new Fragment2();
                break;
        }
        return fragment;
    }

}

Активность при создании

    screensAdapter = new ScreensAdapter(this, getFragmentManager());
    viewPager.setAdapter(screensAdapter);

Таким образом, мой viewPager имеет два фрагмента, каждый фрагмент запускает свою задачу в onActivityCreated, получает данные и рисует макет на основе полученных данных. Также каждый фрагмент имеет onCreateOptionsMenu

  @Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setHasOptionsMenu(true);
    MyTask task = new MyTask();
    task.setTaskListener(this);
    task.execute();


}

 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.fragment_menu, menu);
}

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

    screenAdapter = new ScreenAdapter(this, getFragmentManager());
    viewPager.post(new Runnable() {
        public void run() {
            viewPager.setAdapter(screenAdapter);
        }
    });
person Georgy Gobozov    schedule 20.08.2013
comment
Спасибо! установка адаптера внутри runnable решила это для меня - person Eidan Spiegel; 25.06.2015

В моем случае я проследил основную причину проблемы до того, что, по моему мнению, является ошибкой в ​​FragmentStatePagerAdapter, которая вызывает Fragment#setMenuVisibility в false и неспособность правильно установить его обратно в true, когда он восстанавливает свое состояние.

Обходные пути:

  1. Используйте FragmentPagerAdapter вместо FragmentStatePagerAdapter

  2. Если вы должны использовать FragmentStatePagerAdapter, в вашем подклассе адаптера переопределите setPrimaryItem следующим образом:

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        super.setPrimaryItem(container, position, object);
    
        //This is a workaround for a bug in FragmentStatePagerAdapter
        Fragment currentItem = getItem(position);
        if (currentItem != null) {
            currentItem.setMenuVisibility(true);
            currentItem.setUserVisibleHint(true);
        }
    }
    
person Ilan Klinghofer    schedule 06.01.2015
comment
Этот ответ спас чью-то жизнь! - person grrigore; 07.01.2021

Сначала создайте метод в подклассе FragmentPagerAdapter, чтобы получить текущий фрагмент

    public SherlockFragment getFragment() {
    return currentFragment;
}

        @Override
public void onTabSelected(final Tab tab, FragmentTransaction ft) {

    ((SherlockFragmentActivity) mContext).invalidateOptionsMenu();
    Fragment f = ((SherlockFragmentActivity) mContext)
            .getSupportFragmentManager().findFragmentByTag(
                    makeFragmentName(tab.getPosition()));

    currentFragment=(SherlockFragment) f;
 }

Теперь переопределите нижеприведенные методы в Main Activity.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    if (mTabsAdapter.getPositionOfTabSelected() != 0) {
        menu.add("Read").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        menu.add("Write").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        menu.add("Clear").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        menu.add("Factory data reset").setShowAsAction(
                MenuItem.SHOW_AS_ACTION_IF_ROOM);
    }

    return super.onCreateOptionsMenu(menu);
}

Теперь вызовите onOptionItemSelected из активности во фрагмент

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    mTabsAdapter.getFragment().onOptionsItemSelected(item);
    return super.onOptionsItemSelected(item);
}
person Deepak Goel    schedule 25.03.2013

Я решил очень похожую проблему, в которой значки панели действий, назначенные фрагментом внутри ViewPager, исчезали при паузе(). Они снова появлялись, когда Фрагмент возвращался в поле зрения и пользователь проводил пальцем влево или вправо, но не сразу. Решение заключалось в вызове notifyDataSetChanged() для PagerAdapter в методе onResume() фрагмента.

@Override
public void onResume() {
    mPagerAdapter.notifyDataSetChanged();
}
person Matt W    schedule 26.02.2014

У меня возникла эта проблема с элементами панели действий, когда я использовал HorizontalScrollView для отображения вкладок, но я перешел на PagerTitleStrip, и проблема была решена.

Возможно, эта информация поможет кому-то еще.

person Jose M Lechon    schedule 17.03.2014

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

В основной деятельности:

boolean isFragmentVisible(int fragmentIndex) { ... }

В onCreateOptionsMenu() в вашем фрагменте:

if ( getActivity().isFragmentVisible(HOME_FRAGMENT_POS) ) {
    inflater.inflate(R.menu.menu_home_fragment, menu);
}
person Lane    schedule 29.12.2020