Thêm thực đơn

Thử cách Compose
Jetpack Compose là bộ công cụ giao diện người dùng được đề xuất cho Android. Tìm hiểu cách thêm thành phần trong Compose.

Trình đơn là thành phần giao diện người dùng phổ biến trong nhiều loại ứng dụng. Người nhận mang lại trải nghiệm người dùng quen thuộc và nhất quán, hãy sử dụng Menu API đến hiển thị hành động của người dùng và các tuỳ chọn khác trong hoạt động của bạn.

Hình ảnh cho thấy ví dụ về trình đơn mục bổ sung
Hình 1. Một trình đơn được kích hoạt bằng cách nhấn vào biểu tượng xuất hiện bên dưới biểu tượng trình đơn mục bổ sung.

Tài liệu này trình bày cách tạo 3 loại trình đơn hoặc bản trình bày hành động trên mọi phiên bản Android:

Trình đơn tùy chọn và thanh ứng dụng
Trình đơn tuỳ chọn là tập hợp chính gồm các mục trong trình đơn cho của bạn. Đây là nơi bạn đặt ra các hành động có tác động chung đến ứng dụng, chẳng hạn như "Tìm kiếm", "Soạn email", và "Cài đặt".

Xem trình đơn Tạo tuỳ chọn .

Trình đơn theo bối cảnh và chế độ thao tác theo bối cảnh
Trình đơn theo bối cảnh là một trình đơn nổi xuất hiện khi người dùng thực hiện thao tác chạm và giữ một phần tử. Nó cung cấp các thao tác ảnh hưởng đến nội dung hoặc khung ngữ cảnh đã chọn.

Chế độ thao tác theo ngữ cảnh hiển thị các mục hành động ảnh hưởng đến nội dung đã chọn trên thanh ở đầu màn hình và cho phép người dùng chọn nhiều mục.

Hãy xem phần Tạo trình đơn theo ngữ cảnh .

Trình đơn bật lên
Trình đơn bật lên hiển thị danh sách các mục theo chiều dọc được liên kết với khung hiển thị gọi trình đơn. Thật hữu ích khi cung cấp nhiều thao tác liên quan đến nội dung cụ thể hoặc để cung cấp các tuỳ chọn cho phần thứ hai của một lệnh. Các thao tác trong trình đơn bật lên không ảnh hưởng trực tiếp đến nội dung tương ứng — đó chính là mục đích của các thao tác theo ngữ cảnh. Thay vào đó, trình đơn bật lên dành cho các thao tác mở rộng có liên quan đến các vùng nội dung trong hoạt động của bạn.

Xem phần Tạo trình đơn bật lên.

Xác định trình đơn trong XML

Đối với mọi loại trình đơn, Android cung cấp một định dạng XML tiêu chuẩn để xác định trình đơn mục. Thay vì tạo trình đơn trong mã của hoạt động, hãy xác định một trình đơn và tất cả các mục trong tệp XML tài nguyên trình đơn. Bạn có thể rồi tăng cường tài nguyên trình đơn – tải nó dưới dạng Menu đối tượng – trong hoạt động hoặc mảnh của bạn.

Bạn nên sử dụng tài nguyên trình đơn vì những lý do sau:

  • Dễ dàng hình ảnh hóa cấu trúc trình đơn ở định dạng XML hơn.
  • Lớp này tách nội dung của trình đơn khỏi hành vi của ứng dụng .
  • Trình đơn này cho phép bạn tạo các cấu hình trình đơn thay thế cho nền tảng khác nhau phiên bản, kích thước màn hình và các cấu hình khác bằng cách tận dụng tài nguyên ứng dụng khung.

Để xác định trình đơn, hãy tạo một tệp XML bên trong Thư mục res/menu/ và xây dựng trình đơn bằng các thành phần sau phần tử:

<menu>
Xác định Menu, là một vùng chứa các mục trong trình đơn. Đáp Phần tử <menu> phải là nút gốc của tệp và nó có thể lưu một hoặc nhiều <item><group> phần tử.
<item>
Tạo một MenuItem, đại diện cho một mục trong trình đơn. Phần tử này có thể chứa một phần tử lồng nhau Phần tử <menu> để tạo trình đơn phụ.
<group>
Một vùng chứa không bắt buộc, ẩn dành cho <item> phần tử. Tính năng này cho phép bạn phân loại các mục trong trình đơn để chúng dùng chung thuộc tính, chẳng hạn như làm trạng thái hoạt động và chế độ hiển thị. Để biết thêm thông tin, hãy xem Mục Tạo một nhóm thực đơn.

Trình đơn mẫu có tên là game_menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/new_game"
          android:icon="@drawable/ic_new_game"
          android:title="@string/new_game"
          app:showAsAction="ifRoom"/>
    <item android:id="@+id/help"
          android:icon="@drawable/ic_help"
          android:title="@string/help" />
</menu>

Phần tử <item> hỗ trợ một số thuộc tính mà bạn có thể dùng để xác định giao diện và hành vi của một mục. Các mục trong trình đơn trước bao gồm các thuộc tính sau:

android:id
Mã tài nguyên dành riêng cho mục, cho phép ứng dụng nhận ra mặt hàng khi người dùng chọn.
android:icon
Tham chiếu đến đối tượng có thể vẽ để sử dụng làm biểu tượng của mục.
android:title
Tham chiếu đến một chuỗi dùng làm tiêu đề mục.
android:showAsAction
Thông số kỹ thuật về thời điểm và cách thức mục này xuất hiện dưới dạng mục hành động trong thanh ứng dụng.

Đây là những thuộc tính quan trọng nhất mà bạn sử dụng, nhưng vẫn còn nhiều thuộc tính khác sẵn có. Để biết thông tin về tất cả các thuộc tính được hỗ trợ, hãy xem Tài nguyên trình đơn tài liệu.

Bạn có thể thêm trình đơn phụ vào một mục trong bất kỳ trình đơn nào bằng cách thêm Phần tử <menu> làm phần tử con của <item>. Trình đơn phụ rất hữu ích khi ứng dụng có thể sắp xếp được nhiều chức năng vào chủ đề, như các mục trong thanh trình đơn của ứng dụng trên máy tính—chẳng hạn như Tệp, Chỉnh sửaXem. Hãy xem ví dụ sau:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/file"
          android:title="@string/file" >
        <!-- "file" submenu -->
        <menu>
            <item android:id="@+id/create_new"
                  android:title="@string/create_new" />
            <item android:id="@+id/open"
                  android:title="@string/open" />
        </menu>
    </item>
</menu>

Để sử dụng trình đơn trong hoạt động của bạn, hãy _inflate_ tài nguyên trình đơn, chuyển đổi tài nguyên XML vào một đối tượng có thể lập trình được bằng cách sử dụng MenuInflater.inflate(). Các phần sau đây cho biết cách mở rộng trình đơn cho từng loại trình đơn.

Tạo trình đơn tuỳ chọn

Trình đơn tuỳ chọn (như trình đơn minh hoạ trong hình 1) là nơi bạn bao gồm các hành động và tuỳ chọn khác có liên quan đến bối cảnh hoạt động hiện tại, chẳng hạn như "Tìm kiếm", "Soạn email", và "Cài đặt".

Hình ảnh minh hoạ thanh ứng dụng của ứng dụng Google Trang tính
Hình 2. Ứng dụng Google Trang tính, đang hiển thị một số nút, bao gồm cả nút mục bổ sung hành động.

Bạn có thể khai báo các mục cho trình đơn tuỳ chọn từ Activity lớp con hoặc một Fragment lớp con. Nếu cả hoạt động và mảnh đều khai báo các mục cho trình đơn tuỳ chọn, các mục sẽ được kết hợp trong giao diện người dùng. Các mục của hoạt động xuất hiện đầu tiên, sau đó là các phân đoạn của từng mảnh, theo thứ tự các phân đoạn sẽ được thêm vào hoạt động. Nếu cần, bạn có thể sắp xếp lại các mục trong trình đơn với thuộc tính android:orderInCategory trong mỗi <item> bạn cần di chuyển.

Để chỉ định trình đơn tuỳ chọn cho một hoạt động, hãy ghi đè onCreateOptionsMenu(). Các mảnh tự cung cấp dữ liệu onCreateOptionsMenu() . Trong phương pháp này, bạn có thể tăng cường tài nguyên trình đơn, được xác định trong XML, vào Menu được cung cấp trong . Lệnh này được minh hoạ trong ví dụ sau:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater: MenuInflater = menuInflater
    inflater.inflate(R.menu.game_menu, menu)
    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.game_menu, menu);
    return true;
}

Bạn cũng có thể thêm các món trong thực đơn bằng cách sử dụng add() và truy xuất các mục bằng findItem() để sửa đổi thuộc tính bằng API MenuItem.

Xử lý các sự kiện nhấp chuột

Khi người dùng chọn một mục trong trình đơn tuỳ chọn, bao gồm cả các mục hành động trong thanh ứng dụng, hệ thống sẽ gọi onOptionsItemSelected() . Phương thức này truyền tham số MenuItem đã chọn. Bạn có thể xác định mặt hàng bằng cách gọi getItemId(), Hàm này trả về mã nhận dạng duy nhất cho mục trong trình đơn, được xác định bởi Thuộc tính android:id trong tài nguyên trình đơn hoặc với một số nguyên đã cho cho phương thức add(). Bạn có thể so khớp mã này với trình đơn đã biết để thực hiện hành động thích hợp.

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // Handle item selection.
    return when (item.itemId) {
        R.id.new_game -> {
            newGame()
            true
        }
        R.id.help -> {
            showHelp()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection.
    switch (item.getItemId()) {
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

Khi xử lý thành công một mục trong trình đơn, hãy trả lại true. Nếu bạn không xử lý mục trong trình đơn, hãy gọi phương thức triển khai lớp cấp cao onOptionsItemSelected(). Phương thức triển khai mặc định trả về kết quả false.

Nếu hoạt động của bạn bao gồm các mảnh, thì trước tiên, hệ thống sẽ gọi onOptionsItemSelected() cho hoạt động, sau đó cho mỗi mảnh theo thứ tự thêm các mảnh, cho đến khi một giá trị trả về true hoặc tất cả các mảnh đều được gọi.

Thay đổi các mục trong trình đơn trong thời gian chạy

Sau khi gọi onCreateOptionsMenu(), hệ thống sẽ giữ lại thực thể của Menu mà bạn điền và không gọi onCreateOptionsMenu() một lần nữa trừ phi trình đơn không hợp lệ. Tuy nhiên, chỉ sử dụng onCreateOptionsMenu() để tạo trình đơn ban đầu trạng thái và không được thực hiện thay đổi trong vòng đời hoạt động.

Nếu bạn muốn sửa đổi trình đơn tuỳ chọn dựa trên các sự kiện xảy ra trong vòng đời hoạt động, bạn có thể thực hiện việc này trong onPrepareOptionsMenu() . Phương thức này truyền cho bạn đối tượng Menu vì đối tượng này hiện tại tồn tại để bạn có thể sửa đổi, chẳng hạn như bằng cách thêm, xoá hoặc vô hiệu hoá các mục. Các mảnh cũng cung cấp onPrepareOptionsMenu() .

Trình đơn tuỳ chọn được coi là luôn mở khi các mục trong trình đơn được trình bày thanh ứng dụng. Khi một sự kiện xảy ra và bạn muốn cập nhật trình đơn, hãy gọi invalidateOptionsMenu() để yêu cầu hệ thống gọi onPrepareOptionsMenu().

Tạo trình đơn theo bối cảnh

Hình ảnh hiển thị trình đơn theo bối cảnh nổi
Hình 3. Trình đơn theo bối cảnh nổi.

Trình đơn theo bối cảnh cung cấp các thao tác ảnh hưởng đến một mục hoặc ngữ cảnh cụ thể trong giao diện người dùng. Bạn có thể cung cấp trình đơn theo bối cảnh cho mọi chế độ xem, nhưng đó là thường được dùng cho các mục trong RecylerView hoặc các tập hợp khung hiển thị khác mà trong đó người dùng có thể thực hiện thao tác trực tiếp trên mỗi mục.

Có hai cách cung cấp thao tác theo ngữ cảnh:

  • Trong trình đơn ngữ cảnh nổi. Một trình đơn sẽ xuất hiện dưới dạng danh sách nổi các mục trong trình đơn, tương tự như một hộp thoại, khi người dùng thực hiện thao tác chạm và giữ một khung hiển thị khai báo hỗ trợ cho một ngữ cảnh . Người dùng có thể thực hiện thao tác theo ngữ cảnh trên một mục tại một thời điểm.
  • chế độ thao tác theo ngữ cảnh. Chế độ này là một hệ thống việc triển khai ActionMode hiển thị thanh thao tác theo ngữ cảnh (CAB) ở đầu màn hình có các mục hành động ảnh hưởng đến(các) mục đã chọn. Khi chế độ này đang hoạt động, người dùng có thể thực hiện hành động trên nhiều mục cùng một lúc, nếu ứng dụng hỗ trợ điều đó.

Lưu ý: Trình đơn theo bối cảnh không hỗ trợ phím tắt mục và biểu tượng mục.

Tạo trình đơn theo bối cảnh nổi

Để cung cấp trình đơn theo bối cảnh nổi, hãy làm như sau:

  1. Đăng ký View mà trình đơn theo bối cảnh được liên kết bằng đang gọi registerForContextMenu() và truyền vào đó View.

    Nếu hoạt động của bạn sử dụng RecyclerView và bạn muốn mỗi để cung cấp cùng một trình đơn theo bối cảnh, hãy đăng ký tất cả các mục cho một ngữ cảnh bằng cách chuyển RecyclerView đến registerForContextMenu()

  2. Triển khai onCreateContextMenu() trong Activity hoặc Fragment của bạn.

    Khi chế độ xem đã đăng ký nhận được thao tác chạm & giữ sự kiện, hệ thống sẽ gọi phương thức onCreateContextMenu() của bạn. Đây là nơi bạn xác định các mục trong trình đơn, thường bằng cách tăng cường một tài nguyên trình đơn, như sau ví dụ:

    Kotlin

        override fun onCreateContextMenu(menu: ContextMenu, v: View,
                                menuInfo: ContextMenu.ContextMenuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo)
            val inflater: MenuInflater = menuInflater
            inflater.inflate(R.menu.context_menu, menu)
        }
        

    Java

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v,
                                        ContextMenuInfo menuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo);
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
        }
        

    MenuInflater cho phép bạn tăng cường trình đơn ngữ cảnh từ một tài nguyên trình đơn. Phương thức gọi lại các tham số bao gồm View mà người dùng chọn và ContextMenu.ContextMenuInfo cung cấp thông tin bổ sung về mục đã chọn. Nếu hoạt động của bạn có nhiều chế độ xem, trong đó mỗi chế độ xem cung cấp một trình đơn theo bối cảnh riêng. bạn có thể sử dụng các thông số này để xác định xem nên sử dụng trình đơn theo bối cảnh nào tăng cường.

  3. Triển khai onContextItemSelected(), như trong ví dụ sau. Khi người dùng chọn một mục trong trình đơn, hệ thống sẽ gọi phương thức này để bạn có thể thực hiện hành động thích hợp.

    Kotlin

        override fun onContextItemSelected(item: MenuItem): Boolean {
            val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
            return when (item.itemId) {
                R.id.edit -> {
                    editNote(info.id)
                    true
                }
                R.id.delete -> {
                    deleteNote(info.id)
                    true
                }
                else -> super.onContextItemSelected(item)
            }
        }
        

    Java

        @Override
        public boolean onContextItemSelected(MenuItem item) {
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
            switch (item.getItemId()) {
                case R.id.edit:
                    editNote(info.id);
                    return true;
                case R.id.delete:
                    deleteNote(info.id);
                    return true;
                default:
                    return super.onContextItemSelected(item);
            }
        }
        

    getItemId() truy vấn mã nhận dạng cho mục trong trình đơn đã chọn mà bạn chỉ định cho mỗi mục mục trong trình đơn trong XML sử dụng thuộc tính android:id, như minh hoạ trong Xác định trình đơn trong XML.

    Khi xử lý thành công một mục trong trình đơn, hãy trả lại true. Nếu bạn không xử lý mục trong trình đơn, hãy chuyển mục trong trình đơn đến lớp cấp cao trong quá trình triển khai. Nếu hoạt động có chứa mảnh, thì hoạt động sẽ nhận được lệnh gọi lại này trước tiên. Bằng cách gọi lớp cấp cao khi không được xử lý, hệ thống truyền sự kiện đến phương thức gọi lại tương ứng trong mỗi mảnh, một phương thức tại một lần, theo thứ tự mỗi mảnh được thêm vào, cho đến true hoặc Trả về false. Các cách triển khai mặc định cho Activityandroid.app.Fragment trả bóng false, vì vậy, hãy luôn gọi lớp cấp cao khi chưa xử lý.

Sử dụng chế độ thao tác theo ngữ cảnh

Chế độ thao tác theo ngữ cảnh là cách triển khai hệ thống của ActionMode tập trung sự tương tác của người dùng để đạt được hiệu suất hành động theo ngữ cảnh. Khi người dùng bật chế độ này bằng cách chọn một mục, thanh thao tác theo ngữ cảnh xuất hiện ở đầu màn hình để trình bày hành động mà người dùng có thể thực hiện trên các mục đã chọn. Khi chế độ này bật, người dùng có thể chọn nhiều mục (nếu ứng dụng của bạn hỗ trợ việc đó) và có thể bỏ chọn các mục và tiếp tục di chuyển trong hoạt động đó. Chế độ hành động bị tắt và thanh thao tác theo ngữ cảnh sẽ biến mất khi người dùng bỏ chọn tất cả các mục, nhấn vào nút Quay lại hoặc nhấn vào thao tác Done (Xong) ở bên trái của thanh.

Đối với những khung hiển thị cung cấp thao tác theo ngữ cảnh, bạn thường gọi thuộc tính ngữ cảnh chế độ hành động khi một hoặc cả hai sự kiện sau xảy ra:

  • Người dùng thực hiện thao tác chạm và giữ nguyên khung hiển thị.
  • Người dùng chọn hộp đánh dấu hoặc thành phần giao diện người dùng tương tự trong chế độ xem.

Cách ứng dụng gọi chế độ thao tác theo ngữ cảnh và xác định cho từng tác vụ tuỳ thuộc vào thiết kế của bạn. Có hai thiết kế:

  • Tùy ý đối với các thao tác theo ngữ cảnh trên chế độ xem riêng lẻ.
  • Đối với các thao tác theo ngữ cảnh hàng loạt trên các nhóm mục trong một RecyclerView, cho phép người dùng chọn nhiều mục và thực hiện hành động đối với tất cả các quảng cáo đó.

Phần sau đây mô tả cách thiết lập bắt buộc cho từng trường hợp.

Bật chế độ thao tác theo ngữ cảnh cho từng chế độ xem

Nếu bạn chỉ muốn gọi chế độ thao tác theo ngữ cảnh khi người dùng chọn chế độ xem cụ thể, hãy làm như sau:

  1. Triển khai giao diện ActionMode.Callback như minh hoạ trong ví dụ sau. Trong phương thức gọi lại, bạn có thể chỉ định thao tác cho thanh thao tác theo ngữ cảnh, phản hồi các sự kiện nhấp chuột vào các mục hành động và xử lý các sự kiện khác trong vòng đời cho chế độ thao tác.

    Kotlin

        private val actionModeCallback = object : ActionMode.Callback {
            // Called when the action mode is created. startActionMode() is called.
            override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
                // Inflate a menu resource providing context menu items.
                val inflater: MenuInflater = mode.menuInflater
                inflater.inflate(R.menu.context_menu, menu)
                return true
            }
    
            // Called each time the action mode is shown. Always called after
            // onCreateActionMode, and might be called multiple times if the mode
            // is invalidated.
            override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
                return false // Return false if nothing is done
            }
    
            // Called when the user selects a contextual menu item.
            override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
                return when (item.itemId) {
                    R.id.menu_share -> {
                        shareCurrentItem()
                        mode.finish() // Action picked, so close the CAB.
                        true
                    }
                    else -> false
                }
            }
    
            // Called when the user exits the action mode.
            override fun onDestroyActionMode(mode: ActionMode) {
                actionMode = null
            }
        }
        

    Java

        private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
    
            // Called when the action mode is created. startActionMode() is called.
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                // Inflate a menu resource providing context menu items.
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.context_menu, menu);
                return true;
            }
    
            // Called each time the action mode is shown. Always called after
            // onCreateActionMode, and might be called multiple times if the mode
            // is invalidated.
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false; // Return false if nothing is done.
            }
    
            // Called when the user selects a contextual menu item.
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
               switch (item.getItemId()) {
                    case R.id.menu_share:
                        shareCurrentItem();
                        mode.finish(); // Action picked, so close the CAB.
                        return true;
                    default:
                        return false;
                }
            }
    
            // Called when the user exits the action mode.
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                actionMode = null;
            }
        };
        

    Những lệnh gọi lại sự kiện này gần như giống hệt với lệnh gọi lại cho trình đơn tùy chọn, ngoại trừ mỗi trình đơn này cũng truyền đối tượng ActionMode liên kết với sự kiện. Bạn có thể sử dụng các API ActionMode để thực hiện nhiều thay đổi đối với CAB, chẳng hạn như sửa đổi tiêu đề và phụ đề bằng setTitle()setSubtitle(), chỉ số này rất hữu ích khi cho biết số lượng mục được chọn.

    Mẫu ở trên đặt biến actionMode thành null khi huỷ chế độ hành động. Trong bước tiếp theo, hãy xem cách khởi động biến đó và cách lưu biến thành viên trong hoạt động hoặc có thể hữu ích.

  2. Gọi điện startActionMode() khi bạn muốn hiển thị thanh này, chẳng hạn như khi người dùng thực hiện thao tác chạm và giữ nguyên khung hiển thị.

    Kotlin

        someView.setOnLongClickListener { view ->
            // Called when the user performs a touch & hold on someView.
            when (actionMode) {
                null -> {
                    // Start the CAB using the ActionMode.Callback defined earlier.
                    actionMode = activity?.startActionMode(actionModeCallback)
                    view.isSelected = true
                    true
                }
                else -> false
            }
        }
        

    Java

        someView.setOnLongClickListener(new View.OnLongClickListener() {
            // Called when the user performs a touch & hold on someView.
            public boolean onLongClick(View view) {
                if (actionMode != null) {
                    return false;
                }
    
                // Start the CAB using the ActionMode.Callback defined earlier.
                actionMode = getActivity().startActionMode(actionModeCallback);
                view.setSelected(true);
                return true;
            }
        });
        

    Khi bạn gọi startActionMode(), hệ thống sẽ trả về Đã tạo ActionMode. Khi lưu dữ liệu này trong biến thành viên, bạn có thể thực hiện thay đổi đối với thanh thao tác theo ngữ cảnh để phản hồi các sự kiện khác. Trong mẫu trước, ActionMode được dùng để đảm bảo rằng thực thể ActionMode sẽ không được tạo lại nếu đã tồn tại đang hoạt động, bằng cách kiểm tra xem thành phần có giá trị rỗng hay không trước khi bắt đầu thao tác .

Tạo trình đơn bật lên

Hình ảnh hiển thị trình đơn bật lên trong ứng dụng Gmail, được neo vào nút mục bổ sung ở trên cùng bên phải.
Hình 4. Trình đơn bật lên trong ứng dụng Gmail neo vào nút mục bổ sung ở góc trên cùng bên phải.

Một PopupMenu là một trình đơn cửa sổ phụ được liên kết với View. Quảng cáo này xuất hiện bên dưới quảng cáo cố định xem có còn chỗ hay không hoặc ở phía trên chế độ xem. Công cụ này hữu ích cho sau:

  • Cung cấp trình đơn kiểu mục bổ sung cho các thao tác liên quan đến nội dung cụ thể, chẳng hạn như tiêu đề email của Gmail, được hiển thị trong hình 4.
  • Cung cấp phần thứ hai của một câu lệnh, chẳng hạn như nút được đánh dấu Add (Thêm) để tạo trình đơn bật lên với các tuỳ chọn Add (Thêm) .
  • Cung cấp thực đơn tương tự như Spinner không giữ lại lựa chọn liên tục.

Nếu xác định trình đơn trong XML, dưới đây là cách bạn có thể hiển thị trình đơn bật lên:

  1. Tạo thực thể cho PopupMenu bằng hàm khởi tạo. Việc này sẽ lấy ứng dụng hiện tại ContextView mà trình đơn được liên kết.
  2. Sử dụng MenuInflater để tăng cường tài nguyên trình đơn của bạn vào Đối tượng Menu được trả về bởi PopupMenu.getMenu().
  3. Gọi PopupMenu.show().

Ví dụ: đây là nút hiển thị trình đơn bật lên:

<ImageButton
    android:id="@+id/dropdown_menu"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/descr_overflow_button"
    android:src="@drawable/arrow_drop_down" />

Sau đó, tác vụ có thể hiển thị trình đơn bật lên như sau:

Kotlin

findViewById<ImageButton>(R.id.dropdown_menu).setOnClickListener {
    val popup = PopupMenu(this, it)
    val inflater: MenuInflater = popup.menuInflater
    inflater.inflate(R.menu.actions, popup.menu)
    popup.show()
}

Java

findViewById(R.id.dropdown_menu).setOnClickListener(v -> {
    PopupMenu popup = new PopupMenu(this, v);
    popup.getMenuInflater().inflate(R.menu.actions, popup.getMenu());
    popup.show();
});

Trình đơn sẽ đóng khi người dùng chọn một mục hoặc nhấn vào bên ngoài trình đơn . Bạn có thể theo dõi sự kiện loại bỏ bằng cách sử dụng PopupMenu.OnDismissListener.

Xử lý các sự kiện nhấp chuột

Để thực hiện một thao tác khi người dùng chọn một mục trong trình đơn, hãy triển khai PopupMenu.OnMenuItemClickListener giao diện và đăng ký nó với PopupMenu bằng cách gọi setOnMenuItemclickListener(). Khi người dùng chọn một mục, hệ thống sẽ gọi onMenuItemClick() trong giao diện.

Lệnh này được minh hoạ trong ví dụ sau:

Kotlin

fun showMenu(v: View) {
    PopupMenu(this, v).apply {
        // MainActivity implements OnMenuItemClickListener.
        setOnMenuItemClickListener(this@MainActivity)
        inflate(R.menu.actions)
        show()
    }
}

override fun onMenuItemClick(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.archive -> {
            archive(item)
            true
        }
        R.id.delete -> {
            delete(item)
            true
        }
        else -> false
    }
}

Java

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);

    // This activity implements OnMenuItemClickListener.
    popup.setOnMenuItemClickListener(this);
    popup.inflate(R.menu.actions);
    popup.show();
}

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.archive:
            archive(item);
            return true;
        case R.id.delete:
            delete(item);
            return true;
        default:
            return false;
    }
}

Tạo một nhóm thực đơn

Nhóm trình đơn là một tập hợp các mục trong trình đơn có chung đặc điểm nhất định. Có nhóm, bạn có thể làm như sau:

Bạn có thể tạo một nhóm bằng cách lồng các phần tử <item> bên trong phần tử <group> trong tài nguyên trình đơn của bạn hoặc bằng cách chỉ định một mã nhóm có add() .

Dưới đây là ví dụ về tài nguyên trình đơn bao gồm một nhóm:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/menu_save"
          android:title="@string/menu_save" />
    <!-- menu group -->
    <group android:id="@+id/group_delete">
        <item android:id="@+id/menu_archive"
              android:title="@string/menu_archive" />
        <item android:id="@+id/menu_delete"
              android:title="@string/menu_delete" />
    </group>
</menu>

Các mục trong nhóm này sẽ xuất hiện ở cùng cấp với mục đầu tiên mục – cả 3 mục trong trình đơn đều đồng cấp. Tuy nhiên, bạn có thể sửa đổi đặc điểm của hai mục trong nhóm bằng cách tham chiếu mã nhóm và sử dụng các phương thức trước đó. Hệ thống cũng không bao giờ tách riêng các mục đã được nhóm với nhau. Cho Ví dụ: nếu bạn khai báo android:showAsAction="ifRoom" cho mỗi mục, cả hai đều xuất hiện trong thanh tác vụ hoặc cả hai đều xuất hiện trong hành động tràn.

Sử dụng các mục trong trình đơn có thể đánh dấu

Hình 5. Trình đơn phụ có mục có thể đánh dấu.

Trình đơn có thể hữu ích như một giao diện để bật và tắt các tuỳ chọn, sử dụng hộp đánh dấu cho các tuỳ chọn độc lập, hoặc nút chọn cho các nhóm các lựa chọn độc quyền. Hình 5 cho thấy một trình đơn con gồm các mục có thể đánh dấu nút chọn.

Bạn có thể xác định hành vi có thể đánh dấu cho từng mục trong trình đơn bằng cách sử dụng Thuộc tính android:checkable trong <item> hoặc cho toàn bộ nhóm có android:checkableBehavior trong phần tử <group>. Ví dụ: tất cả các mục trong nhóm trình đơn này có thể đánh dấu bằng nút chọn:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item android:id="@+id/red"
              android:title="@string/red" />
        <item android:id="@+id/blue"
              android:title="@string/blue" />
    </group>
</menu>

Thuộc tính android:checkableBehavior chấp nhận một trong sau:

single
Chỉ có thể đánh dấu một mục trong nhóm, dẫn đến radio các nút.
all
Tất cả các mục đều có thể đánh dấu vào hộp, dẫn đến việc có hộp đánh dấu.
none
Không có mục nào đánh dấu được.

Bạn có thể áp dụng trạng thái đã đánh dấu mặc định cho một mục bằng cách sử dụng Thuộc tính android:checked trong phần tử <item> và thay đổi mã trong mã bằng setChecked() .

Khi bạn chọn một mục có thể đánh dấu, hệ thống sẽ gọi các mục tương ứng phương thức gọi lại do mục chọn, chẳng hạn như onOptionsItemSelected(). Đây là nơi bạn đặt trạng thái của hộp đánh dấu, vì hộp đánh dấu hoặc nút nút sẽ không tự động thay đổi trạng thái. Bạn có thể truy vấn trạng thái hiện tại của mặt hàng—như trước khi người dùng chọn mặt hàng—với isChecked() rồi đặt trạng thái đã đánh dấu bằng setChecked(). Nội dung này được thể hiện trong ví dụ sau:

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.vibrate, R.id.dont_vibrate -> {
            item.isChecked = !item.isChecked
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.vibrate:
        case R.id.dont_vibrate:
            if (item.isChecked()) item.setChecked(false);
            else item.setChecked(true);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

Nếu bạn không đặt trạng thái đã đánh dấu theo cách này, thì trạng thái hiển thị của hộp đánh dấu hoặc nút chọn không thay đổi khi người dùng chọn hộp đánh dấu đó. Khi bạn thực hiện thiết lập trạng thái, hoạt động sẽ duy trì trạng thái đã đánh dấu của mục để khi người dùng mở trình đơn vào lúc khác, trạng thái đã đánh dấu mà bạn đặt là hiển thị.

Thêm các mục trong trình đơn dựa trên ý định

Đôi khi, bạn muốn một mục trong trình đơn khởi chạy một hoạt động bằng một Intent, cho dù đó là hoạt động trong ứng dụng của bạn hay một ứng dụng khác. Khi biết ý định bạn muốn sử dụng và có một mục cụ thể trong trình đơn bắt đầu ý định, bạn có thể thực thi ý định bằng startActivity() trong phương thức gọi lại thích hợp theo mục đã chọn, chẳng hạn như Gọi lại onOptionsItemSelected().

Tuy nhiên, nếu bạn không chắc chắn rằng thiết bị của người dùng có chứa một ứng dụng xử lý ý định, thì việc thêm một mục trong trình đơn gọi ra mục đó có thể dẫn đến mục trong trình đơn không hoạt động vì ý định có thể không phân giải thành một hoạt động. Để giải quyết vấn đề này, Android cho phép bạn tự động thêm các mục trong trình đơn khi Android tìm các hoạt động trên thiết bị xử lý ý định của bạn.

Để thêm các mục trong trình đơn dựa trên các hoạt động có sẵn chấp nhận một ý định, hãy làm như sau như sau:

  1. Xác định một ý định bằng danh mục CATEGORY_ALTERNATIVE hoặc CATEGORY_SELECTED_ALTERNATIVE, hoặc cả hai và mọi yêu cầu khác.
  2. Gọi điện Menu.addIntentOptions(). Sau đó, Android sẽ tìm kiếm bất kỳ ứng dụng nào có thể thực hiện ý định và thêm chúng vào trình đơn của bạn.

Nếu không có ứng dụng nào được cài đặt đáp ứng ý định, thì sẽ không có trình đơn nào mục đã được thêm.

Lệnh này được minh hoạ trong ví dụ sau:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Create an Intent that describes the requirements to fulfill, to be
    // included in the menu. The offering app must include a category value
    // of Intent.CATEGORY_ALTERNATIVE.
    val intent = Intent(null, dataUri).apply {
        addCategory(Intent.CATEGORY_ALTERNATIVE)
    }

    // Search and populate the menu with acceptable offering apps.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items are added.
            0,                  // Unique item ID (none).
            0,                  // Order for the items (none).
            this.componentName, // The current activity name.
            null,               // Specific items to place first (none).
            intent,             // Intent created above that describes the requirements.
            0,                  // Additional flags to control items (none).
            null)               // Array of MenuItems that correlate to specific items (none).

    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu){
    super.onCreateOptionsMenu(menu);

    // Create an Intent that describes the requirements to fulfill, to be
    // included in the menu. The offering app must include a category value
    // of Intent.CATEGORY_ALTERNATIVE.
    Intent intent = new Intent(null, dataUri);
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

    // Search and populate the menu with acceptable offering apps.
    menu.addIntentOptions(
         R.id.intent_group,         // Menu group to which new items are added.
         0,                         // Unique item ID (none).
         0,                         // Order for the items (none).
         this.getComponentName(),   // The current activity name.
         null,                      // Specific items to place first (none).
         intent,                    // Intent created above that describes the requirements.
         0,                         // Additional flags to control items (none).
         null);                     // Array of MenuItems that correlate to specific items (none).

    return true;
}

Đối với mỗi hoạt động được tìm thấy cung cấp một bộ lọc ý định khớp với ý định đã xác định, một mục trong trình đơn sẽ được thêm vào bằng cách sử dụng giá trị trong android:label là tiêu đề mục trong trình đơn và biểu tượng ứng dụng làm trình đơn biểu tượng mục. Phương thức addIntentOptions() trả về số lượng đã thêm mục trong trình đơn.

Cho phép thêm hoạt động của bạn vào các trình đơn khác

Bạn có thể cung cấp dịch vụ liên quan đến hoạt động của mình cho các ứng dụng khác để ứng dụng đó có thể được bao gồm trong trình đơn của các mục khác—thu hồi các vai trò được mô tả trước đó.

Để được đưa vào các trình đơn khác của ứng dụng, hãy xác định bộ lọc ý định như bình thường, nhưng bao gồm CATEGORY_ALTERNATIVE hoặc Giá trị CATEGORY_SELECTED_ALTERNATIVE hoặc cả hai cho ý định danh mục bộ lọc. Lệnh này được minh hoạ trong ví dụ sau:

<intent-filter label="@string/resize_image">
    ...
    <category android:name="android.intent.category.ALTERNATIVE" />
    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    ...
</intent-filter>

Đọc thêm về cách viết bộ lọc ý định trong Ý định và ý định .