How can I get the current location of an ActionBar MenuItem?
android, android-layout
Solution
I finally found an answer to this! It is a bit hacky, but not too much. The only downsides are:
You have to replace the `MenuItem`s with your own `View`. An `ImageView` is fairly similar to the standard one but it misses features like long-pressing to see a tool-tip, and probably other things.
I haven't tested it a lot.
Ok, here's how we do it. Change your `Activity`'s `onCreateOptionsMenu()` to something like this:
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;
}
And then you have a function that updates your helpful instructions view with the location of the button and redraws it:
private void informButtonLocation(int cx, int cy)
{
MyHelpView v = (MyHelpView)findViewById(R.id.instructions);
v.setText("Click this button.");
v.setTarget(cx, cy);
}
Finally make your fancy schmancy custom view that draws an arrow to (near) the button. In your `onDraw` method you can convert the screen coordinates back to `Canvas` coordinates using the same `getLocationOnScreen` function. Something like this:
@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);
}
(But obviously you have to clip the target location to the Canvas size). If you go for the same method as Google where you overlay a view over the entire screen you won't have to worry about that, but it is more intrusive. Google's method definitely doesn't use any private APIs (amazingly). Check out `Cling.java` in the Launcher2 code.
Incidentally, if you look at the source for Google's similar implementation of this for the launcher (they call the instructions screen a `Cling` for some reason), you can see that it is even more hacky. They basically use absolute screen positions determined by which layout is being used.
Hope this helps people!
Problem
I want to show a little info label underneath a `MenuItem` in the app's `ActionBar`. To position this correctly I need to know the x-position of a `MenuItem`. Google brought me no useful results so far. Is it even possible?