你好,游客 登录 注册 搜索
背景:
阅读新闻

Android PopupWindow重写系统菜单

[日期:2011-11-17] 来源:Linux社区  作者:maylian7700 [字体: ]

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

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款
第 1 楼
* ggg 发表于 2012/8/29 16:34:49
ggg