【问题标题】:How can I get the location of an icon in the action menu?如何在操作菜单中获取图标的位置?
【发布时间】:2016-07-05 02:12:30
【问题描述】:

我正在为应用程序开发教程,我需要指向工具栏中的特定图标。

以下是操作菜单的 XML 摘录:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">

<item android:id="@+id/AbErase"
    android:title="@string/Erase"
    android:icon="@android:drawable/ic_delete"
    android:orderInCategory="10"
    app:showAsAction="ifRoom|collapseActionView" />

<item android:id="@+id/AbSuggest"
    android:title="@string/Suggest"
    android:icon="@mipmap/ic_lightbulb_outline_white_48dp"
    android:orderInCategory="50"
    app:showAsAction="ifRoom|collapseActionView" />
<item android:id="@+id/AbUndo"
    android:title="@string/ActionBarUndo"
    android:icon="@android:drawable/ic_menu_revert"
    android:orderInCategory="51"
    app:showAsAction="ifRoom|collapseActionView" />
...

这是我的代码:

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

    isBeingRestored = (savedInstanceState != null);

    Toolbar scToolbar = (Toolbar) findViewById(R.id.Sc_toolbar);
    setSupportActionBar(scToolbar);

    scToolbar.post(new Runnable() {
        @Override
        public void run() {
            if (!isBeingRestored) {
                //View mErase = findViewById(R.id.AbErase);
                View mErase = overflowMenu.getItem(0).getActionView();
                int[] location = new int[2];
                mErase.getLocationOnScreen(location);
                eraseIconLeft = location[0];
            }
        }
    }

View mErase = findViewById(R.id.AbErase); mErase 设置为 null, **** start EDIT **** 这并不奇怪,因为 AbErase 是 MenuItem 的 id,而不是 id的一个视图。 **** 结束编辑 **** View mErase = overflowMenu.getItem(0).getActionView(); location 设置为 (0, 24),这是错误的,因为工具栏中已经有徽标图标和标题。

如何获取工具栏中AbErase 视图的绝对X 坐标?

**** 编辑 **** 这是静态变量overflowMenu的初始化代码:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);

    actionBar.collapseActionView();

    overflowMenu = menu;

    isInitializedMenuItem = menu.findItem(R.id.AbInitialized);
    isInitializedMenuItem.setChecked(isInitializeCbxChecked);

    return super.onCreateOptionsMenu(menu);
}

【问题讨论】:

    标签: android android-actionbar android-menu


    【解决方案1】:

    试试下面的代码sn -p

    @Nullable 
    View getMenuItemView(Toolbar toolbar, @IdRes int menuItemId) throws IllegalAccessException,NoSuchFieldException {
        Field mMenuView = Toolbar.class.getDeclaredField("mMenuView");
        mMenuView.setAccessible(true);
        Object menuView = mMenuView.get(toolbar);
        Field mChildren = menuView.getClass()  //android.support.v7.internal.view.menu.ActionMenuView
                              .getSuperclass() //android.support.v7.widget.LinearLayoutCompat
                              .getSuperclass() //android.view.ViewGroup
                              .getDeclaredField("mChildren");
        mChildren.setAccessible(true);
        View[] children = (View[]) mChildren.get(menuView);
        for (View child : children) {
            if (child.getId() == menuItemId) {
                return child;
            }                          
        }
        return null;                      
    }
    

    假设 android.support.v7.widget 已被使用。上述方法将返回一个与您的菜单项对应的视图。

    View menuItemView = getMenuItemView(scToolbar,R.id.AbErase);
    int x = menuItemView.getX();
    int y = menuItemView.getY();
    

    你有你的坐标。

    编辑

    经过更多研究,我发现不需要反思。

    在获取 menuItemView 之前,我们必须等待 Toolbar 完成其布局传递

    scToolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                scToolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                View menuItem = findViewById(R.id.AbErase);
                if (menuItem != null) {
                    int[] location = new int[2];
                    menuItem.getLocationOnScreen(location);
                    int x = location[0]; //x coordinate
                    int y = location[1]; //y coordinate
                }
            }
        });
    

    上面的sn-p经过测试可以工作,可以放在任何方便的生命周期回调方法中。

    编辑

    在查看活动源时,必须记住的关键是 View menuItem = findViewById(R.id.AbErase); 只能从 ViewTreeObserver.OnGlobalLayoutListeneronGlobalLayout 回调中调用。

    【讨论】:

    • 我在我的Runnable中插入了上面与menuItemView相关的3条指令,如果我没记错的话应该在工具栏布局后执行。删除.getSuperclass() //android.support.v7.internal.widget.LinearLayoutICS 后,menuItemView 设置正确。但是,它的 Left、Top、Right、Bottom 字段都为零。这段代码执行得太早了吗?我应该什么时候执行它,假设它必须在任何用户交互之前执行?
    • @ema3272 你使用的是哪个版本的android支持库?我会查看相关来源,看看我是否遗漏了什么。从技术上讲,它应该在视图处于可见状态时执行,“onCreate”对此有点为时过早,尝试“onResume”应该可以工作。
    • 对 '.getSuperclass() //android.support.v7.internal.widget.LinearLayoutICS' 感到抱歉,'ActionMenuView' 现在是 'LinearLayoutCompat' 的子级,因此出现错误,编辑了答案。
    • 我使用 com.android.support:appcompat-v7:24.0.0 从onResume() 调用getMenuItemView(scToolbar,R.id.AbErase) 时,mMenuView.get(toolbar) 现在返回null
    • R.id.AbErase 是 MenuItem 的 id,而不是 View 的 id,请参阅问题中的 XML。因此,当我在 EDIT 中运行代码时,findViewById(R.id.AbErase) 返回 null。
    【解决方案2】:

    onCreate 方法还为时过早。该视图尚未测量。覆盖onPrepareOptionMenu() 并将您的代码放入其中。同时删除你的 Runnable,这是不必要的。

    【讨论】:

    • 我使用 Runnable 是因为我发现 2. Add a runnable to the layout queue: View.post() 中描述的解决方案非常优雅且面向性能。无论如何,当我按照建议将代码移动到onPrepareOptionMenu 时,mErasenull,无论以何种方式初始化它。所以这也不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-26
    • 2013-04-11
    • 2016-10-10
    • 1970-01-01
    相关资源
    最近更新 更多