EL : Compatible PopupMenu Class
大约两周前, 在修复EL的 2.3.x的兼容问题时, 有一个是PopupMenu相关的, 这个类在API9才有的, 那么在2.3.x平台上就没法用了. 为了能够兼容, 当时用了PopupWindow.
这两天考虑到PopupMenu后面还是很需要的, 且以前的封装仅仅是为了实现播放快速跳转的兼容功能, 没考虑再使用, 所以昨晚重新封装了下, 这样更加方便和实用了.
实现的方式还是在2.3.x平台下使用PopupWindow来实现PopupMenu的功能, 但这次只需要在xml下定义个Menu资源就好了, 不再需要另行准备个PopupWindow所需的Layout资源了, 方便再次使用.
4.x平台下直接就使用Menu资源初始PopupMenu了, 而使用PopupWindow时, 就需要先解析Menu资源文件, 得到所需的Item的ID和Title, 然后使用这些数据初始PopupWindow的View.
封装的ELPopupMenu代码如下:
1
package jie.android.el.view;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import org.xmlpull.v1.XmlPullParserException;
6
7 import jie.android.el.R;
8 import jie.android.el.utils.Utils;
9 import android.content.Context;
10 import android.content.res.XmlResourceParser;
11 import android.graphics.drawable.BitmapDrawable;
12 import android.util.AttributeSet;
13 import android.view.Gravity;
14 import android.view.Menu;
15 import android.view.MenuItem;
16 import android.view.View;
17 import android.view.View.OnClickListener;
18 import android.view.ViewGroup.LayoutParams;
19 import android.widget.Button;
20 import android.widget.LinearLayout;
21 import android.widget.PopupMenu;
22 import android.widget.PopupMenu.OnMenuItemClickListener;
23 import android.widget.PopupWindow;
24
25 public class ELPopupMenu {
26
27 private class MenuData {
28 public int id;
29 public String title;
30
31 public MenuData( int id, String title) {
32 this.id = id;
33 this.title = title;
34 }
35 }
36
37 public interface OnItemClickListener {
38 public void OnClick( int item);
39 }
40
41 private Context context;
42 private int resMenuId;
43 private View parent;
44 private Object popup;
45 private OnItemClickListener listener;
46
47 public ELPopupMenu(Context context, int resMenuId, View parent, OnItemClickListener listener) {
48 this.context = context;
49 this.resMenuId = resMenuId;
50 this.parent = parent;
51 this.listener = listener;
52
53 make();
54 }
55
56 private void make() {
57 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
58 popup = makePopupMenu(context, resMenuId, parent);
59 } else {
60 popup = makePopupWindow(context, resMenuId, parent);
61 }
62 }
63
64 private Object makePopupMenu(Context context, int resId, View parent) {
65 PopupMenu pm = new PopupMenu(context, parent);
66 pm.getMenuInflater().inflate(resId, pm.getMenu());
67 pm.setOnMenuItemClickListener( new OnMenuItemClickListener() {
68
69 @Override
70 public boolean onMenuItemClick(MenuItem menu) {
71 if (listener != null) {
72 listener.OnClick(menu.getItemId());
73 return true;
74 }
75 return false;
76 }
77 });
78
79 return pm;
80 }
81
82 private int getAttributeIntValue(Context context, String resId) {
83 return Integer.valueOf(resId.substring(1));
84 }
85 private String getAttributeStringValue(Context context, String resId) {
86 int id = Integer.valueOf(resId.substring(1));
87 return context.getString(id);
88 }
89
90
91 private ArrayList<MenuData> analyseMenuResource(Context context, int resId) {
92
93 ArrayList<MenuData> menu = new ArrayList<MenuData>();
94
95 XmlResourceParser parser = context.getResources().getXml(resId);
96
97 try {
98 int event = parser.next();
99 while (event != XmlResourceParser.END_DOCUMENT) {
100 if (event == XmlResourceParser.START_TAG) {
101 if (parser.getName().equals("item")) {
102 menu.add( new MenuData(getAttributeIntValue(context, parser.getAttributeValue(Utils.NS_ANDROID, "id")),
103 getAttributeStringValue(context, parser.getAttributeValue(Utils.NS_ANDROID, "title"))));
104 }
105 }
106 event = parser.next();
107 }
108
109 } catch (XmlPullParserException e) {
110 // TODO Auto-generated catch block
111 e.printStackTrace();
112 } catch (IOException e) {
113 // TODO Auto-generated catch block
114 e.printStackTrace();
115 }
116
117 return menu;
118 }
119
120 private Object makePopupWindow(Context context, int resId, View parent) {
121
122 ArrayList<MenuData> menu = analyseMenuResource(context, resId);
123 if (menu.size() == 0) {
124 return null;
125 }
126
127 OnClickListener l = new OnClickListener() {
128 @Override
129 public void onClick(View v) {
130 if (listener != null) {
131 listener.OnClick(v.getId());
132 }
133 if (popup != null) {
134 ((PopupWindow)popup).dismiss();
135 }
136 }
137 };
138
139 AttributeSet attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "LinearLayout", R.id.linearLayout1);
140 LinearLayout ll = new LinearLayout(context, attrs);
141
142 int pos = 0;
143 for (MenuData data : menu) {
144 if (pos == 0) {
145 attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "Button", R.id.button1);
146 } else if (pos == menu.size() -1) {
147 attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "Button", R.id.button3);
148 } else {
149 attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "Button", R.id.button2);
150 }
151
152 Button btn = new Button(context, attrs);
153 btn.setId(data.id);
154 btn.setText(data.title);
155 btn.setOnClickListener(l);
156
157 ll.addView(btn, ll.generateLayoutParams(attrs));
158
159 ++ pos;
160 }
161
162 PopupWindow pw = new PopupWindow(ll);
163 // pw.setWidth(250);
164 pw.setWidth(LayoutParams.WRAP_CONTENT);
165 pw.setHeight(LayoutParams.WRAP_CONTENT);
166 pw.setFocusable( true);
167 pw.setOutsideTouchable( true);
168 pw.setBackgroundDrawable( new BitmapDrawable());
169
170 return pw;
171 }
172
173 public void show() {
174 show(Gravity.LEFT | Gravity.BOTTOM, 16, 64);
175 }
176
177 public void show( int gravity, int x, int y) {
178 if (popup != null) {
179 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
180 ((PopupMenu)popup).show();
181 } else {
182 ((PopupWindow)popup).showAtLocation(parent, gravity, x, y);
183 }
184 }
185 }
186
187 public void setItemEnabled( int item, boolean enabled) {
188 if (popup != null) {
189 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
190 Menu menu = ((PopupMenu)popup).getMenu();
191 MenuItem sub = menu.findItem(item);
192 if (sub != null) {
193 sub.setEnabled(enabled);
194 }
195 } else {
196 View p = ((PopupWindow)popup).getContentView();
197 View v = p.findViewById(item);
198 v.setEnabled(enabled);
199 }
200 }
201 }
202
203 }
204
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import org.xmlpull.v1.XmlPullParserException;
6
7 import jie.android.el.R;
8 import jie.android.el.utils.Utils;
9 import android.content.Context;
10 import android.content.res.XmlResourceParser;
11 import android.graphics.drawable.BitmapDrawable;
12 import android.util.AttributeSet;
13 import android.view.Gravity;
14 import android.view.Menu;
15 import android.view.MenuItem;
16 import android.view.View;
17 import android.view.View.OnClickListener;
18 import android.view.ViewGroup.LayoutParams;
19 import android.widget.Button;
20 import android.widget.LinearLayout;
21 import android.widget.PopupMenu;
22 import android.widget.PopupMenu.OnMenuItemClickListener;
23 import android.widget.PopupWindow;
24
25 public class ELPopupMenu {
26
27 private class MenuData {
28 public int id;
29 public String title;
30
31 public MenuData( int id, String title) {
32 this.id = id;
33 this.title = title;
34 }
35 }
36
37 public interface OnItemClickListener {
38 public void OnClick( int item);
39 }
40
41 private Context context;
42 private int resMenuId;
43 private View parent;
44 private Object popup;
45 private OnItemClickListener listener;
46
47 public ELPopupMenu(Context context, int resMenuId, View parent, OnItemClickListener listener) {
48 this.context = context;
49 this.resMenuId = resMenuId;
50 this.parent = parent;
51 this.listener = listener;
52
53 make();
54 }
55
56 private void make() {
57 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
58 popup = makePopupMenu(context, resMenuId, parent);
59 } else {
60 popup = makePopupWindow(context, resMenuId, parent);
61 }
62 }
63
64 private Object makePopupMenu(Context context, int resId, View parent) {
65 PopupMenu pm = new PopupMenu(context, parent);
66 pm.getMenuInflater().inflate(resId, pm.getMenu());
67 pm.setOnMenuItemClickListener( new OnMenuItemClickListener() {
68
69 @Override
70 public boolean onMenuItemClick(MenuItem menu) {
71 if (listener != null) {
72 listener.OnClick(menu.getItemId());
73 return true;
74 }
75 return false;
76 }
77 });
78
79 return pm;
80 }
81
82 private int getAttributeIntValue(Context context, String resId) {
83 return Integer.valueOf(resId.substring(1));
84 }
85 private String getAttributeStringValue(Context context, String resId) {
86 int id = Integer.valueOf(resId.substring(1));
87 return context.getString(id);
88 }
89
90
91 private ArrayList<MenuData> analyseMenuResource(Context context, int resId) {
92
93 ArrayList<MenuData> menu = new ArrayList<MenuData>();
94
95 XmlResourceParser parser = context.getResources().getXml(resId);
96
97 try {
98 int event = parser.next();
99 while (event != XmlResourceParser.END_DOCUMENT) {
100 if (event == XmlResourceParser.START_TAG) {
101 if (parser.getName().equals("item")) {
102 menu.add( new MenuData(getAttributeIntValue(context, parser.getAttributeValue(Utils.NS_ANDROID, "id")),
103 getAttributeStringValue(context, parser.getAttributeValue(Utils.NS_ANDROID, "title"))));
104 }
105 }
106 event = parser.next();
107 }
108
109 } catch (XmlPullParserException e) {
110 // TODO Auto-generated catch block
111 e.printStackTrace();
112 } catch (IOException e) {
113 // TODO Auto-generated catch block
114 e.printStackTrace();
115 }
116
117 return menu;
118 }
119
120 private Object makePopupWindow(Context context, int resId, View parent) {
121
122 ArrayList<MenuData> menu = analyseMenuResource(context, resId);
123 if (menu.size() == 0) {
124 return null;
125 }
126
127 OnClickListener l = new OnClickListener() {
128 @Override
129 public void onClick(View v) {
130 if (listener != null) {
131 listener.OnClick(v.getId());
132 }
133 if (popup != null) {
134 ((PopupWindow)popup).dismiss();
135 }
136 }
137 };
138
139 AttributeSet attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "LinearLayout", R.id.linearLayout1);
140 LinearLayout ll = new LinearLayout(context, attrs);
141
142 int pos = 0;
143 for (MenuData data : menu) {
144 if (pos == 0) {
145 attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "Button", R.id.button1);
146 } else if (pos == menu.size() -1) {
147 attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "Button", R.id.button3);
148 } else {
149 attrs = Utils.getAttributeSet(context, R.layout.layout_popmenu_window_template, "Button", R.id.button2);
150 }
151
152 Button btn = new Button(context, attrs);
153 btn.setId(data.id);
154 btn.setText(data.title);
155 btn.setOnClickListener(l);
156
157 ll.addView(btn, ll.generateLayoutParams(attrs));
158
159 ++ pos;
160 }
161
162 PopupWindow pw = new PopupWindow(ll);
163 // pw.setWidth(250);
164 pw.setWidth(LayoutParams.WRAP_CONTENT);
165 pw.setHeight(LayoutParams.WRAP_CONTENT);
166 pw.setFocusable( true);
167 pw.setOutsideTouchable( true);
168 pw.setBackgroundDrawable( new BitmapDrawable());
169
170 return pw;
171 }
172
173 public void show() {
174 show(Gravity.LEFT | Gravity.BOTTOM, 16, 64);
175 }
176
177 public void show( int gravity, int x, int y) {
178 if (popup != null) {
179 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
180 ((PopupMenu)popup).show();
181 } else {
182 ((PopupWindow)popup).showAtLocation(parent, gravity, x, y);
183 }
184 }
185 }
186
187 public void setItemEnabled( int item, boolean enabled) {
188 if (popup != null) {
189 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
190 Menu menu = ((PopupMenu)popup).getMenu();
191 MenuItem sub = menu.findItem(item);
192 if (sub != null) {
193 sub.setEnabled(enabled);
194 }
195 } else {
196 View p = ((PopupWindow)popup).getContentView();
197 View v = p.findViewById(item);
198 v.setEnabled(enabled);
199 }
200 }
201 }
202
203 }
204
初始PopupWindow的View的资源模板文件如下:
1
<?
xml version="1.0" encoding="utf-8"
?>
2 < LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"
3 android:id ="@+id/linearLayout1"
4 android:layout_width ="match_parent"
5 android:layout_height ="wrap_content"
6 android:layout_marginLeft ="16dp"
7 android:layout_marginRight ="16dp"
8 android:background ="@android:color/darker_gray"
9 android:orientation ="vertical" >
10
11 < Button
12 android:id ="@+id/button1"
13 android:layout_width ="match_parent"
14 android:layout_height ="wrap_content"
15 android:layout_marginTop ="4dp" />
16
17 < Button
18 android:id ="@+id/button2"
19 android:layout_width ="match_parent"
20 android:layout_height ="wrap_content" />
21
22 < Button
23 android:id ="@+id/button3"
24 android:layout_width ="match_parent"
25 android:layout_height ="wrap_content"
26 android:layout_marginBottom ="0dp" />
27 </ LinearLayout >
2 < LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"
3 android:id ="@+id/linearLayout1"
4 android:layout_width ="match_parent"
5 android:layout_height ="wrap_content"
6 android:layout_marginLeft ="16dp"
7 android:layout_marginRight ="16dp"
8 android:background ="@android:color/darker_gray"
9 android:orientation ="vertical" >
10
11 < Button
12 android:id ="@+id/button1"
13 android:layout_width ="match_parent"
14 android:layout_height ="wrap_content"
15 android:layout_marginTop ="4dp" />
16
17 < Button
18 android:id ="@+id/button2"
19 android:layout_width ="match_parent"
20 android:layout_height ="wrap_content" />
21
22 < Button
23 android:id ="@+id/button3"
24 android:layout_width ="match_parent"
25 android:layout_height ="wrap_content"
26 android:layout_marginBottom ="0dp" />
27 </ LinearLayout >