21

我想MenuItem在应用程序的ActionBar. 要正确定位它,我需要知道 a 的 x 位置MenuItem

到目前为止,谷歌没有给我带来任何有用的结果。

甚至可能吗?

4

4 回答 4

34

我找到了另一种方法来挂钩操作栏按钮,这可能比公认的解决方案简单一些。您可以简单地调用findViewById, 菜单项的 id!

现在困难的部分是你什么时候可以调用这个?在onCreateonResume中,为时过早。一种方法是使用ViewTreeObserver窗口的:

    final ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getViewTreeObserver();
    viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            View menuButton = findViewById(R.id.menu_refresh);
            // This could be called when the button is not there yet, so we must test for null
            if (menuButton != null) {
                // Found it! Do what you need with the button
                int[] location = new int[2];
                menuButton.getLocationInWindow(location);
                Log.d(TAG, "x=" + location[0] + " y=" + location[1]);

                // Now you can get rid of this listener
                viewTreeObserver.removeGlobalOnLayoutListener(this);
            }
        }
    });

这进去了onCreate

注意:这是在使用 ActionBarSherlock 的项目上测试的,因此它可能不适用于“真实”操作栏。

于 2012-11-09T00:14:26.740 回答
14

我终于找到了这个问题的答案!这有点hacky,但不是太多。唯一的缺点是:

  1. 您必须将MenuItems 替换为您自己的View. AnImageView与标准的非常相似,但它缺少长按以查看工具提示等功能,可能还有其他功能。

  2. 我没有测试很多。

好的,这就是我们的做法。Activity将您的 '更改onCreateOptionsMenu()为如下所示:

View mAddListingButton;

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.activity_main, menu);
    
    // Find the menu item we are interested in.
    MenuItem it = menu.findItem(R.id.item_new_listing);
    
    // Create a new view to replace the standard one.
    // It would be nice if we could just *get* the standard one
    // but unfortunately it.getActionView() returns null.
    
    // actionButtonStyle makes the 'pressed' state work.
    ImageView button = new ImageView(this, null, android.R.attr.actionButtonStyle);
    button.setImageResource(R.drawable.ic_action_add_listing);
    // The onClick attributes in the menu resource no longer work (I assume since
    // we passed null as the attribute list when creating this button.
    button.setOnClickListener(this);
    
    // Ok, now listen for when layouting is finished and the button is in position.
    button.addOnLayoutChangeListener(new OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom,
                int oldLeft, int oldTop, int oldRight, int oldBottom)
        {
            // Apparently this can be called before layout is finished, so ignore it.
            // Remember also that it might never be called with sane values, for example
            // if your action button is pushed into the "more" menu thing.
            if (left == 0 && top == 0 && right == 0 && bottom == 0)
                return;
            
            // This is the only way to get the absolute position on the screen.
            // The parameters passed to this function are relative to the containing View
            // and v.getX/Y() return 0.
            int[] location = new int[2];
            v.getLocationOnScreen(location);
            
            // Ok, now we know the centre location of the button in screen coordinates!
            informButtonLocation(location[0] + (right-left)/2, location[1] + (bottom-top)/2);
        }
    });
    
    it.setActionView(button);
    mAddListingButton = it.getActionView();
    return true;
}

然后你有一个函数可以用按钮的位置更新你的有用指令视图并重新绘制它:

private void informButtonLocation(int cx, int cy)
{
    MyHelpView v = (MyHelpView)findViewById(R.id.instructions);
    v.setText("Click this button.");
            v.setTarget(cx, cy);
}

最后制作你喜欢的 schmancy 自定义视图,该视图将箭头绘制到(靠近)按钮。在您的方法中,您可以使用相同的函数onDraw将屏幕坐标转换回坐标。像这样的东西:CanvasgetLocationOnScreen

@Override
public void onDraw(Canvas canvas)
{
    int[] location = new int[2];
    getLocationOnScreen(location);
    
    canvas.drawColor(Color.WHITE);

    mPaint.setColor(Color.BLACK);
    mPaint.setTextSize(40);
    mPaint.setAntiAlias(true);
    mPaint.setTextAlign(Align.CENTER);
    canvas.drawText(mText, getWidth()/2, getHeight()/2, mPaint);

    // Crappy line that isn't positioned right at all and has no arrowhead.
    if (mTargetX != -1 && mTargetY != -1)
        canvas.drawLine(getWidth()/2, getHeight()/2,
                mTargetX - location[0], mTargetY - location[1], mPaint);
}

(但显然您必须将目标位置剪辑到 Canvas 大小)。如果您采用与 Google 相同的方法,将视图覆盖在整个屏幕上,您就不必担心这一点,但它更具侵入性。Google 的方法绝对不使用任何私有 API(令人惊讶)。查看Cling.javaLauncher2 代码。

顺便说一句,如果您查看 Google 类似的启动器实现的源代码(Cling出于某种原因,他们将指令屏幕称为 a),您会发现它更加骇人听闻。它们基本上使用由正在使用的布局确定的绝对屏幕位置。

希望这对人们有所帮助!

于 2012-07-15T19:20:02.760 回答
11

只需将菜单项作为普通视图处理..像这样:

    View myActionView = findViewById(R.id.my_menu_item_id);
    if (myActionView != null) {
        int[] location = new int[2];
        myActionView.getLocationOnScreen(location);

        int x = location[0];
        int y = location[1];
        Log.d(TAG, "menu item location --> " + x + "," + y);
    }

注意: 我已经使用 ToolBar 配置了我的活动,而不是直接作为 ActionBar 进行了测试……但我想它应该可以类似地工作。

于 2015-05-13T13:49:58.683 回答
0

此答案现已过时,请在上面查找已接受的答案

我发现(此时)没有办法计算出菜单项的 x/y 值。看到菜单项也可以从屏幕上隐藏,直到用户按下菜单按钮。在 Android 3.0 及更高版本中,您可以选择在应用程序的ActionBar. 您可以在操作栏中添加很多菜单项,当没有更多位置可以在操作栏中放置项目时,它将简单地创建一个“溢出”按钮,该按钮将包含所有无法放置在操作栏中的菜单项,因为空间不足或因为程序员明确设置菜单项出现在溢出菜单中。

所有这些行为可能是菜单项没有您可以检索(或设置)的 x/y 位置的原因。

我最终解决这个问题的方法是使用该Paint.measureText()函数来确定操作栏中每个菜单项的宽度,并使用它来计算标签应该在屏幕右侧的偏移量。

这是一种解决方法,但如果有人和我有同样的问题,请知道这很好用。请记住在Paint对象上设置适当的字体大小,否则当您测量文本的长度时,它很可能会使用与菜单项的字体大小不同的字体大小来计算文本宽度。

于 2012-05-16T12:26:15.247 回答