Hướng dẫn sử dụng Android OptionMenu

Xem thêm các chuyên mục:

1- Android Option Menu

Trong Android một Option Menu là một tập hợp của các tùy chọn (option) chính cho một ứng dụng, người dùng có thể lựa chọn một trong các tùy chọn để thực hiện một hành động. Option Menu xuất hiện trên App Bar (Thanh ứng dụng), phía bên phải.
Ví dụ, dưới đây là một đoạn mã XML để tạo ra một Option Menu đơn giản.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:title="Menu Item 1" />
    <item android:title="Menu Item 2" >
        <menu >
            <item android:title="Menu Item 2.1" />
            <item android:title="Menu Item 2.2" />
        </menu>
    </item>
    <item android:title="Menu Item 3" />
</menu>
Người dùng cần nhấn vào biểu tượng  để hiển thị Option Menu.
Một Menu có thể chứa một hoặc nhiều MenuItem và các Sub Menu (Menu con).
Trước Android 3.0 bạn dễ dàng sét đặt các biểu tượng cho các Menu Item. Nhưng sau đó tư duy thiết kế của Google thay đổi, họ muốn các nhà phát triển đặt tất cả các chức năng trên App Bar thay vì Menu, và họ không còn cho phép các biểu tượng hiển thị trên các Menu Item của Overflow Menu (Menu tràn). Mặc dù vậy vẫn có một vài ngoại lệ, chẳng hạn các thiết bị Android liên quan tới Samsung vẫn hỗ trợ hiển thị biểu tượng trên các Overflow Menu.
activity_main_menu.xml (2)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:icon="@drawable/icon_open"
        android:title="Open"
        app:showAsAction="collapseActionView" />
    <item
        android:icon="@drawable/icon_share"
        android:title="Share">
        <menu>
            <item android:title="Facebook" />
            <item android:title="Instagram" />
        </menu>
    </item>
    <item
        android:icon="@drawable/icon_delete"
        android:title="Delete" />

    <item
        android:id="@+id/app_bar_search"
        android:icon="@drawable/icon_search"
        android:title="Search"
        app:actionViewClass="android.widget.SearchView" />

    <item
        android:icon="@drawable/icon_settings"
        android:title="Settings" />
</menu>
Theo một chia sẻ trên StackOverflow, bạn vẫn có cách "xảo trá" để làm cho các biểu tượng hiển thị trên Menu Item của Overflow Menu, nhưng không đảm bảo rằng nó sẽ hoạt động trong các phiên bản sau này.
ActivityMain.java
package org.o7planning.optionmenututorial;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @SuppressLint("RestrictedApi")
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.activity_main_menu, menu);

        // If you want Icon display in Overflow Menu.
        // https://stackoverflow.com/questions/19750635/icon-in-menu-not-showing-in-android
        if(menu instanceof MenuBuilder){
            MenuBuilder m = (MenuBuilder) menu;
            m.setOptionalIconsVisible(true);
        }
        return true;
    }
}

app:showAsAction="always | ifRoom"

Tư duy mới của Google là các Menu Item thông dụng của một Option Menu nên được hiển thị trên App Bar, nhưng người dùng sẽ chỉ nhìn thấy biểu tượng (icon) của nó, họ cho rằng điều này sẽ làm nâng cao trải nghiệm của người dùng đối với ứng dụng.
activity_main_menu.xml (3)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:icon="@drawable/icon_open"
        android:title="Open"
        app:showAsAction="collapseActionView" />
    <item
        android:icon="@drawable/icon_share"
        android:title="Share"
        app:showAsAction="ifRoom">
        <menu>
            <item android:title="Facebook" />
            <item android:title="Instagram" />
        </menu>
    </item>
    <item
        android:icon="@drawable/icon_delete"
        android:title="Delete" />

    <item
        android:id="@+id/app_bar_search"
        android:icon="@drawable/icon_search"
        android:title="Search"
        app:actionViewClass="android.widget.SearchView"
        app:showAsAction="always" />

    <item
        android:icon="@drawable/icon_settings"
        android:title="Settings"
        app:showAsAction="always" />
</menu>
Menu Item với thuộc tính (attribute) app:showAsAction="always" sẽ luôn luôn hiển thị trên App Bar, và bạn sẽ không nhìn thấy nó trên Overflow Menu.
<item
    android:icon="@drawable/icon_settings"
    android:title="Settings"
    app:showAsAction="always" />
Menu Item với thuộc tính (attribute) app:showAsAction="ifRoom" sẽ hiển thị trên App Bar nếu còn đủ không gian cho nó. Và khi nó xuất hiện trên App Bar bạn sẽ không nhìn thấy nó trên Overflow Menu.
Không khó hiểu khi các chức năng  Search, Settings thường được nhìn thấy trên App Bar, bởi vì chúng là các chức năng quan trọng của ứng dụng.

2- Ví dụ Option Menu

Xem trước ví dụ:
Trên Android Studio tạo mới một project:
  • Name: OptionMenuExample
  • Package name: org.o7planning.optionmenuexample
Copy một vài icon vào thư mục drawable của project:
icon_open.png icon_delete.png icon_settings.png icon_search.png icon_share.png
Trên Android Studio chọn:
  • File > New > Android Resource File
  • File name: activity_main_menu.xml
  • Resource Type: Menu
Trên Android Studio bạn có thể thiết kế Menu một cách trực quan.
Sét đặt ID, Title, Icon cho các Menu Item:
Sét đặt 2 chức năng "Search""Settings" sẽ luôn hiển thị trên App Bar, và chức năng "Share" sẽ hiển thị trên App Bar nếu có đủ không gian trống.
  • Search: showAsAction="always"
  • Settings: showAsAction="always"
  • Share:  showAsAction="ifRoom"
activity_main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/menuItem_open"
        android:icon="@drawable/icon_open"
        android:title="Open" />
    <item
        android:id="@+id/menuItem_share"
        android:icon="@drawable/icon_share"
        android:title="Share"
        app:showAsAction="ifRoom">
        <menu>
            <item
                android:id="@+id/menuItem_facebook"
                android:title="Facebook" />
            <item
                android:id="@+id/menuItem_instagram"
                android:title="Instagram" />
        </menu>
    </item>
    <item
        android:id="@+id/menuItem_delete"
        android:icon="@drawable/icon_delete"
        android:title="Delete" />
    <item
        android:id="@+id/menuItem_search"
        android:icon="@drawable/icon_search"
        android:title="Search"
        app:actionViewClass="android.widget.SearchView"
        app:showAsAction="always" />
    <item
        android:id="@+id/menuItem_settings"
        android:icon="@drawable/icon_settings"
        android:title="Settings"
        app:showAsAction="always" />
</menu>
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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package org.o7planning.optionmenuexample;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;

import android.annotation.SuppressLint;
import android.app.SearchManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.SearchView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final String LOG_TAG= "OptionMenuExample";
    private SearchView searchView;

    private String lastQuery;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    @SuppressLint("RestrictedApi")
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.activity_main_menu, menu);

        // If you want Icon display in Overflow Menu.
        // https://stackoverflow.com/questions/19750635/icon-in-menu-not-showing-in-android
        if (menu instanceof MenuBuilder) {
            MenuBuilder m = (MenuBuilder) menu;
            m.setOptionalIconsVisible(true);
        }

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        this.searchView = (SearchView) menu.findItem(R.id.menuItem_search).getActionView();

        this.searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
         // Need click "search" icon to expand SearchView.
        this.searchView.setIconifiedByDefault(true);


        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

            // Typing search text.
            public boolean onQueryTextChange(String newText) {
                // This is your adapter that will be filtered
                Log.i(LOG_TAG, "onQueryTextChange: " + newText);
                return true;
            }

            // Press Enter to search (Or something to search).
            public boolean onQueryTextSubmit(String query) {
                // IMPORTANT!
                // Prevent onQueryTextSubmit() method called twice.
                // https://stackoverflow.com/questions/34207670
                searchView.clearFocus();

                Log.i(LOG_TAG, "onQueryTextSubmit: " + query);
                return doSearch(query);
            }
        });

        searchView.setOnSearchClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(LOG_TAG, "SearchView.onSearchClickListener!" );
            }
        }) ;

        return super.onCreateOptionsMenu(menu);
    }


    private boolean doSearch(String query) {
        if (query == null || query.isEmpty()) {
            return false; // Cancel search.
        }
        this.lastQuery = query;

        Toast.makeText(this, "Search: " + query, Toast.LENGTH_LONG).show();
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menuItem_open:
                Toast.makeText(this, "Open ...", Toast.LENGTH_LONG).show();
                return true;
            case R.id.menuItem_delete:
                Toast.makeText(this, "Delete ...", Toast.LENGTH_LONG).show();
                return true;
            case R.id.menuItem_facebook:
                Toast.makeText(this, "Facebook Share ...", Toast.LENGTH_LONG).show();
                return true;
            case R.id.menuItem_instagram:
                Toast.makeText(this, "Instagram Share ...", Toast.LENGTH_LONG).show();
                return true;
            case R.id.menuItem_settings:
                Toast.makeText(this, "Settings ...", Toast.LENGTH_LONG).show();
                return true;
            case R.id.menuItem_search:
                Log.i(LOG_TAG, "onOptionsItemSelected (R.id.menuItem_search)");
                Toast.makeText(this, "Search ...", Toast.LENGTH_LONG).show();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

Xem thêm các chuyên mục: