Fragment中的Context菜单使用来自不同Fragment的ListView:registerForContextMenu(getListView())

时间:2014-08-02 15:21:49

标签: android listview android-fragments fragment contextmenu

我已经尝试过寻找解决方案,但没有发现任何符合我情况的解决方案。我有一个MainActivity,它扩展了FragmentActivity和多个ListFragments。我使用PagerSlidingTabStrip库作为slidetabs和一个ViewPager。片段没有XML布局,它们只是ListFragments,它返回ListView,因此不需要布局。

这是一个音板应用程序,长按列表项,允许用户将声音文件设置为铃声,通知或闹钟,或保存到SD卡。

现在,所有片段加载自己的数据就好了。一切似乎都没问题,但是,当我在后台加载的Fragment上使用上下文菜单时,似乎是使用了在它之前加载的第一个或前一个片段中的ListView,它是在创建它时可见的

我的意思是,说我的MainActivity开始,它加载FragmentA,并在后台FragmentB也被预加载。

在onActivityCreated方法中,对于两个片段,它使用registerForContextMenu(getListView())。

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

    // Data loading etc

    MyAdapter adapter = new MyAdapter(getActivity(),
            R.layout.data_row, data);
    setListAdapter(adapter);

    registerForContextMenu(getListView());
}

但似乎正在发生的事情是FragmentB调用了registerForContextMenu(getListView())并且它似乎正在使用当前活动的ListView,它是FragmentA的列表。

所以,假设我选择从上下文菜单中保存文件。我长按了FragmentB的第一项,但它试图保存FragmentA的第一项。如果我只是点击列表项,它会按照您的预期播放它自己的声音,但上下文菜单命令会使用预加载时可见的片段列表。

这是onCreateContextMenu。注意,此时它在上下文菜单标题中使用了正确的项目标题。

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

    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
    menu.setHeaderTitle(data.get(info.position).getDataName());
    MenuInflater inflater = this.getActivity().getMenuInflater();
    inflater.inflate(R.menu.context_menu, menu);
}

这是onContextItemSelected。

@Override
public boolean onContextItemSelected(MenuItem item) {
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
    int dataID = data.get(info.position).getDataID();
    String dataName = data.get(info.position).getDataName();

    Activity activity = getActivity();
    if(activity instanceof MainActivity) {
        switch (item.getItemId()){
        case R.id.context_ringtone:
            ((MainActivity) activity).setRingtone(dataID, dataName);
            return true;
        case R.id.context_notification:
            ((MainActivity) activity).setNotification(dataID, dataName);
            return true;
        case R.id.context_alarm:
            ((MainActivity) activity).setAlarm(dataID, dataName);
            return true;
        case R.id.context_sd_card:
            ((MainActivity) activity).saveFile(dataID, dataName);
            return true;
        default:
            return super.onContextItemSelected(item);
        }
    }
    return super.onContextItemSelected(item);
}

除了预加载的Fragments使用错误的ListView之外,这一切都按预期工作。

所以我正在寻找的是一种方法来确保当FragmentB在后台加载,而另一个Fragment处于活动状态时,FragmentB应该为上下文菜单注册它自己的ListView,而不是发生哪个活动的ListView在当时可见。

我尝试过使用MyFragment.this.getListView(),我尝试过使用setUserVisibleHint方法,但这些方法没有帮助。我已经尝试在onResume中执行registerForContextMenu(getListView()),希望它在Fragment变为活动状态时重新注册正确的ListView。

非常感谢任何帮助,谢谢!

2 个答案:

答案 0 :(得分:15)

我终于搞清楚了!

事实证明,不只是为错误的ListView调用上下文菜单,正在为所有片段调用上下文菜单。

这是通过在onContextItemSelected方法中的if语句中使用getUserVisibleHint()来解决的,这样如果调用上下文菜单的Fragment不可见,它将返回false,但如果它是当前可见的Fragment,则将执行正确的代码,意味着跳过不是预期片段的片段。我曾经以不同的方式尝试过getUserVisibleHint()但是我没有从正确的角度考虑问题。

以下是解决方案的示例。

@Override
public boolean onContextItemSelected(MenuItem item) {
    if (getUserVisibleHint()) {
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        int dataID = data.get(info.position).getDataID();
        String dataName = data.get(info.position).getDataName();

        Activity activity = getActivity();
        if(activity instanceof MainActivity) {
            switch (item.getItemId()){
            case R.id.context_ringtone:
                ((MainActivity) activity).setRingtone(dataID, dataName);
                return true;
            case R.id.context_notification:
                ((MainActivity) activity).setNotification(dataID, dataName);
                return true;
            case R.id.context_alarm:
                ((MainActivity) activity).setAlarm(dataID, dataName);
                return true;
            case R.id.context_sd_card:
                ((MainActivity) activity).saveFile(dataID, dataName);
                return true;
            default:
                return super.onContextItemSelected(item);
            }
        }
    }
    return false;
}

答案 1 :(得分:0)

使用registerForContextMenu(getListView())代替使用getListView().setOnCreateContextMenuListener()

下面是一个示例:

    public class ListViewFragment extends Fragment  implements AdapterView.OnItemLongClickListener {

        private int position;

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);

            getListView().setOnCreateContextMenuListener(this);
            getListView().setOnItemLongClickListener(this);
        }

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

            String title = mAdapter.getTitle(position);

            menu.setHeaderTitle(title);
            menu.add(0, MenuIds.play, 0, getString(R.string.play));

         }

         @Override
         public boolean onContextItemSelected(MenuItem item) {
             Log.e("onContextItemSelected",String.valueOf(position));
             return true;
         }

         @Override
         public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
             this.position = position;
             return false;
         }
    }