Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set a long click listener on a MenuItem (on a NavigationView)?

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:

Menu menu = navigationView.getMenu();
MenuItem menuItem = menu.findItem(R.id.menu_item);

// TODO set a long click listener on the menuItem.
menuItem.setOnLongClickListener(...); // Method does not exist, any other solutions?

Edit: I don't want to set a custom ActionView, I want the long click listener to the whole MenuItem, without a custom View.

like image 489
Thomas Vos Avatar asked Jun 18 '16 16:06

Thomas Vos


3 Answers

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):

  1. create menu / for example by inflate
  2. get menu item
  3. create action view for example ImageButton
  4. set long click listener on action view
  5. set on menu item an action view

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.

like image 159
ceph3us Avatar answered Nov 01 '22 04:11

ceph3us


Solution

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:

  1. I tested it only in MaterialComponents library 1.1.0-alpha08, so I don't guarantee that it will work in the future
  2. I already asked devs to provide better API for this, but I don't know if they gonna make it or not
  3. 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
            }
        }
    }
}
like image 26
massivemadness Avatar answered Nov 01 '22 04:11

massivemadness


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
    }
like image 1
Edalat Feizi Avatar answered Nov 01 '22 04:11

Edalat Feizi