上下文菜单在手机App中有着大量的应用, 也是最常用的显示菜单的方式之一, 具体应用在长按某一项需要出现一个菜单栏的情况, 如下图:
在开始介绍上下文菜单之前, 我们还需要了解一个控件PopupWindow, 顾名思义,就是弹窗,因为popUpwindow的特性完美的符合使用上下文菜单的使用场景, 所以一般的上下文菜单都要用Popupwindow作为载体来实现.
使用 PopupWindow 很简单,可以总结为三个步骤:
- 创建PopupWindow对象实例.
- 设置背景、注册事件监听器和添加动画.
- 显示PopupWindow.
关于 PopupWindow 知道这么多就够了, 现在我们就可以开始实现上下文菜单了.
1. 主页布局
添加一个 ListView 用来显示多条数据.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/id_listView_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ebebeb"
android:divider="@null"/>
</LinearLayout>
有了 ListView 自然还需要一个 ListViewItem, 来表示每一项.
home_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/id_textView_home_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_msg_item"
android:padding="10dp"
android:text="ITEM"
android:textColor="#626262"
android:textSize="20sp"/>
</LinearLayout>
主页布局完成.
2. 上线文菜单布局
添加一个 ListView 来显示多条命令.
context_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#dcdcdc"
android:padding="2dp"
android:orientation="vertical">
<ListView
android:id="@+id/id_listView_context"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:divider="@null"/>
</LinearLayout>
有了 ListView 自然需要 item 来描述每一项长啥样.
context_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<TextView
android:id="@+id/id_textView_context_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_msg_item"
android:padding="10dp"
android:text="TextView"
android:textColor="#626262"
android:textSize="20sp"/>
</LinearLayout>
上下文菜单的布局也完成了, 下面来为布局添加具体的逻辑.
3. 上下文菜单的逻辑
该类用来显示一个上下文菜单, 外部可以通过
addList(String option, String value)添加菜单项, 两个参数分别为显示的值以及命令的代号.
show(Context context, View anchor, int xOff, int yOff)方法用来显示菜单项, 四个参数分别是当前的上下文, 点击的控件, 在X轴上显示的位置以及在Y轴上显示的位置.
ContextMenu.java
package example.contextmenu2;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import java.util.ArrayList;
public class ContextMenu {
// popupWindow
View contentView;
PopupWindow popupWindow;
// listView
LayoutInflater layoutInflater;
ListAdapter listAdapter;
ArrayList listData = new ArrayList<>();
// 回调(点击之后要做的事情)
public OnMenuItemClickedListener listener;
public ContextMenu()
{
}
// 添加命令
public void addList(String option, String value) {
ListItem it = new ListItem();
it.option = option;
it.value = value;
listData.add(it);
}
// 显示ContextMenu
public void show(Context context, View anchor, int xOff, int yOff) {
layoutInflater = LayoutInflater.from(context);
contentView = layoutInflater.inflate(R.layout.context_list, null);
// 初始化窗口内容
initContentView();
// 创建PopupWindow, 宽度恒定, 高度自适应
popupWindow = new PopupWindow(contentView, 450 , ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setFocusable(true); // 设置焦点, 为true是点击外部popUpwindow会消失
popupWindow.showAsDropDown(anchor, xOff, yOff);
}
// 初始化窗口
private void initContentView() {
listAdapter = new ListAdapter();
ListView listView = contentView.findViewById(R.id.id_listView_context);
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
ListItem it = (ListItem) listAdapter.getItem(position);
onItemClicked(view, it);
}
});
}
// 回调
public interface OnMenuItemClickedListener
{
void onMenuItemClicked(String option, String value);
}
// 点击后的操作
private void onItemClicked(View view, ListItem it) {
// 关闭窗口
popupWindow.dismiss();
// 回调
if (listener != null)
{
listener.onMenuItemClicked(it.option, it.value);
}
}
// item 类
public static class ListItem {
public String option; // 属性名
public String value; // 对应命令
}
// 适配器
private class ListAdapter extends BaseAdapter {
@Override
public int getCount() {
return listData.size();
}
@Override
public Object getItem(int position) {
return listData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.context_item, parent, false);
}
// 显示数据
ListItem it = (ListItem) getItem(position);
((TextView)convertView.findViewById(R.id.id_textView_context_item)).setText(it.option);
return convertView;
}
}
}
4.主页面逻辑
注意看注释
MainActivity.java
package example.contextmenu2;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
final String TAG = "测试: ";
MyListAdapter listAdapter;
ArrayList listData = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 ListView
listAdapter = new MyListAdapter();
ListView listView = (ListView) findViewById(R.id.id_listView_home);
listView.setAdapter(listAdapter);
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
MyListItem it = (MyListItem) listAdapter.getItem(position);
onItemLongClicked(view, it);
return true;
}
});
// 初始化数据
demo();
}
// 长按事件
private void onItemLongClicked(View view, MyListItem it) {
ContextMenu menu = new ContextMenu();
menu.addList("发送给朋友", "sendto");
menu.addList("收藏", "favorite");
menu.addList("删除", "remove");
menu.addList("更多", "more");
menu.listener = new ContextMenu.OnMenuItemClickedListener(){
@Override
public void onMenuItemClicked(String option, String value) {
Toast.makeText(MainActivity.this, "点击了: " + option, Toast.LENGTH_SHORT).show();
}
};
menu.show(this, view, view.getWidth()/2, -view.getHeight()/2);
}
// 初始化数据
private void demo() {
MyListItem it;
it = new MyListItem();
it.value = "生命里,一些缱绻,无论素净,还是喧哗,都已经被岁月赋予了清喜的味道,一些闲词,或清新,或淡雅,总会在某一个回眸的时刻醉了流年,濡湿了柔软的心,冥冥之中,我们沿着呼唤的风声,终于在堆满落花的秋里,再次重逢,念在天涯,心在咫尺,我相信,一米阳光,才是我们最好的距离。";
listData.add(it);
it = new MyListItem();
it.value = "缘起是诗,缘离是画,这些关于岁月,关于记忆的章节,终会被时光搁置在无法触及的红尘之外,曾经,你我一别经年,可风里,总有一段美丽会与我们不期而遇,一盏琉璃,半杯心悦,端然着那一份醉人的静,这安静行走的流年,总会被过往赋予一份清喜,一份浪漫。";
listData.add(it);
it = new MyListItem();
it.value = "或许,习惯了着布衣素颜,让清心若雪,不喜张扬,不畏喧哗,守着一怀自己的素韵安静,在自己心中的半亩桃源,修篱种菊,喜欢与山水相依,与流水对话,让文字的墨香,依附在心灵的每一个角落,也喜欢,在闲时,端坐时光一隅,将一本书读到无字,将一盏茶喝到无味,将一个故事看到流泪.……心染尘香,不须有多少的柔情话语去讲,只要能够念起,便是一份温暖。";
listData.add(it);
it = new MyListItem();
it.value = "再打开记忆的栅栏,取一壶往昔,与流年对坐,情有多深,心就有多疼,触不到的可惜,在挥手袖的风里滋养着忧伤,捻一缕清芬,看三千浮华,历历往事,素淡清雅,研一池墨香,植于眉心,相遇如梦,一直充满虔诚的无暇,这个十月,我用满怀的深情,打开文字的苍白,灵动的心事,穿过岁月的轩窗,迎面而来,我未曾离开,为何不见了原来这世间的繁华与葱笼?是时光的无情,还是流年容易沧桑,让所有的一切都如逝水匆匆,去了遥远的天涯?";
listData.add(it);
listAdapter.notifyDataSetChanged();
}
// item 类
public static class MyListItem {
public String value;
}
// 适配器
public class MyListAdapter extends BaseAdapter {
@Override
public int getCount() {
return listData.size();
}
@Override
public Object getItem(int position) {
return listData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 新建视图
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.home_item, parent, false);
}
MyListItem it = (MyListItem) getItem(position);
((TextView)convertView.findViewById(R.id.id_textView_home_item)).setText(it.value);
return convertView;
}
}
}
到此一个上下文菜单就制作完成了.