当前位置: 代码迷 >> Android >> Android开发技艺——使用PopupWindow实现弹出菜单
  详细解决方案

Android开发技艺——使用PopupWindow实现弹出菜单

热度:100   发布时间:2016-04-27 22:05:11.0
Android开发技巧——使用PopupWindow实现弹出菜单

转于:http://blog.csdn.net/maosidiaoxian/article/details/39178167

?

-----------------------------------------------------------------------------------------

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

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

1.弹出菜单的封装PopMenu

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

?

[java]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. /*?
  2. ?*?Date:?14-6-13?
  3. ?*?Project:?Parking?Lay-by?
  4. ?*/??
  5. package?cn.irains.access.v2.common;??
  6. ??
  7. import?android.content.Context;??
  8. import?android.graphics.drawable.ColorDrawable;??
  9. import?android.view.KeyEvent;??
  10. import?android.view.View;??
  11. import?android.view.ViewGroup;??
  12. import?android.widget.AdapterView;??
  13. import?android.widget.ArrayAdapter;??
  14. import?android.widget.ListView;??
  15. import?android.widget.PopupWindow;??
  16. ??
  17. import?java.util.ArrayList;??
  18. ??
  19. /**?
  20. ?*?对弹出菜单的封装.?
  21. ?*?Author:?msdx?([email protected])?
  22. ?*?Time:?14-6-13?下午1:51?
  23. ?*/??
  24. public?abstract?class?PopMenu?{??
  25. ????/**?
  26. ?????*?上下文.?
  27. ?????*/??
  28. ????private?Context?mContext;??
  29. ????/**?
  30. ?????*?菜单项?
  31. ?????*/??
  32. ????private?ArrayList<Item>?mItemList;??
  33. ????/**?
  34. ?????*?列表适配器.?
  35. ?????*/??
  36. ????private?ArrayAdapter<Item>?mAdapter;??
  37. ????/**?
  38. ?????*?菜单选择监听.?
  39. ?????*/??
  40. ????private?OnItemSelectedListener?mListener;??
  41. ????/**?
  42. ?????*?列表.?
  43. ?????*/??
  44. ????private?ListView?mListView;??
  45. ????/**?
  46. ?????*?弹出窗口.?
  47. ?????*/??
  48. ????private?PopupWindow?mPopupWindow;??
  49. ??
  50. ????public?PopMenu(Context?context)?{??
  51. ????????mContext?=?context;??
  52. ????????mItemList?=?new?ArrayList<Item>(2);??
  53. ????????View?view?=?onCreateView(context);??
  54. ????????view.setFocusableInTouchMode(true);??
  55. ????????mAdapter?=?onCreateAdapter(context,?mItemList);??
  56. ????????mListView?=?findListView(view);??
  57. ????????mListView.setAdapter(mAdapter);??
  58. ????????mListView.setOnItemClickListener(new?AdapterView.OnItemClickListener()?{??
  59. ????????????@Override??
  60. ????????????public?void?onItemClick(AdapterView<?>?parent,?View?view,?int?position,?long?id)?{??
  61. ????????????????Item?item?=?mAdapter.getItem(position);??
  62. ????????????????if?(mListener?!=?null)?{??
  63. ????????????????????mListener.selected(view,?item,?position);??
  64. ????????????????}??
  65. ????????????????mPopupWindow.dismiss();??
  66. ????????????}??
  67. ????????});??
  68. ????????view.setOnKeyListener(new?View.OnKeyListener()?{??
  69. ????????????@Override??
  70. ????????????public?boolean?onKey(View?v,?int?keyCode,?KeyEvent?event)?{??
  71. ????????????????if?(keyCode?==?KeyEvent.KEYCODE_MENU?&&?mPopupWindow.isShowing())?{??
  72. ????????????????????mPopupWindow.dismiss();??
  73. ????????????????????return?true;??
  74. ????????????????}??
  75. ????????????????return?false;??
  76. ????????????}??
  77. ????????});??
  78. ????????mPopupWindow?=?new?PopupWindow(view,?ViewGroup.LayoutParams.WRAP_CONTENT,?ViewGroup.LayoutParams.WRAP_CONTENT,?true);??
  79. ????????mPopupWindow.setBackgroundDrawable(new?ColorDrawable(0x00000000));??
  80. ????}??
  81. ??
  82. ????/**?
  83. ?????*?菜单的界面视图.?
  84. ?????*?
  85. [email protected]?
  86. [email protected]?
  87. ?????*/??
  88. ????protected?abstract?View?onCreateView(Context?context);??
  89. ??
  90. ????/**?
  91. ?????*?菜单界面视图中的列表.?
  92. ?????*?
  93. [email protected]?
  94. [email protected]?
  95. ?????*/??
  96. ????protected?abstract?ListView?findListView(View?view);??
  97. ??
  98. ????/**?
  99. ?????*?菜单列表中的适配器.?
  100. ?????*?
  101. [email protected]?
  102. [email protected]?
  103. [email protected]?
  104. ?????*/??
  105. ????protected?abstract?ArrayAdapter<Item>?onCreateAdapter(Context?context,?ArrayList<Item>?itemList);??
  106. ??
  107. ????/**?
  108. ?????*?添加菜单项.?
  109. ?????*?
  110. [email protected]?
  111. [email protected]?
  112. ?????*/??
  113. ????public?void?addItem(String?text,?int?id)?{??
  114. ????????mItemList.add(new?Item(text,?id));??
  115. ????????mAdapter.notifyDataSetChanged();??
  116. ????}??
  117. ??
  118. ????/**?
  119. ?????*?添加菜单项.?
  120. ?????*?
  121. [email protected]?
  122. [email protected]?
  123. ?????*/??
  124. ????public?void?addItem(int?resId,?int?id)?{??
  125. ????????addItem(mContext.getString(resId),?id);??
  126. ????}??
  127. ??
  128. ????/**?
  129. ?????*?作为指定View的下拉控制显示.?
  130. ?????*?
  131. [email protected]?
  132. ?????*/??
  133. ????public?void?showAsDropDown(View?parent)?{??
  134. ????????mPopupWindow.showAsDropDown(parent);??
  135. ????}??
  136. ??
  137. ????/**?
  138. ?????*?隐藏菜单.?
  139. ?????*/??
  140. ????public?void?dismiss()?{??
  141. ????????mPopupWindow.dismiss();??
  142. ????}??
  143. ??
  144. ????/**?
  145. ?????*?设置菜单选择监听.?
  146. ?????*?
  147. [email protected]?
  148. ?????*/??
  149. ????public?void?setOnItemSelectedListener(OnItemSelectedListener?listener)?{??
  150. ????????mListener?=?listener;??
  151. ????}??
  152. ??
  153. ????/**?
  154. ?????*?当前菜单是否正在显示.?
  155. ?????*?
  156. [email protected]?
  157. ?????*/??
  158. ????public?boolean?isShowing()?{??
  159. ????????return?mPopupWindow.isShowing();??
  160. ????}??
  161. ??
  162. ????/**?
  163. ?????*?菜单项.?
  164. ?????*/??
  165. ????public?static?class?Item?{??
  166. ????????public?String?text;??
  167. ????????public?int?id;??
  168. ??
  169. ????????public?Item(String?text,?int?id)?{??
  170. ????????????this.text?=?text;??
  171. ????????????this.id?=?id;??
  172. ????????}??
  173. ??
  174. ????????@Override??
  175. ????????public?String?toString()?{??
  176. ????????????return?text;??
  177. ????????}??
  178. ????}??
  179. ??
  180. ????/**?
  181. ?????*?菜单项选择监听接口.?
  182. ?????*/??
  183. ????public?static?interface?OnItemSelectedListener?{??
  184. ????????/**?
  185. ?????????*?菜单被选择时的回调接口.?
  186. ?????????*?
  187. [email protected]iew.?
  188. [email protected]?
  189. [email protected]?
  190. ?????????*/??
  191. ????????public?void?selected(View?view,?Item?item,?int?position);??
  192. ????}??
  193. }??


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

?

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

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

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

[java]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. /**?
  2. ?*?菜单项.?
  3. ?*/??
  4. public?static?class?Item?{??
  5. ????public?String?text;??
  6. ????public?int?id;??
  7. ??
  8. ????public?Item(String?text,?int?id)?{??
  9. ????????this.text?=?text;??
  10. ????????this.id?=?id;??
  11. ????}??
  12. ??
  13. ????@Override??
  14. ????public?String?toString()?{??
  15. ????????return?text;??
  16. ????}??
  17. }??

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

?

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

2.PopMenu的使用

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

?

[java]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. /*?
  2. ?*?Date:?14-9-2?
  3. ?*?Project:?Access-Control-V2?
  4. ?*/??
  5. package?cn.irains.access.v2.usermanager;??
  6. ??
  7. import?android.content.Context;??
  8. import?android.view.LayoutInflater;??
  9. import?android.view.View;??
  10. import?android.widget.ArrayAdapter;??
  11. import?android.widget.ListView;??
  12. ??
  13. import?java.util.ArrayList;??
  14. ??
  15. import?cn.irains.access.v2.R;??
  16. import?cn.irains.access.v2.common.PopMenu;??
  17. ??
  18. /**?
  19. ?*?Author:?msdx?([email protected])?
  20. ?*?Time:?14-9-2?上午8:56?
  21. ?*/??
  22. public?class?UserMenu?extends?PopMenu?{??
  23. ????public?UserMenu(Context?context)?{??
  24. ????????super(context);??
  25. ????}??
  26. ??
  27. ????@Override??
  28. ????protected?ListView?findListView(View?view)?{??
  29. ????????return?(ListView)?view.findViewById(R.id.menu_listview);??
  30. ????}??
  31. ??
  32. ????@Override??
  33. ????protected?View?onCreateView(Context?context)?{??
  34. ????????View?view?=?LayoutInflater.from(context).inflate(R.layout.menu_user,?null);??
  35. ????????return?view;??
  36. ????}??
  37. ??
  38. ????@Override??
  39. ????protected?ArrayAdapter<Item>?onCreateAdapter(Context?context,?ArrayList<Item>?items)?{??
  40. ????????return?new?ArrayAdapter<Item>(context,?R.layout.item_menu_user,?items);??
  41. ????}??
  42. }??


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

?

?

[java]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. /*?
  2. ?*?Date:?14-9-2?
  3. ?*?Project:?Access-Control-V2?
  4. ?*/??
  5. package?cn.irains.access.v2.common;??
  6. ??
  7. import?android.content.Context;??
  8. import?android.util.AttributeSet;??
  9. import?android.view.View;??
  10. import?android.widget.ListView;??
  11. ??
  12. /**?
  13. ?*?宽度适配内容的ListView.?
  14. ?*?Author:?msdx?([email protected])?
  15. ?*?Time:?14-9-2?下午5:14?
  16. ?*/??
  17. public?class?WrapWidthListView?extends?ListView?{??
  18. ??
  19. ????public?WrapWidthListView(Context?context)?{??
  20. ????????super(context);??
  21. ????}??
  22. ??
  23. ????public?WrapWidthListView(Context?context,?AttributeSet?attrs)?{??
  24. ????????super(context,?attrs);??
  25. ????}??
  26. ??
  27. ????public?WrapWidthListView(Context?context,?AttributeSet?attrs,?int?defStyle)?{??
  28. ????????super(context,?attrs,?defStyle);??
  29. ????}??
  30. ??
  31. ????@Override??
  32. ????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{??
  33. ????????int?width?=?0;??
  34. ????????for?(int?i?=?0;?i?<?getChildCount();?i++)?{??
  35. ????????????View?child?=?getChildAt(i);??
  36. ????????????child.measure(MeasureSpec.makeMeasureSpec(0,?MeasureSpec.UNSPECIFIED),?heightMeasureSpec);??
  37. ????????????int?w?=?child.getMeasuredWidth();??
  38. ????????????if?(w?>?width)?width?=?w;??
  39. ????????}??
  40. ??
  41. ????????widthMeasureSpec?=?MeasureSpec.makeMeasureSpec(width?+?getPaddingLeft()?+?getPaddingRight(),?MeasureSpec.EXACTLY);??
  42. ??
  43. ????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);??
  44. ????}??
  45. }??


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

[html]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. <?xml?version="1.0"?encoding="utf-8"?>??
  2. <LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
  3. ??????????????android:layout_width="wrap_content"??
  4. ??????????????android:paddingRight="@dimen/pop_menu_padding"??
  5. ??????????????android:orientation="vertical"??
  6. ??????????????android:layout_height="wrap_content">??
  7. ??
  8. ????<ImageView??
  9. ????????android:id="@+id/head"??
  10. ????????android:src="@drawable/pop_menu_head"??
  11. ????????android:layout_gravity="right"??
  12. ????????android:layout_width="wrap_content"??
  13. ????????android:contentDescription="@null"??
  14. ????????android:layout_marginRight="18dp"??
  15. ????????android:layout_height="wrap_content"/>??
  16. ??
  17. ????<cn.irains.access.v2.common.WrapWidthListView??
  18. ????????android:id="@+id/menu_listview"??
  19. ????????android:padding="6dp"??
  20. ????????android:focusable="true"??
  21. ????????android:layout_width="wrap_content"??
  22. ????????android:background="@drawable/pop_menu_body"??
  23. ????????android:cacheColorHint="@android:color/transparent"??
  24. ????????android:layout_height="wrap_content"/>??
  25. </LinearLayout>??

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

?

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

?

[html]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. <?xml?version="1.0"?encoding="utf-8"?>??
  2. ??
  3. <TextView?xmlns:android="http://schemas.android.com/apk/res/android"??
  4. ??????????android:textSize="@dimen/text_size_large"??
  5. ??????????android:textColor="@color/text_choice_selector"??
  6. ??????????android:background="@drawable/item_choice_selector"??
  7. ??????????android:gravity="center"??
  8. ??????????android:layout_gravity="center"??
  9. ??????????android:paddingLeft="20dp"??
  10. ??????????android:paddingTop="6dp"??
  11. ??????????android:singleLine="true"??
  12. ??????????android:paddingBottom="6dp"??
  13. ??????????android:paddingRight="20dp"??
  14. ??????????android:layout_width="wrap_content"??
  15. ??????????android:layout_height="wrap_content"/>??


使用代码如下:

[java]?view plaincopy在CODE上查看代码片派生到我的代码片
?
  1. ????private?static?final?int?USER_SEARCH?=?0;??
  2. ????private?static?final?int?USER_ADD?=?1;??
  3. ????private?UserMenu?mMenu;??
  4. ??????
  5. ??????
  6. ????private?void?initMenu()?{??
  7. ????????mMenu?=?new?UserMenu(context);??
  8. ????????mMenu.addItem(R.string.user_search,?USER_SEARCH);??
  9. ????????mMenu.addItem(R.string.user_add,?USER_ADD);??
  10. ????????mMenu.setOnItemSelectedListener(new?PopMenu.OnItemSelectedListener()?{??
  11. ????????????@Override??
  12. ????????????public?void?selected(View?view,?PopMenu.Item?item,?int?position)?{??
  13. ????????????????switch?(item.id)?{??
  14. ????????????????????case?USER_SEARCH:??
  15. ????????????????????????startActivity(new?Intent(getActivity(),?UserSearchActivity.class));??
  16. ????????????????????????break;??
  17. ????????????????????case?USER_ADD:??
  18. ????????????????????????startActivity(new?Intent(getActivity(),?UserAddActivity.class));??
  19. ????????????????????????break;??
  20. ????????????????}??
  21. ????????????}??
  22. ????????});??
  23. ????}??

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

?

?

  相关解决方案