【问题标题】:Android Toolbar overflow menu view idAndroid 工具栏溢出菜单视图 id
【发布时间】:2017-03-22 14:47:58
【问题描述】:

我想要做的是显示一个 PopupWindow 指向工具栏上的溢出图标(三个点)。所以我需要使用图标的 id 获取对 View 对象的引用。但是id是什么?

PopupWindow 用于告诉用户溢出菜单中添加了新条目。并建议用户查看。

【问题讨论】:

    标签: android


    【解决方案1】:

    你应该创建按钮 id

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <item type="id" name="overflowActionButton"/>
    </resources>
    

    然后创建按钮样式

    <style name="Widget.ActionButton.Overflow" parent="Widget.AppCompat.ActionButton.Overflow">
        <item name="android:id">@id/overflowActionButton</item>
    </style>
    

    并在主题中添加此样式

    <style name="Theme.App" parent="Theme.MaterialComponents.Light.NoActionBar">
        <item name="actionOverflowButtonStyle">@style/Widget.ActionButton.Overflow</item>
    </style>
    

    最后你应该通过 id 找到按钮视图

    activity.findViewById(R.id.overflowActionButton)
    

    做你想做的事

    【讨论】:

    • 工作。谢谢。
    【解决方案2】:

    溢出菜单项没有资源 ID。我通过遍历工具栏找到了溢出视图。调试器显示的 id 为 -1,而 Hierarchy Viewer 没有显示资源 id。

    这是我如何找到没有资源 ID 的溢出视图:

    /**
     * Get the OverflowMenuButton.
     *
     * @param activity
     *     the Activity
     * @return the OverflowMenuButton or {@code null} if it doesn't exist.
     */
    public static ImageView getOverflowMenuButton(Activity activity) {
      return findOverflowMenuButton(activity, findActionBar(activity));
    }
    
    static ImageView findOverflowMenuButton(Activity activity, ViewGroup viewGroup) {
      if (viewGroup == null) {
        return null;
      }
      ImageView overflow = null;
      for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
        View v = viewGroup.getChildAt(i);
        if (v instanceof ImageView && (v.getClass().getSimpleName().equals("OverflowMenuButton") ||
            v instanceof ActionMenuView.ActionMenuChildView)) {
          overflow = (ImageView) v;
        } else if (v instanceof ViewGroup) {
          overflow = findOverflowMenuButton(activity, (ViewGroup) v);
        }
        if (overflow != null) {
          break;
        }
      }
      return overflow;
    }
    
    static ViewGroup findActionBar(Activity activity) {
      try {
        int id = activity.getResources().getIdentifier("action_bar", "id", "android");
        ViewGroup actionBar = null;
        if (id != 0) {
          actionBar = (ViewGroup) activity.findViewById(id);
        }
        if (actionBar == null) {
          return findToolbar((ViewGroup) activity.findViewById(android.R.id.content).getRootView());
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
      return null;
    }
    
    static ViewGroup findToolbar(ViewGroup viewGroup) {
      ViewGroup toolbar = null;
      for (int i = 0, len = viewGroup.getChildCount(); i < len; i++) {
        View view = viewGroup.getChildAt(i);
        if (view.getClass() == android.support.v7.widget.Toolbar.class ||
            view.getClass().getName().equals("android.widget.Toolbar")) {
          toolbar = (ViewGroup) view;
        } else if (view instanceof ViewGroup) {
          toolbar = findToolbar((ViewGroup) view);
        }
        if (toolbar != null) {
          break;
        }
      }
      return toolbar;
    }
    

    调用getOverflowMenuButton(activity) 将在onCreate 中返回null,因为尚未布置溢出菜单。要在 onCreate 中获取溢出菜单,我做了以下操作:

    findViewById(android.R.id.content).post(new Runnable() {
      @Override public void run() {
        ImageView overflow = getOverflowMenuButton(MainActivity.this);
      }
    });
    

    【讨论】:

      【解决方案3】:

      我找到了一个名为 TapTarget 的库和一个函数 TapTarget.forToolbarOverflow()。它提出了一个解决方案:https://github.com/KeepSafe/TapTargetView/blob/master/taptargetview/src/main/java/com/getkeepsafe/taptargetview/TapTarget.java#L96

      它找到溢出视图的方式并不整洁,但应该是稳定的。

      【讨论】:

      • 这是一个 hacky 解决方案,可能不适用于较新的 android 版本。
      • 只要你使用androidx.appcompat.widget.Toolbar而不是原生的android.widget.Toolbar,它只取决于支持库版本,而不是Android版本。每次更新支持库时都可以轻松地测试这一点。
      【解决方案4】:

      您想创建自定义DropDown 菜单吗? consider this "native" way

      或在您的menu.xml 中使用android:showAsAction="never"showAsAction 属性 HERE 的文档。当MenuItems 之一设置never 值时,您将自动获得溢出的三点图标,这些MenuItems 将隐藏在那里

      如果确实需要,您也可以尝试使用Hierarchy Viewer 来调查此 ID

      【讨论】:

      • 没有菜单项已经存在。我试图显示一个弹出窗口来告诉用户溢出菜单中添加了几个新选项。弹出窗口将使溢出菜单中的新条目更容易被发现。
      • 或许可以尝试使用Hierarchy Viewer来调查这个id
      【解决方案5】:

      我没有使用昂贵且复杂的布局遍历来查找溢出菜单,而是通过使用 Toolbar 视图作为锚点并将重力设置为 Gravity,实现了在溢出菜单下显示 PopupWindow。END:

       /**
        * Sets the anchor view and shows the popup. In case of narrow display the menu items may be hidden in an overflow
        * menu, in that case anchorView may be null and the popup will be anchored to the end of the toolbar.
        */
       public void show(@Nullable View anchorView, @NonNull View toolbarView) {
           if (anchorView == null) {
               setDropDownGravity(Gravity.END);
               setAnchorView(toolbarView);
           } else {
               setAnchorView(anchorView);
           }
           show();
       }
      

      【讨论】:

        【解决方案6】:

        您可能在菜单资源中创建了一个菜单项 xml 以具有溢出按钮,因此您必须使用您在菜单 xml 中指定的溢出按钮项的 id

        【讨论】: