Android Kotlin: How to create a popup menu with icons

This code demonstrates how to create a popup menu with icons in an Android application written in Kotlin. The code utilizes functionalities from the AppCompat library and offers solutions for different Android API levels.

Key functionalities explained:

  • The MainActivity.kt file handles the core logic. It displays a text view with a click listener that triggers the showPopupMenu() function.
  • The showPopupMenu() function inflates a popup menu from a resource file (popup_menu.xml). It sets a listener to handle user selections on the menu items.
  • There are two approaches for displaying icons on the menu items:
    • For API level 29 (Android Q) and above, a direct method (setForceShowIcon(true)) is available.
    • For lower API levels, reflection is used to access a hidden method and achieve the same result.

Summary

This code provides a well-structured and informative example for implementing a popup menu with icons in an Android Kotlin application. It showcases handling different API levels and offers flexibility for developers targeting various Android versions.


MainActivity.kt

package com.example.jetpack

import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.widget.PopupMenu
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import java.lang.reflect.Method


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // show sdk version in text view
        textView.append("\nAPI " + Build.VERSION.SDK_INT)

        // text view click listener
        textView.setOnClickListener {
            showPopupMenu()
        }
    }


    // method to show popup menu
    private fun showPopupMenu(){
        val popup = PopupMenu(this,textView)

        popup.apply {
            // inflate the popup menu
            menuInflater.inflate(R.menu.popup_menu,menu)

            // popup menu item click listener
            setOnMenuItemClickListener {
                when(it.itemId){
                    R.id.red->{
                        textView.setTextColor(Color.RED)
                    }R.id.green->{
                        textView.setTextColor(Color.GREEN)
                    }R.id.yellow->{
                        textView.setTextColor(Color.YELLOW)
                    }R.id.gray->{
                        textView.setTextColor(Color.GRAY)
                    }
                }

                false
            }
        }

        // show icons on popup menu
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            popup.setForceShowIcon(true)
        }else{
            try {
                val fields = popup.javaClass.declaredFields
                for (field in fields) {
                    if ("mPopup" == field.name) {
                        field.isAccessible = true
                        val menuPopupHelper = field[popup]
                        val classPopupHelper =
                            Class.forName(menuPopupHelper.javaClass.name)
                        val setForceIcons: Method = classPopupHelper.getMethod(
                            "setForceShowIcon",
                            Boolean::class.javaPrimitiveType
                        )
                        setForceIcons.invoke(menuPopupHelper, true)
                        break
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        // finally, show the popup menu
        popup.show()
    }
}
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.textview.MaterialTextView
        android:id="@+id/textView"
        style="@style/TextAppearance.MaterialComponents.Headline4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="11dp"
        android:text="Show Popup Menu"
        android:background="#F6F6F2"
        android:padding="5dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
res/menu/popup_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/red"
        android:title="Red"
        android:icon="@drawable/ic_camera"
        android:iconTint="#F44336"
        android:iconTintMode="src_in"
        />
    <item
        android:id="@+id/green"
        android:title="Green"
        android:icon="@drawable/ic_donut_large"
        android:iconTint="#4CAF50"
        android:iconTintMode="src_in"
        />
    <item
        android:id="@+id/yellow"
        android:title="Yellow"
        android:icon="@drawable/ic_dock"
        android:iconTint="#FFEB3B"
        android:iconTintMode="src_in"
        />
    <item
        android:id="@+id/gray"
        android:title="Gray"
        android:icon="@drawable/ic_add_circle"
        android:iconTint="#707070"
        android:iconTintMode="src_in"
        />
</menu>
More android kotlin tutorials