歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android PopupWindow重寫系統菜單

Android PopupWindow重寫系統菜單

日期:2017/3/1 11:01:03   编辑:Linux編程

個人感覺系統的菜單不是很好看,所以每次在應用添加菜單的時候總是自己用PopupWindow重寫一個菜單,於是乎,寫多了,自已想把它整理出來,以後可以重用。今天貼出來,與大家共享,寫的不好的地方請指正。另外效率及擴展性方面還不夠好,只是將平常用到的總結在一起,僅供參考。

先上圖:



下面貼代碼:

下面是整個菜單,主要是控制菜單的顯示和消失,另外對文字和圖片(大小最好都一樣,否則最後效果有點難看)分別做了適配,其中對文字的長度進行了處理。相對於前一篇文章android PopupWindow模擬Windows開始菜單顯示消失效果(http://www.linuxidc.com/Linux/2011-11/47440.htm)又介紹了PopupWindow的一些用法。另外還對菜單的高度的值進行了修正,因為菜單裡面是用GridView進行適配的,如果GridView的高度比整個菜單的高度小那麼就會出現滑動,很不好看,這裡要注意一下,分別用H屏的和W屏的模擬器用圖片、文字、圖片+文字測試過,都沒有問題。大家有什麼更好的方法可以在下面留言。

Android PopupWindow重寫系統菜單demo下載:

免費下載地址在 http://linux.linuxidc.com/

用戶名與密碼都是www.linuxidc.com

具體下載目錄在 /pub/Android源碼集錦/2011年/11月/Android PopupWindow重寫系統菜單/

  1. package com.jacp.menu.view;
  2. import java.util.ArrayList;
  3. import android.content.Context;
  4. import android.content.res.Resources;
  5. import android.graphics.Bitmap;
  6. import android.graphics.BitmapFactory;
  7. import android.graphics.Color;
  8. import android.graphics.Paint;
  9. import android.graphics.Point;
  10. import android.graphics.Rect;
  11. import android.util.DisplayMetrics;
  12. import android.view.Gravity;
  13. import android.view.KeyEvent;
  14. import android.view.MotionEvent;
  15. import android.view.View;
  16. import android.view.View.OnKeyListener;
  17. import android.view.View.OnTouchListener;
  18. import android.view.ViewGroup;
  19. import android.widget.AdapterView;
  20. import android.widget.AdapterView.OnItemClickListener;
  21. import android.widget.GridView;
  22. import android.widget.LinearLayout;
  23. import android.widget.PopupWindow;
  24. /**
  25. * 自定義菜單
  26. * @author [email protected]
  27. *
  28. */
  29. public class MenuView
  30. {
  31. private PopupWindow mPopup;
  32. private Context mContext;
  33. /**
  34. * 圖片資源
  35. */
  36. private int[] mImgRes = new int[0];
  37. /**
  38. * 文字資源
  39. */
  40. private String[] mTexts = new String[0];
  41. /**
  42. * 菜單背景
  43. */
  44. private int mBg;
  45. /**
  46. * 菜單顯示消失的動畫
  47. */
  48. private int mAnimStyle;
  49. /**
  50. * 文字大小
  51. */
  52. private float mTxtSize = -1;
  53. /**
  54. * 文字顏色
  55. */
  56. private int mTxtColor = -1;
  57. /**
  58. * 文本相對圖片的對齊方式
  59. */
  60. private int mAlign = MenuItem.TEXT_BOTTOM;
  61. /**
  62. * 菜單項選中的效果
  63. */
  64. private int mSelector = -1;
  65. /**
  66. * 菜單的寬
  67. */
  68. private int mWidth;
  69. /**
  70. * 菜單的高
  71. */
  72. private int mHeight;
  73. /**
  74. * 存放菜單項
  75. */
  76. private GridView mGridView;
  77. /**
  78. * 設置文字的最大長度,超過則會以"..."替代
  79. */
  80. private int mMaxStrLength = 4;
  81. /**
  82. * 菜單項點擊事件
  83. */
  84. private OnMenuItemClickListener mListener;
  85. /**
  86. * 是否對過長字符串采取優化
  87. */
  88. private boolean mIsOptimizeTxt = true;
  89. /**
  90. * 保存菜單項
  91. */
  92. private ArrayList<MenuItem> mMenuItems = new ArrayList<MenuItem>();
  93. public MenuView(Context context)
  94. {
  95. if (null == context)
  96. {
  97. throw new IllegalArgumentException();
  98. }
  99. mContext = context;
  100. }
  101. /**
  102. * 設置圖片資源
  103. * @param imgRes
  104. */
  105. public void setImageRes(int[] imgRes)
  106. {
  107. if (null != imgRes)
  108. {
  109. mImgRes = imgRes;
  110. }
  111. }
  112. /**
  113. * 設置菜單背景
  114. * @param bgRes
  115. */
  116. public void setBackgroundResource(int bgRes)
  117. {
  118. mBg = bgRes;
  119. }
  120. /**
  121. * 設置菜單項的文字
  122. * @param txtRes 資源數組
  123. */
  124. public void setText(int[] txtRes)
  125. {
  126. if (null == txtRes)
  127. {
  128. return;
  129. }
  130. final Resources res = mContext.getResources();
  131. final int length = txtRes.length;
  132. mTexts = new String[length];
  133. for (int i = 0; i < length; i++)
  134. {
  135. mTexts[i] = res.getString(txtRes[i]);
  136. }
  137. }
  138. /**
  139. * 設置菜單項的文字
  140. * @param txtRes
  141. */
  142. public void setText(String[] texts)
  143. {
  144. mTexts = texts;
  145. }
  146. /**
  147. * 設置文字大小
  148. * @param txtSize
  149. */
  150. public void setTextSize(float txtSize)
  151. {
  152. mTxtSize = txtSize;
  153. }
  154. /**
  155. * 設置文字顏色
  156. * @param color
  157. */
  158. public void setTextColor(int color)
  159. {
  160. mTxtColor = color;
  161. }
  162. /**
  163. * 設置文本相對圖片的對齊方式
  164. * @param align
  165. */
  166. public void setTextAlign(int align)
  167. {
  168. mAlign = align;
  169. }
  170. /**
  171. * 允許文本的最大長度
  172. * @param length
  173. */
  174. public void setMaxTextLength(int length)
  175. {
  176. mMaxStrLength = length;
  177. }
  178. /**
  179. * 設置是否對過長文本進行優化
  180. * @param isOptimize
  181. */
  182. public void isOptimizeText(boolean isOptimize)
  183. {
  184. mIsOptimizeTxt = isOptimize;
  185. }
  186. /**
  187. * 設置菜單動畫
  188. * @param animStyle
  189. */
  190. public void setAnimStyle(int animStyle)
  191. {
  192. mAnimStyle = animStyle;
  193. }
  194. /**
  195. * 設置菜單的寬度
  196. * @param width
  197. */
  198. public void setWidth(int width)
  199. {
  200. mWidth = width;
  201. }
  202. /**
  203. * 設置菜單的高度
  204. * @param height
  205. */
  206. public void setHeight(int height)
  207. {
  208. mHeight = height;
  209. }
  210. /**
  211. * 設置菜單被項被選中的效果
  212. * @param selector
  213. */
  214. public void setSelector(int selector)
  215. {
  216. mSelector = selector;
  217. }
  218. /**
  219. * 設置裝載菜單內容的載體
  220. * @param view
  221. */
  222. public void setMenuConentView(GridView view)
  223. {
  224. mGridView = view;
  225. }
  226. /**
  227. * 顯示菜單
  228. * @return 顯示成功返回true, 失敗返回false
  229. */
  230. public boolean show()
  231. {
  232. if (hide())
  233. {
  234. return false;
  235. }
  236. final Context context = mContext;
  237. final int length = mImgRes.length;
  238. final int txtLength = mTexts.length;
  239. Point point = new Point();
  240. if (length != 0 && txtLength != 0)
  241. {
  242. Point p1 = getTextMaxDimenstion(mTexts);
  243. Point p2 = getImageMaxDimension(mImgRes);
  244. switch (mAlign)
  245. {
  246. case MenuItem.TEXT_BOTTOM:
  247. case MenuItem.TEXT_TOP:
  248. point.x = Math.max(p1.x, p2.x);
  249. point.y = p1.y + p2.y;
  250. break;
  251. case MenuItem.TEXT_LEFT:
  252. case MenuItem.TEXT_RIGHT:
  253. point.x = p1.x + p2.x;
  254. point.y = Math.max(p1.y, p2.y);
  255. break;
  256. }
  257. }
  258. else
  259. {
  260. if (length != 0)
  261. {
  262. point = getImageMaxDimension(mImgRes);
  263. }
  264. else if (txtLength != 0)
  265. {
  266. point = getTextMaxDimenstion(mTexts);
  267. }
  268. }
  269. DisplayMetrics metrics = context.getResources().getDisplayMetrics();
  270. int width = mWidth == 0 ? metrics.widthPixels : mWidth;
  271. float density = metrics.density;
  272. int imgWidth = point.x;
  273. int height = point.y;
  274. // 除去5dp的間距一行所能擺放圖片的個數
  275. int columns = (int) ((width - 5 * density) / (imgWidth + 5 * density));
  276. int leng = length != 0 ? length : txtLength;
  277. int rows = columns == 0 ? 0 : leng / columns;
  278. if (columns * rows < leng)
  279. {
  280. rows += 1;
  281. }
  282. final LinearLayout layout = initLayout(context);
  283. GridView gridView = mGridView;
  284. if (null == gridView)
  285. {
  286. gridView = getContentView(context, columns);
  287. }
  288. else
  289. {
  290. setContentViewListener(gridView);
  291. }
  292. layout.addView(gridView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
  293. // TODO 對高度進行修正
  294. int h = 0;
  295. if (mAlign == MenuItem.TEXT_LEFT || mAlign == MenuItem.TEXT_RIGHT)
  296. {
  297. h = (int) (height * rows + 5 * density);
  298. }
  299. else if (mAlign == MenuItem.TEXT_BOTTOM || mAlign == MenuItem.TEXT_TOP)
  300. {
  301. h = (int) ((height + 5 * density) * rows);
  302. }
  303. if (txtLength != 0)
  304. {
  305. h += 6 * density;
  306. }
  307. mPopup = new PopupWindow(context);
  308. mPopup.setWidth(width);
  309. mPopup.setHeight(mHeight == 0 ? h : mHeight);
  310. mPopup.setContentView(layout);
  311. mPopup.setFocusable(true);
  312. mPopup.setOutsideTouchable(true);
  313. mPopup.setTouchable(true);
  314. // 設置背景為null,就不會出現黑色背景,按返回鍵PopupWindow就會消失
  315. mPopup.setBackgroundDrawable(null);
  316. if (mAnimStyle != 0)
  317. {
  318. mPopup.setAnimationStyle(mAnimStyle);
  319. }
  320. mPopup.showAtLocation(layout, Gravity.BOTTOM | Gravity.CENTER, 0, 0);
  321. return true;
  322. }
  323. private LinearLayout initLayout(Context context)
  324. {
  325. LinearLayout layout = new LinearLayout(context);
  326. layout.setOrientation(LinearLayout.VERTICAL);
  327. layout.setFadingEdgeLength(0);
  328. layout.setGravity(Gravity.CENTER);
  329. layout.setOnTouchListener(new OnTouchListener()
  330. {
  331. @Override
  332. public boolean onTouch(View v, MotionEvent event)
  333. {
  334. if (event.getAction() == MotionEvent.ACTION_DOWN)
  335. {
  336. hide();
  337. }
  338. return false;
  339. }
  340. });
  341. return layout;
  342. }
  343. /**
  344. * 初始數據,將數據加載到對應的View中
  345. */
  346. private void initData()
  347. {
  348. MenuItem item = new MenuItem(mContext);
  349. item.setTextAlign(mAlign);
  350. item.setTextColor(mTxtColor);
  351. item.setTextSize(mTxtColor);
  352. int txtLength = mTexts.length;
  353. int imgLength = mImgRes.length;
  354. if (txtLength != 0 && imgLength != 0) // 圖片和文字同時存在的情況
  355. {
  356. for (int i = 0; i < imgLength; i++)
  357. {
  358. MenuItem menuItem = new MenuItem(mContext, item);
  359. menuItem.setImageRes(mImgRes[i]);
  360. menuItem.setText(mTexts[i]);
  361. mMenuItems.add(menuItem);
  362. }
  363. }
  364. else
  365. {
  366. if (txtLength != 0) // 只有文字的情況
  367. {
  368. for (int i = 0; i < txtLength; i++)
  369. {
  370. MenuItem menuItem = new MenuItem(mContext, item);
  371. menuItem.setText(mTexts[i]);
  372. mMenuItems.add(menuItem);
  373. }
  374. }
  375. else if (imgLength != 0) // 只有圖片的情況
  376. {
  377. for (int i = 0; i < imgLength; i++)
  378. {
  379. MenuItem menuItem = new MenuItem(mContext, item);
  380. menuItem.setImageRes(mImgRes[i]);
  381. mMenuItems.add(menuItem);
  382. }
  383. }
  384. }
  385. }
  386. /**
  387. * 初始化菜單內容組件
  388. * @param context
  389. * @param columns 菜單的列數
  390. * @return
  391. */
  392. private GridView getContentView(Context context, int columns)
  393. {
  394. if (mMenuItems.isEmpty())
  395. {
  396. initData();
  397. }
  398. if (null != mGridView)
  399. {
  400. return mGridView;
  401. }
  402. GridView gridView = new GridView(context);
  403. gridView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
  404. gridView.setAdapter(new MenuAdapter(mMenuItems));
  405. gridView.setVerticalSpacing(0);
  406. gridView.setNumColumns(columns);
  407. gridView.setGravity(Gravity.CENTER);
  408. gridView.setVerticalScrollBarEnabled(false);
  409. if (mBg != 0)
  410. {
  411. gridView.setBackgroundResource(mBg);
  412. }
  413. if (mSelector != -1)
  414. {
  415. gridView.setSelector(mSelector);
  416. }
  417. gridView.setHorizontalScrollBarEnabled(false);
  418. setContentViewListener(gridView);
  419. return gridView;
  420. }
  421. /**
  422. * 注冊事件
  423. * @param gridView
  424. */
  425. private void setContentViewListener(GridView gridView)
  426. {
  427. if (null == gridView.getOnItemClickListener())
  428. {
  429. gridView.setOnItemClickListener(new OnItemClickListener()
  430. {
  431. @Override
  432. public void onItemClick(AdapterView<?> parent, View view,
  433. int position, long id)
  434. {
  435. if (null != mListener)
  436. {
  437. mListener.onMenuItemClick(parent, view, position);
  438. }
  439. hide();
  440. }
  441. });
  442. }
  443. gridView.setOnKeyListener(new OnKeyListener()
  444. {
  445. @Override
  446. public boolean onKey(View v, int keyCode, KeyEvent event)
  447. {
  448. if (event.getAction() == KeyEvent.ACTION_DOWN)
  449. {
  450. switch (keyCode)
  451. {
  452. case KeyEvent.KEYCODE_BACK:
  453. case KeyEvent.KEYCODE_MENU:
  454. hide();
  455. break;
  456. }
  457. }
  458. return false;
  459. }
  460. });
  461. }
  462. /**
  463. * 獲取所有圖片的最大的寬和高
  464. * @param imgRes
  465. * @return
  466. */
  467. private Point getImageMaxDimension(int[] imgRes)
  468. {
  469. final Point point = new Point();
  470. for (int i = 0, length = imgRes.length; i < length; i++)
  471. {
  472. Bitmap tmp = BitmapFactory.decodeResource(mContext.getResources(), imgRes[i]);
  473. int width = tmp.getWidth();
  474. int height = tmp.getHeight();
  475. tmp.recycle();
  476. tmp = null;
  477. if (point.x < width)
  478. {
  479. point.x = width;
  480. }
  481. if (point.y < height)
  482. {
  483. point.y = height;
  484. }
  485. }
  486. return point;
  487. }
  488. /**
  489. * 計算文本的最大長度
  490. * @param txts
  491. * @return
  492. */
  493. private Point getTextMaxDimenstion(String[] txts)
  494. {
  495. final Point point = new Point();
  496. final Rect bounds = new Rect();
  497. final Paint paint = new Paint();
  498. float size = mTxtSize != -1 ? mTxtSize : mContext.getResources().getDisplayMetrics().density * 16;
  499. paint.setTextSize(size);
  500. paint.setColor(mTxtColor != -1 ? mTxtColor : Color.BLACK);
  501. if (mIsOptimizeTxt) // 對文字長度進行優化
  502. {
  503. for (int i = 0, length = txts.length; i < length; i++)
  504. {
  505. String str = txts[i];
  506. if (null == str)
  507. {
  508. str = "";
  509. }
  510. else if (str.length() > mMaxStrLength)
  511. {
  512. // 對字符串長度進行控制
  513. str = new StringBuilder().append(str.substring(0, mMaxStrLength)).append("...").toString();
  514. }
  515. txts[i] = str;
  516. paint.getTextBounds(str, 0, str.length(), bounds);
  517. compareDimension(point, bounds.width(), bounds.height());
  518. }
  519. }
  520. else // 對文字長度不做優化,此時要注意圖片和文字同時存在時最終寬度的問題
  521. {
  522. for (int i = 0, length = txts.length; i < length; i++)
  523. {
  524. String str = txts[i];
  525. if (null == str)
  526. {
  527. str = "";
  528. }
  529. txts[i] = str;
  530. paint.getTextBounds(str, 0, str.length(), bounds);
  531. compareDimension(point, bounds.width(), bounds.height());
  532. }
  533. }
  534. return point;
  535. }
  536. /**
  537. * 比較並改變最大尺寸
  538. * @param point 保存最大尺寸的對象
  539. * @param width 寬
  540. * @param height 高
  541. */
  542. private void compareDimension(Point point, int width, int height)
  543. {
  544. if (point.x < width)
  545. {
  546. point.x = width;
  547. }
  548. if (point.y < height)
  549. {
  550. point.y = height;
  551. }
  552. }
  553. /**
  554. * 隱藏菜單
  555. * @return 隱藏成功返回true,失敗返回false
  556. */
  557. public boolean hide()
  558. {
  559. if (null != mPopup && mPopup.isShowing())
  560. {
  561. mPopup.dismiss();
  562. mPopup = null;
  563. if (null != mListener)
  564. {
  565. mListener.hideMenu();
  566. }
  567. return true;
  568. }
  569. return false;
  570. }
  571. public void dismiss()
  572. {
  573. mMenuItems.clear();
  574. mGridView = null;
  575. mTexts = new String[0];
  576. mImgRes = new int[0];
  577. mWidth = 0;
  578. mHeight = 0;
  579. }
  580. /**
  581. * 設置菜單項被選中監聽器
  582. * @param listener
  583. */
  584. public void setOnMenuItemClickListener(OnMenuItemClickListener listener)
  585. {
  586. mListener = listener;
  587. }
  588. /**
  589. * 菜單項目選中監聽器
  590. * @author maylian.mei
  591. *
  592. */
  593. public interface OnMenuItemClickListener
  594. {
  595. /**
  596. * 菜單項被點擊的會調用的方法
  597. * @param parent
  598. * @param view
  599. * @param position
  600. */
  601. public void onMenuItemClick(AdapterView<?> parent, View view, int position);
  602. /**
  603. * 菜單隱藏會調用的方法
  604. */
  605. public void hideMenu();
  606. }
  607. }
Copyright © Linux教程網 All Rights Reserved