如何在操作菜单中获取图标的位置?

时间:2016-06-29 06:53:40

标签: android android-actionbar android-menu

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

以下是操作菜单的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,**** 启动编辑 ****,这并不令人惊讶,因为AbErase是id一个MenuItem,而不是View的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);
}

2 个答案:

答案 0 :(得分:3)

尝试以下代码段

@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
            }
        }
    });

上面的代码片段经过测试可以工作,它可以放在任何方便的生命周期回调方法中。

修改

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

答案 1 :(得分:0)

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