在本文当中,我将会与大家分享一个封装了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. git pull 报错 You have not concluded your merge (MERGE_HEAD exists).

    git pull时报错 解决方案:

  2. linux设置oracle自动启动

    用root用户 在/etc/init.d/目录下创建Oracle的服务文件 cd /etc/init.d vi oracle11g   添加内容如下   #!/bin/bash # chkconfig ...

  3. Docker安装tomcat和部署项目

    随着微服务的流行,Docker越来越流行,正如它的理念"Build, Ship, and Run Any App, Anywhere"一样,Docker提供的容器隔离技术使得开发人 ...

  4. 81. Search in Rotated Sorted Array II (中等)

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

  5. TP中的AJAX返回ajaxReturn()

    系统支持任何的AJAX类库,Action类提供了ajaxReturn方法用于AJAX调用后返回数据给客户端.并且支持JSON.XML和EVAL三种方式给客户端接受数据,通过配置DEFAULT_AJAX ...

  6. Go 语言结构

    Go Hello World 实例 Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 & 表达式 注释 接下来让我们来看下简单的代码,该代码输出了"Hello ...

  7. k8s Kubernetes v1.10

    #转移页面 http://www.cnblogs.com/elvi/p/8976305.html

  8. Conference-Web Search and Data Mining

    Conference WSDM(Web Search and Data Mining)The ACM WSDM Conference Series 不像KDD.WWW或者SIGIR,WSDM因为从最开 ...

  9. Linux-2.6.25 TCPIP函数调用大致流程

    插口层系统调用send    sys_send        sys_sendtosendto    sys_sendto        sock_sendmsgsendmsg    sys_send ...

  10. how to output quotes in bash prompt

    introduction In certain situations, quotes are required to be output in the command prompt. To do th ...