We choose to do these things not because they are easy, but because they are hard. And because my UI would have come unglued if I couldn't do this.
Import the NavigationItemLongPressInterceptor
class given below into your project.
Menu items for the NavigationView are declared as normal, with two additional attributes to add long-press behaviour.
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:showIn="navigation_view">
<group android:id="@+id/home_views">
<item
android:id="@+id/nav_item_1"
android:icon="@drawable/ic_item_1"
android:title="Item 1"
android:checkable="true"
app:actionViewClass=
"com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor"
app:showAsAction="always"
/>
<item
android:id="@+id/nav_item_2"
android:icon="@drawable/ic_item_2"
android:title="Item 2"
android:checkable="true"
app:actionViewClass=
"com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor"
app:showAsAction="always"
/>
</group>
</menu>
Add an implementation for NavigationItemLongPressInterceptor.OnNavigationItemLongClickListener to your activity,
and implement the onNavigationItemLongClick
method:
public class MainActivity extends MediaActivity
implements
NavigationView.OnNavigationItemSelectedListener,
NavigationItemLongPressInterceptor.OnNavigationItemLongClickListener
. . .
@Override
public void onNavigationItemLongClick(
NavigationItemLongPressInterceptor.SelectedItem selectedItem,
View view)
{
// supply your NavigationView as an argument.
int menItemId = selectedItem.getItemId(mNavigationView);
switch (id) {
...
case R.id.nav_local_device:
case R.id.nav_upnp_devices: {
showNavigationItemSetAsHomePopupMenu(id,view);
}
break;
}
}
}
You probably have to add com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor
to your proguard rules.
NavigationItemLongPressInterceptor.java:
package com.twoplay.netplayer.controls;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.material.navigation.NavigationView;
import androidx.annotation.Nullable;
/**
* Attach a long-click handler to a menu item in a NavigationView menu.
*
* To handle long-click of a Navigator menu item, declare the item as normal, and append
* app:actionViewClass="com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor" and
* app:showAsAction="always" attributes:
*
* <menu xmlns:tools="http://schemas.android.com/tools"
* xmlns:app="http://schemas.android.com/apk/res-auto"
* >
*
* <group android:id="@+id/home_views">
* <item
* android:id="@+id/nav_item_1"
* android:icon="@drawable/ic_item_1"
* android:title="Item 1"
* android:checkable="true"
* app:actionViewClass=
* "com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor"
* app:showAsAction="always"
* </item>
* </group>
*
* Your Application class must implement <L NavigationItemLongPressInterceptor.OnNavigationItemLongClickListener/>
* in order to receive notification of long pressed menu items.
*
* You can retrieve the item id of the menu by calling <L SelectedItem.getItemId/> on the
* <L SelectedItem/> provided as an argument to <L NavigationItemLongPressInterceptor.onNavigationItemLongClick/>
*
* />
*
*/
@SuppressWarnings("unused")
public class NavigationItemLongPressInterceptor extends View {
public NavigationItemLongPressInterceptor(Context context) {
super(context);
init();
}
public NavigationItemLongPressInterceptor(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public NavigationItemLongPressInterceptor(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init()
{
setVisibility(View.INVISIBLE);
setLayoutParams(new ViewGroup.LayoutParams(0,0));
}
public interface OnNavigationItemLongClickListener {
void onNavigationItemLongClick(SelectedItem itemHandle, View view);
}
public static class SelectedItem {
private final View actionView;
private SelectedItem(View actionView) {
this.actionView = actionView;
}
public int getItemId(NavigationView navigationView)
{
return getItemId(navigationView.getMenu());
}
private int getItemId(Menu menu) {
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
if (item.getActionView() == actionView) {
return item.getItemId();
}
SubMenu subMenu = item.getSubMenu();
if (subMenu != null) {
int itemId = getItemId(subMenu);
if (itemId != -1) {
return itemId;
}
}
}
return -1;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
View parent = getMenuItemParent();
parent.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
OnNavigationItemLongClickListener receiver = getReceiver();
if (receiver == null)
{
throw new RuntimeException("Your main activity must implement NavigationViewLongPressInterceptorView.OnNavigationItemLongClickListener");
}
View parent = getMenuItemParent();
receiver.onNavigationItemLongClick(
new SelectedItem(NavigationItemLongPressInterceptor.this),parent);
return true;
}
});
}
private Activity getActivity() {
Context context = getContext();
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity)context;
}
context = ((ContextWrapper)context).getBaseContext();
}
return null;
}
private OnNavigationItemLongClickListener getReceiver() {
Activity activity = getActivity();
if (activity == null) return null;
if (activity instanceof OnNavigationItemLongClickListener)
{
return (OnNavigationItemLongClickListener)activity;
}
return null;
}
private View getMenuItemParent() {
View parent = (View)getParent();
while (true)
{
if (parent.isClickable())
{
return parent;
}
parent = (View) parent.getParent();
}
}
}
This code targets androidx, but it can be trivially backported to AppCompat. Just delete the androidx imports, and replace them with corresponding AppCompat imports. Hopefully older versions of NavigationView
lay out actionView
s the same way.
Tested with 'androidx.appcompat:appcompat:1.0.2', 'com.google.android.material:material:1.0.0'. I'm reasonably confident that it is version-safe. Let me know if getMenuItemParent()
needs adjustments for other AppCompat versions, and I will incorporate changes here.