Android学习笔记 -- 上下文菜单

November . 11 . 2018

上下文菜单在手机App中有着大量的应用, 也是最常用的显示菜单的方式之一, 具体应用在长按某一项需要出现一个菜单栏的情况, 如下图:

contextMenu.PNG

在开始介绍上下文菜单之前, 我们还需要了解一个控件PopupWindow, 顾名思义,就是弹窗,因为popUpwindow的特性完美的符合使用上下文菜单的使用场景, 所以一般的上下文菜单都要用Popupwindow作为载体来实现.

使用 PopupWindow 很简单,可以总结为三个步骤:

  1. 创建PopupWindow对象实例.
  2. 设置背景、注册事件监听器和添加动画.
  3. 显示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;
        }
    }
}

到此一个上下文菜单就制作完成了,效果图在文章最开始的地方,在这就不放了...