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 开发教程中,我们学习了菜单。