Android Kotlin: How to create a menu with checkable menu items

Introduction

This code demonstrates creating an Android app (written in Kotlin) with a menu containing checkable menu items. These menu items allow users to change the text color and formatting (bold, italic, underline) of a TextView within the app.

Breakdown

The code consists of three main parts:

  1. MainActivity.kt: This file contains the main Activity class responsible for handling the UI and user interaction. It inflates the menu layout (options_menu.xml) and defines functionality for both creating the menu and handling user selections.

  2. activity_main.xml: This file defines the layout of the main activity screen. It includes a single MaterialTextView element that displays text and allows users to change its color and formatting.

  3. res/menu/options_menu.xml: This file defines the structure of the menu displayed in the app. It contains six menu items: three for changing text color (red, green, yellow) and three for text formatting (bold, italic, underline). The formatting menu items are defined as checkable, allowing users to toggle them on and off.

Summary

By combining these parts, the code creates a functional Android app where users can change the text color and formatting of a TextView using a menu with checkable options. The onCreateOptionsMenu method inflates the menu and dynamically sets the icon colors. The onOptionsItemSelected method handles user selections and updates the TextView's color and formatting accordingly.


MainActivity.kt

package com.example.jetpack

import android.annotation.SuppressLint
import android.graphics.*
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.menu.MenuBuilder
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {

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


    @SuppressLint("RestrictedApi")
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.options_menu,menu)

        // To display icon on overflow menu
        if (menu is MenuBuilder) {
            menu.setOptionalIconsVisible(true)
        }

        // change menu icon color programmatically
        menu?.apply {
            for(index in 0 until this.size()){
                val item = this.getItem(index)

                var color = Color.parseColor("#4F42B5")
                when (item.itemId) {
                    R.id.red -> {
                        color = Color.RED
                    }
                    R.id.green -> {
                        color = Color.GREEN
                    }
                    R.id.yellow -> {
                        color = Color.YELLOW
                    }
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    item.icon.colorFilter = BlendModeColorFilter(
                        color, BlendMode.SRC_IN
                    )
                }else{
                    item.icon.setColorFilter(color, PorterDuff.Mode.SRC_IN)
                }
            }
        }

        return super.onCreateOptionsMenu(menu)
    }


    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.red->{
                textView.setTextColor(Color.RED)
            }R.id.green->{
                textView.setTextColor(Color.GREEN)
            }R.id.yellow->{
                textView.setTextColor(Color.YELLOW)
            }

            R.id.bold->{
                item.isChecked = !item.isChecked
                if (item.isChecked){
                    if (textView.typeface.isItalic){
                        textView.setTypeface(textView.typeface,Typeface.BOLD_ITALIC)
                    }else{
                        textView.setTypeface(textView.typeface,Typeface.BOLD)
                    }
                }else{
                    if (textView.typeface.isItalic){
                        textView.setTypeface(textView.typeface,Typeface.ITALIC)
                    }else{
                        textView.setTypeface(textView.typeface,Typeface.NORMAL)
                    }
                }
            }

            R.id.italic->{
                item.isChecked = !item.isChecked
                if (item.isChecked){
                    if (textView.typeface.isBold){
                        textView.setTypeface(textView.typeface,Typeface.BOLD_ITALIC)
                    }else{
                        textView.setTypeface(textView.typeface,Typeface.ITALIC)
                    }
                }else{
                    if (textView.typeface.isBold){
                        textView.setTypeface(textView.typeface,Typeface.BOLD)
                    }else{
                        textView.setTypeface(textView.typeface,Typeface.NORMAL)
                    }
                }
            }

            R.id.underline->{
                item.isChecked = !item.isChecked
                if (item.isChecked){
                    textView.paintFlags = textView.paintFlags or
                            Paint.UNDERLINE_TEXT_FLAG
                }else{
                    textView.paintFlags = 0
                }
            }
        }

        return super.onOptionsItemSelected(item)
    }
}
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="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:background="#F6F6F2"
        android:padding="5dp"
        android:text="Kotlin Checkable Menu Item Example"
        android:typeface="monospace"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

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

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/red"
        android:title="Red"
        android:icon="@drawable/ic_color_lens"
        app:showAsAction="always"
        />
    <item
        android:id="@+id/green"
        android:title="Green"
        android:icon="@drawable/ic_color_lens"
        app:showAsAction="always"
        />
    <item
        android:id="@+id/yellow"
        android:title="Yellow"
        android:icon="@drawable/ic_color_lens"
        app:showAsAction="always"
        />
    <item
        android:id="@+id/bold"
        android:title="Bold"
        android:icon="@drawable/ic_format_bold"
        app:showAsAction="never"
        android:checkable="true"
        android:checked="false"
        />
    <item
        android:id="@+id/italic"
        android:title="Italic"
        android:icon="@drawable/ic_format_italic"
        app:showAsAction="never"
        android:checkable="true"
        />
    <item
        android:id="@+id/underline"
        android:title="Underline"
        android:icon="@drawable/ic_format_underlined"
        app:showAsAction="never"
        android:checkable="true"
        />
</menu>
More android kotlin tutorials