In Android, lists tend to have actions associated with them. We can do this via onClick(View v) or event the outdated long press action, but what if we want to give an item in our list a menu style drop-down? Well, this is actually pretty easy to do with the PopupMenu class from Android. So how do we use it?
Since I have received a lot of traffic on my StackOverflow post about just this thing, I have decided to share a quick tutorial on how to add them to your adapter.
Piggybacking off the same repository from our ViewModel and LiveData demo we can easily add a drop-down menu via the PopupMenu class.
First, let’s add the following strings to our strings.xml file:
1 2 3 4 5 |
<resources> <string name="options_save">Save</string> <string name="options_share">Share</string> <string name="options_cancel">Cancel</string> </resources> |
Then let’s create a menu using these values as our titles:
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_save" android:title="@string/options_save"/> <item android:id="@+id/action_share" android:title="@string/options_share"/> <item android:id="@+id/action_cancel" android:title="@string/options_cancel"/> </menu> |
Next, let’s add a new overflow image button using vector assets:
1 2 3 4 5 6 7 8 9 10 11 12 |
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:pathData="M0 0h24v24H0z" /> <path android:fillColor="#000000" android:pathData="M12 8c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2zm0 2c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm0 6c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z" /> </vector> |
We are almost done, now we just need to add an AppCompatImageButton to our layout:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="16dp" android:paddingTop="20dp" android:paddingRight="16dp" android:paddingBottom="20dp"> <android.support.v7.widget.AppCompatImageView android:id="@+id/imageViewPlaceIcon" android:layout_width="56dp" android:layout_height="56dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" tools:src="@tools:sample/avatars" /> <android.support.v7.widget.AppCompatTextView android:id="@+id/textViewPlaceName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" app:layout_constraintHorizontal_bias="0" app:layout_constraintLeft_toRightOf="@+id/imageViewPlaceIcon" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="@tools:sample/full_names" /> <android.support.v7.widget.AppCompatRatingBar android:id="@+id/ratingBarPlace" style="@style/Widget.AppCompat.RatingBar.Small" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" app:layout_constraintHorizontal_bias="0" app:layout_constraintLeft_toRightOf="@+id/imageViewPlaceIcon" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/textViewPlaceName" tools:rating="3" /> <android.support.v7.widget.AppCompatTextView android:id="@+id/textViewPriceLevel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" app:layout_constraintHorizontal_bias="0" app:layout_constraintLeft_toRightOf="@+id/imageViewPlaceIcon" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/ratingBarPlace" tools:text="$$$$" /> <android.support.v7.widget.AppCompatImageButton android:id="@+id/btnMoreOptions" android:layout_width="48dp" android:layout_height="48dp" android:layout_marginStart="8dp" android:layout_marginLeft="8dp" android:background="?android:attr/selectableItemBackground" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toRightOf="@+id/ratingBarPlace" app:layout_constraintHorizontal_bias="1" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_more" /> </android.support.constraint.ConstraintLayout> |
Now let’s hook it all up to our adapter code in our onBindView(holder: ViewHolder, position: Int) method!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val place = placeList[position] Picasso.get().load(place.icon).resize(56, 56).into(holder.placeIcon) holder.placeName.text = place.name holder.placeRating.rating = place.rating holder.placePriceLevel.text = priceArray[place.priceLevel] holder.itemView.setOnClickListener { listener.onClick(place) } // add a listener to the button which displays the PopupMenu holder.placeOptionsButton.setOnClickListener { val popupMenu = PopupMenu(context, holder.placeOptionsButton, Gravity.START) popupMenu.menuInflater.inflate(R.menu.menu_place_options, popupMenu.menu) popupMenu.setOnMenuItemClickListener { item: MenuItem -> when (item.itemId) { R.id.action_save -> { Toast.makeText(context, context.getString(R.string.options_save), Toast.LENGTH_SHORT).show() true } R.id.action_share -> { Toast.makeText(context, context.getString(R.string.options_share), Toast.LENGTH_SHORT).show() true } R.id.action_cancel -> { Toast.makeText(context, context.getString(R.string.options_cancel), Toast.LENGTH_SHORT).show() popupMenu.dismiss() true } else -> { false } } } popupMenu.show() } } |
And there you have it! Your list items will now have a PopupMenu where you can react to the list items overflow button.
Please leave a comment or share if you enjoyed the tutorial.
The full source code is available on Github.