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

Posted

技术标签:

【中文标题】Fragment 中的上下文菜单使用来自不同 Fragment 的 ListView: registerForContextMenu(getListView())【英文标题】:Context Menu in Fragment uses ListView from a different Fragment: registerForContextMenu(getListView()) 【发布时间】:2014-09-25 14:52:11 【问题描述】:

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

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

现在,所有 Fragment 都可以正常加载自己的数据。一切似乎都很好,但是,当我在后台加载的 Fragment 上使用上下文菜单时,它似乎正在使用在它之前加载的第一个或上一个 Fragment 中的 ListView,它在创建时可见.

我的意思是,假设我的 MainActivity 启动,它加载 FragmentA,并且在后台 FragmentB 也被预加载。

在onActivityCreated方法中,对于两个Fragment,它都使用了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。

任何帮助将不胜感激,谢谢!

【问题讨论】:

【参考方案1】:

我终于想通了!

事实证明,这不仅仅是为错误的 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;

【讨论】:

有同样的问题,ViewPager 的活动,每个页面都有自己的 ListView。 getUserVisibleHint() 在这里可以解决问题。 这似乎不是最好的解决方案。在可以同时显示多个片段的应用程序中它会失败。【参考方案2】:

不要使用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;
         
    

【讨论】:

My app calls setOnCreateContextMenuListener on the list view as you suggest, but even so, every fragment is notified when a context menu item is selected.

以上是关于Fragment 中的上下文菜单使用来自不同 Fragment 的 ListView: registerForContextMenu(getListView())的主要内容,如果未能解决你的问题,请参考以下文章

Android 如何从 Fragment 编辑操作栏菜单

如何在Fragment中使用Actionbar-Android开发问答

如何从 qgraphicsview 中的 qgraphicsitem 获取不同的上下文菜单?

android UI:底部菜单栏的学习与制作——Fragment碎片一

为啥android fragment 不调用 oncreateview方法

Android:长按文本视图后,我需要弹出上下文菜单