Trình đơn là thành phần giao diện người dùng phổ biến ở nhiều loại ứng dụng. Để cung cấp trải nghiệm quen thuộc và nhất quán cho người dùng, hãy sử dụng API Menu
để trình bày thao tác của người dùng và các tuỳ chọn khác trong hoạt động.
Tài liệu này cho biết cách tạo ba loại trình đơn hoặc bản trình bày thao tác cơ bản 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 các mục trong trình đơn cho một hoạt động. Đây là nơi bạn đặt các thao tác 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 phần Tạo trình đơn 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à 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ử. API này 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 thao tác ảnh hưởng đến nội dung đã chọn trong thanh ở đầu màn hình và cho phép người dùng chọn nhiều mục.
Xem phần Tạo trình đơn theo bối 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 chế độ xem đã gọi trình đơn. Bạn nên cung cấp nhiều hành động liên quan đến nội dung cụ thể hoặc cung cấp tuỳ chọn cho phần hai của 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, đó cũng là mục đích của tính năng 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 khu vực nội dung trong hoạt động.
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 chuẩn để xác định các mục trong trình đơn. Thay vì tạo một trình đơn trong mã hoạt động, hãy xác định một trình đơn và tất cả các mục thuộc trình đơn đó trong tài nguyên trình đơn XML. Sau đó, bạn có thể tăng cường tài nguyên trình đơn (tải tài nguyên này dưới dạng một đối tượng Menu
) trong phân mảnh hoặc hoạt động 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.
- Thao tác này tách nội dung trình đơn khỏi mã hành vi của ứng dụng.
- Công cụ này cho phép bạn tạo cấu hình của trình đơn thay thế cho các phiên bản nền tảng, 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 khung tài nguyên ứng dụng.
Để xác định trình đơn, hãy tạo một tệp XML bên trong thư mục res/menu/
của dự án và tạo trình đơn bằng các phần tử sau:
<menu>
- Xác định
Menu
, là một vùng chứa các mục trong trình đơn. Phần tử<menu>
phải là nút gốc của tệp và có thể chứa một hoặc nhiều phần tử<item>
và<group>
. <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 phần tử<menu>
lồng nhau để tạo một trình đơn phụ. <group>
- Một vùng chứa không bắt buộc, không hiển thị cho các phần tử
<item>
. Công cụ này cho phép bạn phân loại các mục trong trình đơn để chúng có chung những thuộc tính như trạng thái hoạt động và khả năng hiển thị. Để biết thêm thông tin, hãy xem phần Tạo nhóm trình đơ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 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. Mã này cho phép ứng dụng nhận ra khi người dùng chọn mục đó.
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ột mục hành động trên thanh ứng dụng.
Đây là những thuộc tính quan trọng nhất mà bạn sử dụng. Tuy nhiên, còn có nhiều thuộc tính khác. Để biết thông tin về tất cả các thuộc tính được hỗ trợ, hãy xem tài liệu về Tài nguyên trình đơn.
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ụ hữu ích khi ứng dụng có nhiều chức năng được sắp xếp thành chủ đề, như các mục trên 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ửa và Xem. 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, hãy _phóng to_ tài nguyên trình đơn, chuyển đổi tài nguyên XML thành một đối tượng có thể lập trình bằng cách sử dụng MenuInflater.inflate()
.
Các phần sau đây cho biết cách tăng cườ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 trong hình 1, là nơi bạn đưa các thao tác và tuỳ chọn khác có liên quan đến ngữ 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".
Bạn có thể khai báo các mục cho trình đơn tuỳ chọn từ lớp con Activity
hoặc lớp con Fragment
. Nếu cả hoạt động và mảnh đều khai báo các mục cho trình đơn tuỳ chọn, thì 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 sẽ xuất hiện trước, sau đó là các mục của từng mảnh theo thứ tự và mỗi mảnh đượ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 bằng thuộc tính android:orderInCategory
trong mỗi <item>
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 cung cấp lệnh gọi lại onCreateOptionsMenu()
riêng. Trong phương thức này, bạn có thể tăng cường tài nguyên trình đơn của mình (được xác định trong XML) vào Menu
được cung cấp trong lệnh gọi lại. 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; }
Ngoài ra còn có thể thêm mục trong trình đơ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 mục đó 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 phương thức onOptionsItemSelected()
của hoạt động. Phương thức này truyền tham số MenuItem
đã chọn. Bạn có thể xác định mục bằng cách gọi getItemId()
. Lệnh này trả về mã nhận dạng duy nhất cho mục trong trình đơn, được xác định bằng thuộc tính android:id
trong tài nguyên trình đơn hoặc bằng một số nguyên được cấp cho phương thức add()
. Bạn có thể so khớp mã này với các mục trong trình đơn đã biết để thực hiện thao tác 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 cao cấp của onOptionsItemSelected()
. Cách triển khai mặc định sẽ trả về giá trị sai.
Nếu hoạt động có chứa phân mảnh, trước tiên, hệ thống sẽ gọi onOptionsItemSelected()
cho hoạt động đó, sau đó gọi cho mỗi phân mảnh theo thứ tự thêm vào từng phân mảnh, cho đến khi một hoạt động trả về true
hoặc tất cả các phân mảnh đượ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 bản sao của Menu
đã điền và không gọi lại onCreateOptionsMenu()
trừ khi trình đơn bị vô hiệu hoá.
Tuy nhiên, chỉ nên sử dụng onCreateOptionsMenu()
để tạo trạng thái trình đơn ban đầu và không thực hiện thay đổi trong vòng đời hoạt động.
Nếu muốn sửa trình đơn tuỳ chọn dựa trên các sự kiện xảy ra trong vòng đời của hoạt động, bạn có thể thao tác như vậy trong phương thức onPrepareOptionsMenu()
. Phương thức này chuyển đối tượng Menu
vì đối tượng này hiện đang tồn tại để bạn có thể sửa đối tượng đó, 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 lệnh gọi lại onPrepareOptionsMenu()
.
Trình đơn tuỳ chọn luôn mở khi các mục trong trình đơn hiển thị ở 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
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 khung 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. Tuy nhiên, chế độ xem này thường dùng phổ biến nhất cho các mục ở RecylerView
hoặc các tập kênh chế độ xem khác, trong đó người dùng có thể thực hiện thao tác trực tiếp ở 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ủa 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ữ trên một chế độ xem để khai báo hỗ trợ cho trình đơn theo bối cảnh. Người dùng có thể thực hiện một 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à cách triển khai hệ thống của
ActionMode
, hiển thị một thanh thao tác theo ngữ cảnh (CAB) ở đầu màn hình cùng với các mục hành động ảnh hưởng đến (các) mục đã chọn. Khi chế độ này hoạt động, người dùng có thể thực hiện một thao tác với nhiều mục cùng lúc, nếu ứng dụng của bạn hỗ trợ thao tác đó.
Lưu ý: Trình đơn theo bối cảnh không hỗ trợ lối tắt mục và biểu tượng mục.
Tạo trình đơn ngữ cảnh nổi
Để cung cấp trình đơn ngữ cảnh nổi, hãy làm như sau:
- Đăng ký
View
liên kết với trình đơn theo bối cảnh bằng cách gọiregisterForContextMenu()
và truyềnView
vào đó.Nếu hoạt động sử dụng
RecyclerView
và bạn muốn từng mục cung cấp cùng một trình đơn ngữ cảnh, hãy đăng ký tất cả các mục cho trình đơn ngữ cảnh bằng cách chuyểnRecyclerView
tớiregisterForContextMenu()
. - Triển khai phương thức
onCreateContextMenu()
trongActivity
hoặcFragment
.Khi thành phần hiển thị đã đăng ký nhận được một sự kiện chạm và giữ, hệ thống sẽ gọi phương thức
onCreateContextMenu()
. Đây là nơi bạn xác định các mục trong trình đơn, thường là làm tăng thêm một tài nguyên của trình đơn, như trong ví dụ sau: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 theo bối cảnh từ một tài nguyên trình đơn. Các thông số của phương thức gọi lại bao gồmView
mà người dùng chọn và đối tượngContextMenu.ContextMenuInfo
cung cấp thêm thông tin về mục đã chọn. Nếu hoạt động có nhiều chế độ xem, trong đó mỗi chế độ xem cung cấp một trình đơn ngữ cảnh khác nhau, bạn có thể sử dụng các thông số này để xác định trình đơn ngữ cảnh sẽ tăng cường. 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 thao tác phù 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); } }
Phương thức
getItemId()
truy vấn mã nhận dạng cho mục trình đơn đã chọn, bạn chỉ định cho mỗi mục trình đơn trong XML bằng cách sử dụng thuộc tínhandroid:id
như được hiển thị trong phần 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 tới quá trình triển khai lớp cao cấp. Nếu hoạt động của bạn có các phân mảnh, hoạt động trước đó sẽ nhận được lệnh gọi lại này. Bằng cách gọi lớp cao cấp trên khi không được xử lý, hệ thống sẽ chuyển sự kiện đến phương thức gọi lại tương ứng cho mỗi phân mảnh, lần lượt từng phân mảnh (theo thứ tự thêm từng phân mảnh) cho đến khitrue
hoặcfalse
được trả về. Các phương thức triển khai mặc định choActivity
vàandroid.app.Fragment
trả vềfalse
, vì vậy, hãy luôn gọi lớp cao cấp 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 vào việc thực hiện các thao tác 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 sẽ xuất hiện ở đầu màn hình để hiển thị các thao tác mà người dùng có thể thực hiện trên các mục đã chọn. Khi chế độ này được 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ợ điều đó) và có thể bỏ chọn các mục rồi tiếp tục điều hướng trong hoạt động. Chế độ thao tác bị vô hiệu hoá 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 Xong ở bên trái của thanh.
Đối với các chế độ xem cung cấp thao tác theo ngữ cảnh, bạn thường gọi chế độ thao tác theo ngữ cảnh khi một hoặc cả hai sự kiện sau đây xảy ra:
- Người dùng thực hiện thao tác chạm và giữ trê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 hành vi cho từng thao tác phụ 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 theo nhóm (batch) trên các nhóm mục trong
RecyclerView
, cho phép người dùng chọn nhiều mục và thực hiện thao tác đối với tất cả các mục đó.
Các phần sau đây mô tả cách thiết lập bắt buộc cho trường hợp đầu tiên.
Bật chế độ thao tác theo ngữ cảnh cho từng thành phần hiển thị
Nếu 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:
- Triển khai giao diện
ActionMode.Callback
như trong ví dụ sau. Trong phương thức gọi lại, bạn có thể chỉ định hành động đối với thanh thao tác theo ngữ cảnh, phản hồi các sự kiện nhấp chuột vào mục hành động và xử lý các sự kiện trong vòng đời khác đối với chế độ hành động.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; } };
Các lệnh gọi lại sự kiện này gần giống như lệnh gọi lại cho trình đơn tuỳ chọn, ngoại trừ mỗi lệnh gọi này cũng chuyển đối tượng
ActionMode
liên kết với sự kiện. Bạn có thể sử dụng APIActionMode
để 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ằngsetTitle()
vàsetSubtitle()
. Điều này sẽ giúp cho bạn biết được số lượng mục đã chọn.Mẫu trước đó sẽ đặt biến
actionMode
thànhnull
khi chế độ hành động bị huỷ. Ở bước tiếp theo, hãy xem cách khởi chạy biến đó và cách lưu biến thành viên trong tác vụ hoặc phân mảnh hữu ích thế nào. - Gọi
startActionMode()
khi bạn muốn hiển thị thanh, chẳng hạn như khi người dùng thực hiện thao tác nhấn và giữ trê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ềActionMode
đã tạo. Khi lưu sự kiện này vào một biến thành viên, bạn có thể thay đổ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 bản saoActionMode
sẽ không được tạo lại nếu đã kích hoạt, bằng cách kiểm tra xem thành viên có rỗng hay không trước khi bắt đầu chế độ hành động.
Tạo trình đơn bật lên
PopupMenu
là một trình đơn phương thức liên kết với một View
. Công cụ này xuất hiện bên dưới chế độ xem liên kết nếu còn chỗ hoặc phía trên chế độ xem. Công cụ này hữu ích cho các trường hợp 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, như minh hoạ 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 là Thêm để tạo một trình đơn bật lên với các tuỳ chọn Thêm.
- Cung cấp trình đơn tương tự như
Spinner
nhưng 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:
- Tạo bản sao
PopupMenu
bằng hàm khởi tạo của nó, thao tác này lấy ứng dụng hiện tạiContext
vàView
mà bạn nên liên kết trình đơn. - Sử dụng
MenuInflater
để tăng cường tài nguyên trình đơn vào đối tượngMenu
doPopupMenu.getMenu()
trả về. - Gọi
PopupMenu.show()
.
Ví dụ: sau đây là một 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 bị loại bỏ khi người dùng chọn một mục hoặc nhấn vào khu vực 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 giao diện PopupMenu.OnMenuItemClickListener
và đăng ký giao diệ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 lệnh gọi lạ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 nhóm trình đơ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. Với một nhóm, bạn có thể làm như sau:
- Hiển thị hoặc ẩn tất cả mục bằng
setGroupVisible()
. - Bật hoặc tắt tất cả các mục bằng
setGroupEnabled()
. - Chỉ định xem có thể kiểm tra tất cả các mục bằng
setGroupCheckable()
hay không.
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 của trình đơn hoặc bằng cách chỉ định một mã nhóm bằng phương thứ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 xuất hiện cùng cấp với mục đầu tiên – cả ba mục trong trình đơn đồng cấp với nhau. 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 lại. Ví dụ: nếu khai báo android:showAsAction="ifRoom"
cho mỗi mục, cả hai sẽ xuất hiện trong thanh thao tác hoặc trong mục bổ sung hành động.
Sử dụng các mục trong trình đơn có thể đánh dấu chọn
Trình đơn có thể hữu ích như khi là một giao diện giúp bật và tắt lựa chọn, sử dụng hộp đánh dấu cho các lựa chọn độc lập hoặc nút chọn cho các nhóm lựa chọn loại trừ lẫn nhau. Hình 5 cho thấy một trình đơn phụ có các mục có thể đánh dấu bằng nút chọn.
Bạn có thể xác định thao tác đá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 phần tử <item>
, hoặc cho toàn bộ nhóm có thuộc tính android:checkableBehavior
trong phần tử <group>
. Ví dụ: tất cả các mục trong nhóm trình đơn này đều có thể đánh dấu được 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 các giá trị sau:
single
- Chỉ có thể đánh dấu chọn một mục trong nhóm, dẫn đến các nút chọn.
all
- Bạn có thể đánh dấu vào tất cả các mục, dẫn đến 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 thuộc tính android:checked
trong phần tử <item>
và thay đổi trạng thái đó trong mã bằng phương thức setChecked()
.
Khi chọn một mục có thể đánh dấu, hệ thống sẽ gọi phương thức gọi lại đã chọn cho mục tương ứng, 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 chọn không tự động thay đổi trạng thái của hộp đánh dấu. Bạn có thể truy vấn trạng thái hiện tại của mục (như trước khi người dùng chọn mục) bằng isChecked()
, sau đó đặt trạng thái đã đánh dấu là setChecked()
. Điều 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 không đặt trạng thái đánh dấu theo cách này, trạng thái hiển thị của hộp đánh dấu hoặc nút chọn sẽ không thay đổi khi người dùng chọn hộp đánh dấu hoặc nút chọn đó. Khi bạn đặt trạng thái, hoạt động này sẽ giữ nguyên 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 đã đặt sẽ hiển thị.
Thêm 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 cách sử dụng Intent
, cho dù đó là một hoạt động trong ứng dụng của bạn hay một ứng dụng khác. Khi biết ý định mình muốn sử dụng và có một mục cụ thể trong trình đơn kích hoạt ý định đó, bạn có thể thực thi ý định với startActivity()
trong phương thức gọi lại theo mục đã chọn (chẳng hạn như lệnh gọi lại onOptionsItemSelected()
).
Tuy nhiên, nếu không chắc chắn thiết bị của người dùng có chứa ứng dụng xử lý ý định, thao tác thêm mục trình đơn gọi ứng dụng đó có thể dẫn đến việc một mục trình đơn không hoạt động, vì mục đích có thể không giải quyết được 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 vào trình đơn khi Android tìm thấy các hoạt động trên thiết bị xử lý ý định của bạn.
Để thêm các mục trên trình đơn dựa vào các hoạt động hiện có chấp nhận ý định, hãy làm như sau:
- Xác định ý định bằng danh mục
CATEGORY_ALTERNATIVE
hoặcCATEGORY_SELECTED_ALTERNATIVE
hoặc cả hai, cùng với bất kỳ yêu cầu nào khác. - Gọi
Menu.addIntentOptions()
. Sau đó, Android sẽ tìm kiếm mọi ứng dụng có thể thực hiện ý định và thêm các ứng dụ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, sẽ không có mục trình đơn nào đượ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 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, sử dụng giá trị trong android:label
của bộ lọc ý định làm tiêu đề mục của trình đơn và biểu tượng ứng dụng làm mục trong trình đơn biểu tượng. Phương thức addIntentOptions()
trả về số lượng các mục trong trình đơn đã thêm vào.
Cho phép thêm hoạt động vào các trình đơn khác
Bạn có thể cung cấp dịch vụ về hoạt động của mình cho các ứng dụng khác để có thể đưa ứng dụng vào trình đơn của những ứng dụng khác – đảo ngược các vai trò được mô tả trước đó.
Để được đưa vào trình đơn ứng dụng khác, hãy xác định bộ lọc ý định như bình thường, nhưng hãy sử dụng các giá trị CATEGORY_ALTERNATIVE
hoặc CATEGORY_SELECTED_ALTERNATIVE
hoặc cả hai cho danh mục bộ lọc ý định. 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 bài viết Ý định và bộ lọc ý định.