I'd like to increase the height of a single MenuItem
inside a NavigationView
.
I am trying to put a Google MapFragment
inside the item's actionLayout
, as shown below, but I want the height of the map to be larger.
Any ideas?
activity_main_drawer.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:showIn="navigation_view">
...
<item
android:id="@+id/nav_map"
app:showAsAction="always"
android:title="@string/nav_map"
app:actionLayout="@layout/fragment_map" />
...
</menu>
fragment_map.xml
Adjusting android:layout_height
here has no effect.
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/nav_map_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:mapType="normal"
map:liteMode="true"/>
Activity.onCreate(...)
in MainActivity.kt
Setting the height on the action view has no effect here.
val mapItem = nav.menu.findItem(R.id.nav_map)
// Has no effect: trying to set height to 128dp
mapItem.actionView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 128)
// Hack: hide title
mapItem.title = null
val mapView = supportFragmentManager.findFragmentById(R.id.nav_map_fragment) as SupportMapFragment
mapView.getMapAsync { map ->
...
}
I ended up solving this problem by creating an extension function that will do two things:
MenuItem
(i.e. title = null
)MenuItem
The state change listener has the purpose of, when the item is attached to the window, finding the parent of the action view and setting it's height to WRAP_CONTENT
(and storing the original height). When the view is detached, it restores the original height. This is because it seems like the views are actually recycled between different items as you scroll... so you'd otherwise find that gradually all of the items in the menu are affected, not just your target item.
The extension function is implemented below:
fun MenuItem.fillActionView() {
// Hide the title text
title = null
// Fill the action view
val thisView = actionView
thisView.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener {
private var originalHeight: Int? = null
override fun onViewAttachedToWindow(view: View?) {
if (view === thisView) {
val v = view?.parent?.parent as? View
if (v != null) {
v.layoutParams = v.layoutParams
.apply {
originalHeight = height
height = ViewGroup.LayoutParams.WRAP_CONTENT
}
}
}
}
override fun onViewDetachedFromWindow(view: View?) {
val h = originalHeight
val v = view?.parent?.parent as? View
if ((h != null) && (v != null)) {
v.layoutParams = v.layoutParams
.apply {
height = h
originalHeight = null
}
}
}
})
}
You can use it in your activity's onCreate()
like so:
val mapItem = nav.menu.findItem(R.id.nav_map) // Find your menu item which has the action view set
mapItem.fillActionView()
Java code:
View.OnAttachStateChangeListener menuItemActionViewStateListener = new View.OnAttachStateChangeListener() {
int originalHeight = 0;
@Override
public void onViewAttachedToWindow(View v) {
View parent = (View) v.getParent();
if (parent != null)
parent = (View)parent.getParent();
if (parent != null) {
ViewGroup.LayoutParams p = parent.getLayoutParams();
originalHeight = p.height;
p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
parent.requestLayout();
}
}
@Override
public void onViewDetachedFromWindow(View v) {
if (originalHeight != 0) {
View parent = (View) v.getParent();
if (parent != null)
parent = (View)parent.getParent();
if (parent != null) {
ViewGroup.LayoutParams p = parent.getLayoutParams();
p.height = originalHeight;
}
}
}
};
And just use it like here:
final View v = inflater.inflate(R.layout.main_menu_item, null);
v.addOnAttachStateChangeListener(menuItemActionViewStateListener);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menuItem.setActionView(v);
menuItem.setIcon(null);
menuItem.setTitle(null);
I hope this is helpful to others.