Android 菜单
最后修改于 2012年11月26日
在本章 Android 开发教程中,我们将学习菜单。菜单用于组织应用程序可用的命令。在传统的桌面应用程序中,菜单是菜单栏的一部分,通常位于应用程序的顶部区域。上下文菜单或弹出菜单是菜单的特殊情况。
Android 中有三种类型的菜单:选项菜单、上下文菜单和弹出菜单。选项菜单是 activity 的主要菜单项集合。在选项菜单中,我们应该包含对应用程序有全局影响的命令。例如,“设置”菜单。它显示在 activity 的顶部或底部。上下文菜单在特定上下文中显示菜单项。例如,用于 ListView
项目。当用户长按某个元素时显示。弹出菜单以垂直列表的形式显示项目列表,该列表固定在调用菜单的视图上。如果空间允许,它会显示在锚定视图的下方,否则显示在视图的上方。它应该与 activity 中的内容区域相关联。
菜单可以通过手动编码创建,也可以在 XML 文件中定义。如果我们在 XML 文件中定义菜单,我们会使用 MenuInflater
对象从 XML 文件创建菜单。
选项菜单
我们的选项菜单将包含两个菜单项。当我们选择一个菜单项时,会显示一个 Toast
窗口,其中包含所选菜单项的名称。选项菜单在我们单击菜单按钮后显示。
在此示例中,清单文件未被修改。

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/message" /> </LinearLayout>
在 main.xml
布局文件中,我们有一个 TextView
小部件。它将显示一个欢迎消息。
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">OptionsMenu</string> <string name="message">Demonstrating Options Menu</string> <string name="om1">Settings</string> <string name="om2">Tools</string> </resources>
这是 strings.xml
文件。
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/settings" android:title="@string/om1" /> <item android:id="@+id/tools" android:title="@string/om2" /> </menu>
这是 options_menu.xml
文件。它定义了两个菜单项。该文件位于 res/menu/
子目录中。
package com.zetcode.opmenu; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Toast; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.settings: Toast.makeText(MainActivity.this, "Settings menu selected.", Toast.LENGTH_SHORT).show(); return true; case R.id.tools: Toast.makeText(MainActivity.this, "Tools menu selected.", Toast.LENGTH_SHORT).show(); return true; default: return super.onOptionsItemSelected(item); } } }
要在 activity 中启用选项菜单,我们需要覆盖 onCreateOptionsMenu()
和 onOptionsItemSelected()
方法。
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); return true; }
在 onCreateOptionsMenu()
方法中,我们从 options_menu.xml
文件构建选项菜单。我们使用 MenuInflater
类来完成此工作。
@Override public boolean onOptionsItemSelected(MenuItem item) { ... }
onOptionsItemSelected()
方法处理菜单项上的单击事件。
case R.id.settings: Toast.makeText(MainActivity.this, "Settings menu selected.", Toast.LENGTH_SHORT).show(); return true;
在选择“设置”菜单项的情况下,我们会显示一个 Toast
窗口,上面写着“已选择设置菜单”。

上下文菜单
我们有一个 ListView
,其中包含我们行星的名称。长按某个项目将显示一个上下文菜单,其中包含三个选项:删除、大写和 [小写]。
清单文件未被修改。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/lvId" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
这是 main.xml
文件。它包含一个 ListView
小部件。
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">ContextMenu</string> <string-array name="planets"> <item>Mercury</item> <item>Venus</item> <item>Earth</item> <item>Mars</item> <item>Jupiter</item> <item>Saturn</item> <item>Uranus</item> <item>Neptune</item> <item>Pluto</item> </string-array> </resources>
这是 strings.xml
资源文件。
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp" android:textSize="20sp"> </TextView>
这是 row.xml
资源文件。ListView
的每一行都包含一个单独的 TextView
。
package com.zetcode.conmenu; import android.app.Activity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; import android.view.View; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo; import java.util.Arrays; import java.util.ArrayList; public class MainActivity extends Activity { private ListView lv; private ArrayAdapter<String> la; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setupUI(); } public void setupUI() { lv = (ListView) findViewById(R.id.lvId); String[] planets = getResources().getStringArray(R.array.planets); ArrayList<String> lst = new ArrayList<String>(); lst.addAll(Arrays.asList(planets)); la = new ArrayAdapter<String>(this, R.layout.row, lst); lv.setAdapter(la); registerForContextMenu(lv); } @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); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); int pos = info.position; String i = la.getItem(pos); switch (item.getItemId()) { case R.id.delId: la.remove(i); return true; case R.id.upId: String upln = i.toUpperCase(); la.remove(i); la.insert(upln, pos); return true; case R.id.loId: String lpln = i.toLowerCase(); la.remove(i); la.insert(lpln, pos); return true; default: return super.onContextItemSelected(item); } } }
为了实现上下文菜单,我们必须覆盖 onCreateContextMenu()
和 onContextItemSelected()
方法。我们还需要为特定视图调用 registerForContextMenu()
方法。
String[] planets = getResources().getStringArray(R.array.planets); ArrayList<String> lst = new ArrayList<String>(); lst.addAll(Arrays.asList(planets));
我们将删除 ListView
中的项目。因此,我们需要使用 ArrayList
。否则列表将是只读的。
registerForContextMenu(lv);
上下文菜单已为 ListView
小部件注册。
@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); }
在 onCreateContextMenu()
方法中,我们从 context_menu.xml 文件构建上下文菜单。
@Override public boolean onContextItemSelected(MenuItem item) { ... }
onContextItemSelected()
对列表项选择事件做出反应。
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); int pos = info.position; String i = la.getItem(pos);
为了了解所选项目的详细信息,我们使用 AdapterContextMenuInfo
类。我们可以获取所选项目的索引和文本。
case R.id.delId: la.remove(i); return true;
如果我们选择“删除”上下文菜单选项,我们会从 ArrayAdapter
中删除该项目。
case R.id.upId: String upln = i.toUpperCase(); la.remove(i); la.insert(upln, pos); return true;
对于“大写”选项,我们修改字符串,删除原始字符串,然后插入新字符串。

弹出菜单
该示例显示了单击按钮后的 PopupMenu
。
清单文件未被修改。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/btnId" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginTop="10dip" android:text="@string/btn_label" android:onClick="onClick" /> <TextView android:id="@+id/tvId" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dip" /> </LinearLayout>
这是 main.xml
文件。我们有一个 Button
小部件和一个 TextView
小部件。按钮将显示一个 PopupMenu
。
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">PopupMenu</string> <string name="btn_label">Show menu</string> <string name="pm1">Item 1</string> <string name="pm2">Item 2</string> </resources>
这是 strings.xml
资源文件。
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/settings" android:title="@string/pm1" /> <item android:id="@+id/tools" android:title="@string/pm2" /> </menu>
这是 popup_menu.xml
文件。它定义了两个菜单项。该文件位于 res/menu/
子目录中。
package com.zetcode.popmenu; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.MenuItem; import android.widget.TextView; import android.widget.PopupMenu; import android.widget.PopupMenu.OnMenuItemClickListener; public class MainActivity extends Activity implements OnMenuItemClickListener { private TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView) findViewById(R.id.tvId); } public void onClick(View v) { showPopupMenu(v); } public void showPopupMenu(View v) { PopupMenu pm = new PopupMenu(MainActivity.this, v); pm.getMenuInflater().inflate(R.menu.popup_menu, pm.getMenu()); pm.setOnMenuItemClickListener(this); pm.show(); } @Override public boolean onMenuItemClick(MenuItem item) { tv.setText(item.toString() + " selected"); return true; } }
单击按钮小部件后,会显示一个 PopupMenu
。
public void onClick(View v) { showPopupMenu(v); }
此方法是按钮单击的回调。通过 main.xml
文件中的属性设置了此关联。该方法调用 showPopupMenu()
方法。
public void showPopupMenu(View v) { PopupMenu pm = new PopupMenu(MainActivity.this, v); pm.getMenuInflater().inflate(R.menu.popup_menu, pm.getMenu()); pm.setOnMenuItemClickListener(this); pm.show(); }
我们创建 PopupMenu
类的实例。它构建菜单、设置 OnMenuItemClickListener
并显示 PopupMenu
。
@Override public boolean onMenuItemClick(MenuItem item) { tv.setText(item.toString() + " selected"); return true; }
选择菜单项后,会调用 onMenuItemClick()
方法。它将项目的标题设置为 TextView
小部件。
在本章 Android 开发教程中,我们学习了菜单。