zoukankan      html  css  js  c++  java
  • Android 使用PopupWindow实现弹出菜单

    在本文当中,我将会与大家分享一个封装了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的下拉菜单显示了。效果如下:

  • 相关阅读:
    Aix_bugzilla
    aix Mysql安装 Oracle官方教程
    Aix6.1安装openssh
    日媒:阿里巴巴上市融资或超Facebook
    设计模式(一)---单例模式
    Handler具体解释系列(七)——Activity.runOnUiThread()方法具体解释
    TCP/IP协议族——IP工作原理及实例具体解释(上)
    leetCode 41.First Missing Positive (第一个丢失的正数) 解题思路和方法
    Material Design之RecyclerView的使用(一)
    jQuery和CSS3超酷表单美化插件
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/5381671.html
Copyright © 2011-2022 走看看