How can I set a long click listener on a MenuItem
?
I tried this answer, but the method doesn\'t exist for me. Any solutions?
Code:
<
You can do it this way:
val homeTab = bottomNavigationView.findViewById<BottomNavigationItemView>(R.id.navigation_home)
//R.id.navigation_home id of navigation menu items you provided in menu file for BottomNavigationView
homeTab.setOnLongClickListener {
Toast.makeText(this@MainActivity,"Home Long clicked!",Toast.LENGTH_LONG).show()
true
}
This isn't perfect at all, but still better than everyone is suggesting here, because it works on whole item layout, not only on actionview's part.
Notice:
index
is the position of an item, BUT it also contains position of: HeaderLayout, Divider, Category (Sub menu header)Well, here is the code:
val navigationView = findViewById(R.id.navigation_view)
val navigationRecycler = navigationView[0] as RecyclerView // core-ktx extension for ViewGroup
val navigationLayoutManager = navigationRecycler.layoutManager as LinearLayoutManager
val navigationAdapter = navigationRecycler.adapter
if (navigationAdapter != null) {
navigationRecycler.post { // this line is important
for (index in 0 until navigationAdapter.itemCount) {
val item = navigationLayoutManager.findViewByPosition(index)
item?.setOnLongClickListener {
Toast.makeText(this, "finally!", Toast.LENGTH_SHORT).show()
true
}
}
}
}
one of many ways (assuming we use Toolbar) - this example should give you the idea how to implement long click on toolbar button :
class MyActivity extends Activity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
/** get menu inflater */
MenuInflater menuInflater = getMenuInflater();
/** Inflate the menu
* this adds items to the action bar if it is present. */
menuInflater.inflate(R.menu.menu_home, menu);
/** find interesting item */
MenuItem item = menu.findItem(R.id.itemId);
/** set action view */
item.setActionView(new ImageButton(this)); // this is a Context.class object
/** set listener on action view */
item.getActionView().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
}
in method onCreateOptionsMenu - or any other method when you can get menu item reference (omit step 1-2):
above i set an action view then i get it back from menu item and set listener (the order is no matter it could be also in such way):
ImageButton imageButton = new ImageButton(Context);
imageButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
item.setActionView(imageButton);
ps. you can also set image view in xml as attribute on menu item:
<item
...
android:actionViewClass="android.widget.ImageButton"
/>
then you can get the action view by cast
View menuItemActionView = menu.findItem(R.id.itemId).getActionView();
if(menuItemActionView != null
&& ImageButton.class.isAssignableFrom(menuItemActionView.getCLass())) {
ImageButton imageButton = (ImageButton) menuItemActionView;
}
But then you set the long click listener to the action view only, not the whole item. – SuperThomasLab
-- no you are setting an action view on single element in this case you change a default view (to ImageButton widget) for an menu item - action view could be simple or complex view type
But what if u don't want to change the view, but keep the default view? – SuperThomasLab
example (this is one way of many by using a layout tree observer / by setting a layout change listener):
private View.OnLongClickListener onLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
/** get menu inflater */
MenuInflater menuInflater = getMenuInflater();
/** Inflate the menu
* this adds items to the action bar if it is present. */
menuInflater.inflate(R.menu.menu_home, menu);
/** geta menu item using findItem(int itemid) */
MenuItem item = menu.findItem(R.id.itemLogOut);
/** check if we have item */
if(item!=null) {
/** try get its action view */
View actionView = item.getActionView();
/** check if action view is already set? */
if(actionView==null) {
/** get item id to comparte later in observer listener*/
final int itemId = item.getItemId();
/** if not set on top most window an layout changes listener */
getWindow().getDecorView()
.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/** try get view by id we have stored few line up */
View viewById = v.getRootView().findViewById(itemId);
/** check if we have any result */
if(viewById!=null) {
/** set our listener */
viewById.setOnLongClickListener(onLongClickListener);
/** remove layout observer listener */
v.removeOnLayoutChangeListener(this);
}
}
});
} else {
/** if set we can add our on long click listener */
actionView.setOnLongClickListener(onLongClickListener);
}
}
}
I tried the OnLayoutChangeListener, but it still doesn't work Nothing changed. – SuperThomasLab
YES IT DOES - but i know the reason why it't is not working in your case ??? - in my example we check if view item is laid out instead of that change if menu view is laid out and then check if menu contain item
here is working code for you to study:
https://github.com/c3ph3us/LongClickOnMenuItem
in reply to other comments:
@ SuperThomasLab It's not obvious for me why setOnLongClickListener(...) method is not available for you. – Hossein Seifi
@HosseinSeifi - look at android.view.MenuItem interface - it doesn't provide a such method - so for good programmer this should be obvious :) why he can't reach implementing class method.
You can achieve by doing this:
action_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:support="http://schemas.android.com/apk/res-auto" >
<item
android:id="@+id/item1"
support:showAsAction="always">
</item>
custom_action_view.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:paddingRight="5dp" >
<ImageButton
android:id="@+id/customActionItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@drawable/abc_item_background_holo_dark"
android:src="@drawable/bulb_icon" />
</RelativeLayout>
and menu inflater code is as follow:
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.action_menu, menu);
final MenuItem item1= menu.findItem(R.id.item1);
MenuItemCompat.setActionView(item1, R.layout.custom_action_view);
View vItem1= MenuItemCompat.getActionView(item1);
final ImageButton customActionItem= (ImageButton) vItem1.findViewById(R.id.customActionItem);
customActionItem.setOnLongClickListener(new OnLongClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// do something here
}
});
return super.onCreateOptionsMenu(menu);
}
mBottomNavigationView.findViewById(menuItemId).setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return true;
}
});
From massivemadness answer here is a slight variation to target a menu. I also don't think this is stable, it feels like a bodge but nest it in a try catch, keep calm and move on :)
if(navigationAdapter != null) {
navigationRecycler.post { // this line is important
for(index in 0 until navigationAdapter.itemCount) {
val item = navigationLayoutManager.findViewByPosition(index)
item?.setOnLongClickListener {
try{
Toast.makeText(this, "${nav_view.menu[index - 1 /*Number of children before your menu*/].title}!", Toast.LENGTH_SHORT).show()
true
} catch (e:Throwable){
false
}
}
}
}
}