Reputation: 1051
I am developing a tutorial for an application and I need to point at a particular icon in the toolbar.
Here is an extract of the XML for the action menu:
<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" />
...
Here is my code:
@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];
}
}
}
With View mErase = findViewById(R.id.AbErase);
mErase
is set to null
, **** start EDIT **** which is not suprising as AbErase is the id of a MenuItem, not the id of a View. **** end EDIT ****
With View mErase = overflowMenu.getItem(0).getActionView();
location
is set to (0, 24), which is wrong as there already is a logo icon and a title in the toolbar.
How can I get the absolute X coordinate of the AbErase
view in the toolbar?
**** EDIT ****
here is the code where the initialisation of the static variable overflowMenu
can be found:
@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);
}
Upvotes: 0
Views: 1752
Reputation: 1739
Try the following code snippet
@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;
}
assuming android.support.v7.widget has been used. The above method would return a View corresponding to you menu item.
View menuItemView = getMenuItemView(scToolbar,R.id.AbErase);
int x = menuItemView.getX();
int y = menuItemView.getY();
and you have your coordinates.
after a bit more research I found out reflection isn't required.
Before fetching the menuItemView
we must wait for the Toolbar
to finish its layout pass
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
}
}
});
the above snippet is tested to work, it may be placed in any convenient lifecycle callback method.
On looking at the source for the activity the key things that must be remembered is that View menuItem = findViewById(R.id.AbErase);
should only be called from a ViewTreeObserver.OnGlobalLayoutListener
, onGlobalLayout
callback.
Upvotes: 4
Reputation: 711
In onCreate
method is too early. The view is not measured yet. Override onPrepareOptionMenu()
and put your code in it. Also remove your Runnable, its unnecessary.
Upvotes: 0