【问题标题】:Identifying the view selected in a ContextMenu (Android)识别在 ContextMenu (Android) 中选择的视图
【发布时间】:2020-06-23 01:37:09
【问题描述】:

在 Android 中,onContextItemSelected 有一个 MenuItem 参数,因此不清楚如何识别所选视图。 MenuItem.getMenuInfo 提供对Contextmenu.ContextMenuInfo 的访问,但是虽然两个已知的子类都提供对目标视图的访问,但接口上似乎没有访问器。

另一种方法是将onCreateContextMenu 中提供的View 保存在一个私有类变量中,该变量依赖于onCreateContextMenuonContextItemSelected 之前的活动中不会再次被调用。另一种是将View 的id 用于ContextMenu.additemId 参数。如果我们这样做,则需要通过使用其(可能是国际化的)标题来识别从上下文菜单中选择的选项。

识别onContextSelected中选择的View的最佳方法是什么?

【问题讨论】:

标签: android


【解决方案1】:

对于 Android 中的选项菜单或上下文菜单,没有“识别所选视图”这样的概念。因此,回答您的问题相当困难。所以,我会做一些猜测。

如果“识别所选视图”是指选择了哪个菜单选项,即MenuItem 上的getItemId() 传递给onOptionsItemSelected()onContextItemSelected()

如果“识别所选视图”是指ListView 中的哪一行是长按以调出上下文菜单的行,请将getMenuInfo()(在MenuItem 上调用)转换为AdapterView.AdapterContextMenuInfo ,然后根据您的适配器使用适当的idposition 值。 See here for a sample project 使用了这种技术。

如果“识别所选视图”是指在一个 Activity 中有多个非ListView 上下文菜单,我不会使用该 UI 技术。

【讨论】:

  • 我的意思是第三个选项,通常屏幕上可能有多个视图,其中一些可能希望具有相同的上下文菜单。为什么不使用这种 UI 技术?
  • 简单:上下文菜单没有视觉提示。一小部分 Android 用户知道长按上下文菜单列表。几乎没有人会费心尝试长按上下文菜单的其他任何内容。您通过上下文菜单提供的所有内容都需要由至少一种其他机制提供,以提高可发现性。高级用户可能会发现ListView 上下文菜单,因此提供这些以加快导航速度是可以的。 ListViews 以外的其他内容的上下文菜单?我只是不会打扰。
  • 好点,但你忘了@reply我。假设您必须使用多个上下文菜单。你会用什么方法?
  • @ 回复对 StackOverflow AFAICT 没有特别的影响。同样,我根本不会使用多个上下文菜单。假设有人用枪指着你的亲人,我担心有 N 个具有相同 ID 的菜单项可能会使 Android 感到困惑,所以你的第二个选项似乎很容易最终失败。第一种选择应该更安全,尽管从状态管理的角度来看更麻烦。
【解决方案2】:

上下文菜单的全部意义在于它与单独的底层视图相关联,而在回调“onContextItemSelected”中丢失关联显然是Android中的设计限制。在任何足够大的视图上启用长触摸似乎完全可以替代鼠标右键。

正如其他帖子所建议的,在某些情况下:

AdapterView.AdapterContextMenuInfo menuInfo = 
(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();

是合适的,targetView 是一个有用的参考点。

另一种方法是子类化视图并覆​​盖“getContextMenuInfo”以提供视图参考。比如一个简单的TextView:

包裹 ...; 公共类 TextViewWithContext 扩展 TextView { TextViewContextMenuInfo _contextMenuInfo = null; 公共TextViewWithContext(上下文上下文){ 超级(上下文); _contextMenuInfo = new TextViewContextMenuInfo(this); } public TextViewWithContext(上下文上下文,AttributeSet attrs){ 超级(上下文,属性); _contextMenuInfo = new TextViewContextMenuInfo(this); } 受保护的 ContextMenuInfo getContextMenuInfo() { 返回_contextMenuInfo; } 公共布尔 isContextView(ContextMenuInfo menuInfo) { 返回 menuInfo == (ContextMenuInfo)_contextMenuInfo; } 受保护的类 TextViewContextMenuInfo 实现 ContextMenuInfo { 受保护的TextView _textView = null; 受保护的 TextViewContextMenuInfo(TextView textView) { _textView = 文本视图; } } } ... @覆盖 public boolean onContextItemSelected(MenuItem item) { ContextMenuInfo menuInfo = item.getMenuInfo(); if (textViewWithContext.isContextView(menuInfo) { ... } }

最后,如果基 View 类分配了一个 ContextInfo 对象,该对象具有对视图的反向引用,而不是像现在这样为 null,那将会更有帮助。

【讨论】:

【解决方案3】:

类 TestActivity 扩展 Activity {

// create temp item here

private ImageView tmpImageView = null;

...

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo){
    super.onCreateContextMenu(menu, v, menuInfo);
    // initialize temp item
    mCurrentStatusImage = (ImageView) v.findViewById(R.id.rule_status);
}

public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case ENABLE_ID:
            // use temp item
            tmpImageView.setImageResource(android.R.drawable.presence_online);
            return super.onContextItemSelected(item);
        case DISABLE_ID:
            // use temp item
            tmpImageView.setImageResource(android.R.drawable.presence_invisible);
            return super.onContextItemSelected(item);
        default:    
            return super.onContextItemSelected(item);
 }

【讨论】:

    【解决方案4】:

    我通过根据发送它的项目为 MenuItem 设置 groupID 解决了类似的问题,例如:

        textview.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
            @Override
            public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
                    menu.setHeaderTitle("Context Menu");
                    menu.add(R.id.whateverviewclicked, RENAME_MENU_ITEM, 0, "Rename");
                    menu.add(R.id.whateverviewclicked, DELETE_MENU_ITEM, 1, "Delete");
                 }
             });
    

    这将允许您在 onContextItemSelected 中获取 groupID:

    public boolean onContextItemSelected(MenuItem aItem) {
            int selectedViewID = aItem.getGroupId();
            int selectedItem = aItem.getItemId();
    };
    

    您不必使用资源 ID - 您可以使用任何您想要的 int。为我工作!

    【讨论】:

      【解决方案5】:

      如果您将 ContextMenus 附加到不在 ListView 中的多个视图(即视图下没有适配器)并且您希望确定哪个视图被长按以访问 ContextMenu,则以下“hack”可以得以实施。 (如果 Android 提供一个可以与每个项目关联的侦听器会更好)。

      “hack”是在类中创建一个私有 View 成员 mLastViewTouched,然后将以下 onTouchListener 附加到所有可以生成 ContextMenu 的 View:

       private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
          @Override
          public boolean onTouch(View view, MotionEvent motionEvent)
          {
              mLastViewTouched = view;        // Store a handle on the last view touched. This will be used to identify the view on which the Context Menu was launched
      
              return false;       // We return false since this indicates that the touch was not handled and so it is passed down the stack to be handled appropriately
          }
      };
      

      因此,每当触摸视图时,mLastViewTouched 就会更新。现在在onContextItemSelected 中,您将可以访问启动 ContextMenu 的视图。

      【讨论】:

      • 为什么不从onCreateContextMenu 内部设置一个私有视图成员,而不是创建另一个侦听器并将其单独附加到每个视图?
      【解决方案6】:

      OnCreateContextMenuListenerpublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) 实现中构建菜单时,您可以为每个项目设置自定义MenuItem.OnMenuItemClickListener

          addPhotosBtn.setOnCreateContextMenuListener((menu, v, menuInfo) -> {
              getMenuInflater().inflate(R.menu.upload_image_menu, menu);
              int itemCount = menu.size();
              for(int i = 0; i < itemCount; i++) {
                  menu.getItem(i).setOnMenuItemClickListener(addPhotosBtnMenuItemClickListener);
              }
          });
      

      由于此时您可以访问要为其创建上下文菜单的视图,因此您可以将侦听器与该视图紧密耦合。

      【讨论】:

      • 终于,一个靠谱的答案!
      【解决方案7】:

      这是我区分 2 个列表视图 lstA 和 lstB MenuItem 的方法

          @Override
      public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
          super.onCreateContextMenu(menu, v, menuInfo);
          int startid = (v.getId() == R.id.lstA) ? 0 : 2; //0-1 will be lstA and 2-3 will be lstB
          menu.setHeaderTitle("some title");
          menu.add(Menu.NONE, startid, Menu.NONE, "Item 1");
          menu.add(Menu.NONE, startid+1, Menu.NONE, "Item 2");
      }
      
      @Override
      public boolean onContextItemSelected(MenuItem item) {
          AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
          int menuItemIndex = info.position;
          switch(item.getItemId()) {
              case 0:
                  break;
              case 1:
                  break;
              case 2:
                  break;
              case 3:
                  break;
          }
          return true;
      }
      

      【讨论】:

        猜你喜欢
        • 2016-04-02
        • 1970-01-01
        • 2014-04-05
        • 1970-01-01
        • 1970-01-01
        • 2018-05-05
        • 2021-06-23
        • 2020-09-04
        • 1970-01-01
        相关资源
        最近更新 更多