转于:http://blog.csdn.net/maosidiaoxian/article/details/39178167
?
-----------------------------------------------------------------------------------------
在本文当中,我将会与大家分享一个封装了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?([email protected])?
- ?*?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));??
- ????}??
- ??
- ????/**?
- ?????*?菜单的界面视图.?
- ?????*?
- [email protected]?
- [email protected]?
- ?????*/??
- ????protected?abstract?View?onCreateView(Context?context);??
- ??
- ????/**?
- ?????*?菜单界面视图中的列表.?
- ?????*?
- [email protected]?
- [email protected]?
- ?????*/??
- ????protected?abstract?ListView?findListView(View?view);??
- ??
- ????/**?
- ?????*?菜单列表中的适配器.?
- ?????*?
- [email protected]?
- [email protected]?
- [email protected]?
- ?????*/??
- ????protected?abstract?ArrayAdapter<Item>?onCreateAdapter(Context?context,?ArrayList<Item>?itemList);??
- ??
- ????/**?
- ?????*?添加菜单项.?
- ?????*?
- [email protected]?
- [email protected]?
- ?????*/??
- ????public?void?addItem(String?text,?int?id)?{??
- ????????mItemList.add(new?Item(text,?id));??
- ????????mAdapter.notifyDataSetChanged();??
- ????}??
- ??
- ????/**?
- ?????*?添加菜单项.?
- ?????*?
- [email protected]?
- [email protected]?
- ?????*/??
- ????public?void?addItem(int?resId,?int?id)?{??
- ????????addItem(mContext.getString(resId),?id);??
- ????}??
- ??
- ????/**?
- ?????*?作为指定View的下拉控制显示.?
- ?????*?
- [email protected]?
- ?????*/??
- ????public?void?showAsDropDown(View?parent)?{??
- ????????mPopupWindow.showAsDropDown(parent);??
- ????}??
- ??
- ????/**?
- ?????*?隐藏菜单.?
- ?????*/??
- ????public?void?dismiss()?{??
- ????????mPopupWindow.dismiss();??
- ????}??
- ??
- ????/**?
- ?????*?设置菜单选择监听.?
- ?????*?
- [email protected]?
- ?????*/??
- ????public?void?setOnItemSelectedListener(OnItemSelectedListener?listener)?{??
- ????????mListener?=?listener;??
- ????}??
- ??
- ????/**?
- ?????*?当前菜单是否正在显示.?
- ?????*?
- [email protected]?
- ?????*/??
- ????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?{??
- ????????/**?
- ?????????*?菜单被选择时的回调接口.?
- ?????????*?
- [email protected]iew.?
- [email protected]?
- [email protected]?
- ?????????*/??
- ????????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?([email protected])?
- ?*?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?([email protected])?
- ?*?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的下拉菜单显示了。效果如下:
?
?