在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用。

因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在本文当中,我将与大家分享如何使用PopupWindow实现弹出菜单。

1.弹出菜单的封装PopMenu

PopupWindow可以说是一个浮动在Activity之上的容器,通常用来显示自定义的视图。比如像自动完成输入框AutoCompleteTextView,它的提示列表就是使用PopupWindow来实现的。下面的抽象类PopMenu封装了使用PopupWindow实现弹出菜单的UI逻辑,但不包括界面布局的设定。

/*
 * Date: 14-6-13
 * Project: Parking Lay-by
 */
package cn.irains.access.v2.common;

import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;

import java.util.ArrayList;

/**
 * 对弹出菜单的封装.
 * Author: msdx (645079761@qq.com)
 * Time: 14-6-13 下午1:51
 */
public abstract class PopMenu {
    /**
     * 上下文.
     */
    private Context mContext;
    /**
     * 菜单项
     */
    private ArrayList<Item> mItemList;
    /**
     * 列表适配器.
     */
    private ArrayAdapter<Item> mAdapter;
    /**
     * 菜单选择监听.
     */
    private OnItemSelectedListener mListener;
    /**
     * 列表.
     */
    private ListView mListView;
    /**
     * 弹出窗口.
     */
    private PopupWindow mPopupWindow;

    public PopMenu(Context context) {
        mContext = context;
        mItemList = new ArrayList<Item>(2);
        View view = onCreateView(context);
        view.setFocusableInTouchMode(true);
        mAdapter = onCreateAdapter(context, mItemList);
        mListView = findListView(view);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Item item = mAdapter.getItem(position);
                if (mListener != null) {
                    mListener.selected(view, item, position);
                }
                mPopupWindow.dismiss();
            }
        });
        view.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_MENU && mPopupWindow.isShowing()) {
                    mPopupWindow.dismiss();
                    return true;
                }
                return false;
            }
        });
        mPopupWindow = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        mPopupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
    }

    /**
     * 菜单的界面视图.
     *
     * @param context
     * @return
     */
    protected abstract View onCreateView(Context context);

    /**
     * 菜单界面视图中的列表.
     *
     * @param view
     * @return
     */
    protected abstract ListView findListView(View view);

    /**
     * 菜单列表中的适配器.
     *
     * @param context
     * @param itemList 表示所有菜单项.
     * @return
     */
    protected abstract ArrayAdapter<Item> onCreateAdapter(Context context, ArrayList<Item> itemList);

    /**
     * 添加菜单项.
     *
     * @param text 菜单项文字内容.
     * @param id   菜单项的ID
     */
    public void addItem(String text, int id) {
        mItemList.add(new Item(text, id));
        mAdapter.notifyDataSetChanged();
    }

    /**
     * 添加菜单项.
     *
     * @param resId 菜单项文字内容的资源ID
     * @param id    菜单项的ID.
     */
    public void addItem(int resId, int id) {
        addItem(mContext.getString(resId), id);
    }

    /**
     * 作为指定View的下拉控制显示.
     *
     * @param parent 所指定的View
     */
    public void showAsDropDown(View parent) {
        mPopupWindow.showAsDropDown(parent);
    }

    /**
     * 隐藏菜单.
     */
    public void dismiss() {
        mPopupWindow.dismiss();
    }

    /**
     * 设置菜单选择监听.
     *
     * @param listener 监听器.
     */
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        mListener = listener;
    }

    /**
     * 当前菜单是否正在显示.
     *
     * @return
     */
    public boolean isShowing() {
        return mPopupWindow.isShowing();
    }

    /**
     * 菜单项.
     */
    public static class Item {
        public String text;
        public int id;

        public Item(String text, int id) {
            this.text = text;
            this.id = id;
        }

        @Override
        public String toString() {
            return text;
        }
    }

    /**
     * 菜单项选择监听接口.
     */
    public static interface OnItemSelectedListener {
        /**
         * 菜单被选择时的回调接口.
         *
         * @param view     被选择的内容的View.
         * @param item     被选择的菜单项.
         * @param position 被选择的位置.
         */
        public void selected(View view, Item item, int position);
    }
}

这里面有三个抽象方法,第一个是onCreateView(Context context),在这里需要实现并返回我们的弹出菜单的这个view,然后才能装载到PopupWindow当中并显示出来。

第二个方法是findListView(View view)。这是因为我们的菜单通常是一个列表,然后点击去选择列表的某一项,所以这里需要返回一个ListView对象,用来装载我们的菜单项。

第三个方法是onCreateAdapter,即listview的适配器。

在这个类中,还封装了一个内部类Item:

    /**
     * 菜单项.
     */
    public static class Item {
        public String text;
        public int id;

        public Item(String text, int id) {
            this.text = text;
            this.id = id;
        }

        @Override
        public String toString() {
            return text;
        }
    }

它用来表示我们的菜单项,text是显示在菜单当中的文本信息,id表示菜单项的ID。

在该抽象类中还定义了一个接口OnItemSelectedListener,是在菜单项被点击时的回调接口。关于它的说明见注释,这是我在这个博客里目前为止注释写得最详细的一个类了。

2.PopMenu的使用

首先继承PopMenu并实现抽象方法:

/*
 * Date: 14-9-2
 * Project: Access-Control-V2
 */
package cn.irains.access.v2.usermanager;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;

import cn.irains.access.v2.R;
import cn.irains.access.v2.common.PopMenu;

/**
 * Author: msdx (645079761@qq.com)
 * Time: 14-9-2 上午8:56
 */
public class UserMenu extends PopMenu {
    public UserMenu(Context context) {
        super(context);
    }

    @Override
    protected ListView findListView(View view) {
        return (ListView) view.findViewById(R.id.menu_listview);
    }

    @Override
    protected View onCreateView(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.menu_user, null);
        return view;
    }

    @Override
    protected ArrayAdapter<Item> onCreateAdapter(Context context, ArrayList<Item> items) {
        return new ArrayAdapter<Item>(context, R.layout.item_menu_user, items);
    }
}

ListView的宽度,如果不写死的话,默认是宽度填充满父控件的,就像ViewPager默认高度填满父控件一样。如果想让ListView的宽度适配内容,则需要重写一下。参考前面的文章(Android开发技巧——ViewPager衍生出来的2个类),代码如下:

/*
 * Date: 14-9-2
 * Project: Access-Control-V2
 */
package cn.irains.access.v2.common;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ListView;

/**
 * 宽度适配内容的ListView.
 * Author: msdx (645079761@qq.com)
 * Time: 14-9-2 下午5:14
 */
public class WrapWidthListView extends ListView {

    public WrapWidthListView(Context context) {
        super(context);
    }

    public WrapWidthListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapWidthListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec);
            int w = child.getMeasuredWidth();
            if (w > width) width = w;
        }

        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width + getPaddingLeft() + getPaddingRight(), MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

弹出菜单的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:paddingRight="@dimen/pop_menu_padding"
              android:orientation="vertical"
              android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/head"
        android:src="@drawable/pop_menu_head"
        android:layout_gravity="right"
        android:layout_width="wrap_content"
        android:contentDescription="@null"
        android:layout_marginRight="18dp"
        android:layout_height="wrap_content"/>

    <cn.irains.access.v2.common.WrapWidthListView
        android:id="@+id/menu_listview"
        android:padding="6dp"
        android:focusable="true"
        android:layout_width="wrap_content"
        android:background="@drawable/pop_menu_body"
        android:cacheColorHint="@android:color/transparent"
        android:layout_height="wrap_content"/>
</LinearLayout>

其中的ImageView的照片是一个黑色三角图案。这个等在最后我发一下效果图就明白了。ListView背景是一张黑色图片。

接下来是item的布局,只是一个TextView,代码如下:

<?xml version="1.0" encoding="utf-8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:textSize="@dimen/text_size_large"
          android:textColor="@color/text_choice_selector"
          android:background="@drawable/item_choice_selector"
          android:gravity="center"
          android:layout_gravity="center"
          android:paddingLeft="20dp"
          android:paddingTop="6dp"
          android:singleLine="true"
          android:paddingBottom="6dp"
          android:paddingRight="20dp"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"/>

使用代码如下:

    private static final int USER_SEARCH = 0;
    private static final int USER_ADD = 1;
    private UserMenu mMenu;

    private void initMenu() {
        mMenu = new UserMenu(context);
        mMenu.addItem(R.string.user_search, USER_SEARCH);
        mMenu.addItem(R.string.user_add, USER_ADD);
        mMenu.setOnItemSelectedListener(new PopMenu.OnItemSelectedListener() {
            @Override
            public void selected(View view, PopMenu.Item item, int position) {
                switch (item.id) {
                    case USER_SEARCH:
                        startActivity(new Intent(getActivity(), UserSearchActivity.class));
                        break;
                    case USER_ADD:
                        startActivity(new Intent(getActivity(), UserAddActivity.class));
                        break;
                }
            }
        });
    }

在activity的onCreate或fragment中的onCreateView中初始化menu代码,然后需要显示时调用mMenu.showAsDropDown(view);它就作为view的下拉菜单显示了。效果如下:

本文原创,转载请注明出处:http://blog.csdn.net/maosidiaoxian/article/details/39178167

Android开发技巧——使用PopupWindow实现弹出菜单的更多相关文章

  1. Android 使用PopupWindow实现弹出菜单

    在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...

  2. 如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题

    如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 在android中有时候可能要实现一个底部弹 ...

  3. 用PopupWindow实现弹出菜单(弹出的菜单采用自定义布局)

         用PopupWindow实现弹出菜单是一个比较好的方式.当然我们还有一个类PopupMenu也能实现弹出菜单,但那个太过于局限了,所以不是很推荐. 这个实例的效果是这样的:点击按钮后,一个菜 ...

  4. Android ListView两种长按弹出菜单方式

    转自:http://www.cnblogs.com/yejiurui/p/3247527.html package com.wyl.download_demo; import java.util.Ar ...

  5. Android开发技巧——实现可复用的ActionSheet菜单

    在上一篇<Android开发技巧--使用Dialog实现仿QQ的ActionSheet菜单>中,讲了这种菜单的实现过程,接下来将把它改成一个可复用的控件库. 本文原创,转载请注明出处: h ...

  6. Android开发实战之底部Dialog弹出效果

    在Android开发中,有很多情况下我们需要使用到对话框,遗憾的是,安卓自带的对话框样式不能满足我们实际的需要,所以往往需要我们自定义对话框,具体做法:写一个对话框继承自Dialog实现他的一个构造方 ...

  7. Android基础之响应Menu键弹出菜单Demo

    对于Android我也不是很熟悉,只是学习一些基本内容就OK.所以写的内容也很简单.本Demo要实现的效果就点击Menu键将弹出一个菜单并响应点击菜单项事件. 一.废话少说直接上代码.其实就是重写两个 ...

  8. Android开发禁止首次进入activity弹出软键盘,限制屏幕只能竖屏或者横屏展示

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 只需在在Manifest.xml中设定activity的属性为: android:windowSoft ...

  9. <转>Android开发使输入框点击弹出日期选择对话框的方法

    非常简单直接上代码: 转自:http://blog.sina.com.cn/s/blog_4ac1b5f60102vgnx.html final EditText et1=(EditText)find ...

随机推荐

  1. Servlet中service()方法

    在学习Servlet的过程中,我们大多时候编码都是直接继承HttpServlet这个类,并且重写doGet ,doPost,但是查看Api时我们会发现Servlet接口 ,GenericSevlet抽 ...

  2. SVN提交时显示:Path is not a working copy directory

    说明你地址没有checkout啊 先checkout,才能add和commi. 要是在一个已有的项目出现这个错误,就是包含这个地址的文件夹没添加进去,去上一层再试一次. 总之,养成在项目根目录提交的习 ...

  3. 小程序敏感信息解密-java

    /** * AES解密 * @param content 密文 * @return * @throws InvalidAlgorithmParameterException * @throws NoS ...

  4. React框架 dva 和 mobx 的使用感受

    最近在用react写web项目,领导为了让前端便于维护要求都用react作为开发基础,框架选型不限.在使用 react 的时候或多或少会接触到状态管理,从开始学 react 到现在也挺久了,做一些前端 ...

  5. 异常处理&RandomAccessFile&节奏感

    异常处理 异常处理方面的知识,下面是学习中记的笔记: try尝试捕获异常 catch对捕获的异常进行处理 多个catch要注意的问题: 一.顺序问题,先小后大,也就是先子类后父类.因为当异常出现的时候 ...

  6. Laravel中构造方法中不能写return!!!

    今天遇到的大坑 在laravel中 __construct 这个方法中不能写return 完全不能返回 而且还会向下执行具体原因不知道为什么解决办法!!!!用中间件来实现就可以了 Over!!!

  7. Docker内核能力机制

    能力机制(Capability)是 Linux 内核一个强大的特性,可以提供细粒度的权限访问控制. Linux 内核自 2.2 版本起就支持能力机制,它将权限划分为更加细粒度的操作能力,既可以作用在进 ...

  8. Docker环境 ELK 快速部署

    Docker环境 ELK快速部署 环境 Centos 7.4 , Docker version 17.12 Docker至少3GB内存: #内核配置 echo ' vm.max_map_count = ...

  9. Android 5.0 调色 Palette调色功能

    Palette非常好用,也非常好玩. Palette的作用是从图像中提取突出的颜色,这样我们可以根据提取到的色值把它赋给Toolbar,标题,状态栏等,可以使我们的整个界面色调统一,效果非常好看. P ...

  10. 学习TensorFlow,调用预训练好的网络(Alex, VGG, ResNet etc)

    视觉问题引入深度神经网络后,针对端对端的训练和预测网络,可以看是特征的表达和任务的决策问题(分类,回归等).当我们自己的训练数据量过小时,往往借助牛人已经预训练好的网络进行特征的提取,然后在后面加上自 ...