Подтвердить что ты не робот

Неверный фрагмент в ViewPager получает onContextItemВыбранный вызов

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

Когда в контекстном меню в одном из фрагментов выбирается элемент контекстного меню, неправильный фрагмент принимает вызов onContextItemSelected.

Например, если я нахожусь на фрагменте # 3 в пейджере, вместо этого вместо него получает фрагмент в позиции # 2. Если я вернусь назад к фрагменту № 2, вместо этого вместо него будет найден фрагмент # 3.

У меня есть образец здесь.

(В настоящее время я работаю над этим в своем собственном приложении, имея переменную mHandleContext в каждом фрагменте и включаю/отключая ее при изменении страницы. Таким образом, вызов onContextItemSelected выйдет во все фрагменты пока не назовет правый.)

Я делаю что-то неправильно или это ошибка в библиотеке поддержки? В качестве побочной заметки этого не произошло, когда я использовал ActionBarSherlock 3.5.1, у которого была собственная вилка библиотеки поддержки.

4b9b3361

Ответ 1

Итак, это какое-то идиотское дизайнерское решение от Google или что-то, что просто совсем не считалось. Самый простой способ обойти это - обернуть вызов onContextItemSelected с помощью оператора if следующим образом:

if (getUserVisibleHint()) {
    // Handle menu events and return true
} else
    return false; // Pass the event to the next fragment

В библиотеке совместимости в ActionBarSherlock 3.5 был взломан такой файл.

Ответ 2

Это происходит из-за этого:

public boolean dispatchContextItemSelected(MenuItem item) {
    if (mActive != null) {
        for (int i=0; i<mAdded.size(); i++) {
            Fragment f = mAdded.get(i);
            if (f != null && !f.mHidden) {
                if (f.onContextItemSelected(item)) {
                    return true;
                }
            }
        }
    }
    return false;
}

Как вы можете видеть, FragmentManager вызывает Fragment.onContextItemSelected для всех своих собственных фрагментов, пока не вернет true. В вашем примере я могу предложить такое исправление:

    public static class TestListFragment extends ListFragment {

    private int mNumber = 0;
    private ArrayList<String> mItems;

    public static TestListFragment newInstance(int number) {
        Bundle args = new Bundle();
        args.putInt("number", number + 1);

        TestListFragment fragment = new TestListFragment();
        fragment.setArguments(args);

        return fragment;
    }

    public TestListFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mNumber = getArguments().getInt("number");
        mItems = new ArrayList<String>();
        mItems.add("I am list #" + mNumber);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mItems));
        registerForContextMenu(getListView());
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.add(mNumber, 0, 0, "Hello, World!");
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if(item.getGroupId() == mNumber){
            Log.d("ViewPagerContextMenuBug", "onContextItemSelected called for number " + mNumber);
            Toast.makeText(getActivity(), "onContextItemSelected called for number " + mNumber, Toast.LENGTH_SHORT).show();
            return true;
        }
        return false;
    }

}

Ответ 3

О, Google, я имею в виду WTF?

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

Вы можете использовать MenuItem.OnMenuItemClickListener, чтобы заставить меню фрагмента не использовать all onContextItemSelected, но только ту же функцию этого фрагмента.

Использовать следующую реализацию:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    MenuInflater inflater = getActivity().getMenuInflater();
    if (v == btnShare) {
        inflater.inflate(R.menu.share_menu, menu);
        for (int i = 0; i < menu.size(); ++i) {
            MenuItem item = menu.getItem(i);
            item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {
                    onContextItemSelected(item);
                    return true;
                }
            });
        }
    }
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
    switch (item.getItemId()) {
        case R.id.print:
        // ...
    }
}

Ответ 4

Использование намерений для каждого из пунктов меню работало хорошо для меня.

    @Override  
    public void onCreateContextMenu(ContextMenu menu, View v, ContextmenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);

        MenuInflater inflater = super.getActivity.getMenuInflater();

        inflater.infalte(R.menu.list_item, menu);

        for(int i = 0; i < menu.size(); i++) {
            MenuItem item = menu.getItem(i);
            Intent intent = new Intent();
            intent.putExtra(KEY_EXTRA_FRAGMENT_ID, this.fragmentId);
            if (item != null) {
                item.setIntent(intent);
            }
        }
    }

    @Override
    public boolean onContextItemSelected(MeniItem item) {

        Intent intent = item.getIntent();

        if (intent != null) {
            if (intent.getIntExtra(KEY_EXTRA_FRAGMENT_ID, -1) == this.fragmentId) {

                // Implement code according the item function.

                return true;
            }
        }

        return super.onContextItemSelected(item);
    }