转自:http://www.eefocus.com/bbs/article_867_188597.html
个人感觉系统的菜单不是很好看,所以每次在应用添加菜单的时候总是自己用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重写系统菜单/ package com.jacp.menu.view; import java.util.ArrayList; import Android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnKeyListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.GridView; import android.widget.LinearLayout; import android.widget.PopupWindow; /** * 自定义菜单 * @author maylian7700@126.com * */ public class MenuView { private PopupWindow mPopup; private Context mContext; /** * 图片资源 */ private int[] mImgRes = new int[0]; /** * 文字资源 */ private String[] mTexts = new String[0]; /** * 菜单背景 */ private int mBg; /** * 菜单显示消失的动画 */ private int mAnimStyle; /** * 文字大小 */ private float mTxtSize = -1; /** * 文字颜色 */ private int mTxtColor = -1; /** * 文本相对图片的对齐方式 */ private int mAlign = MenuItem.TEXT_BOTTOM; /** * 菜单项选中的效果 */ private int mSelector = -1; /** * 菜单的宽 */ private int mWidth; /** * 菜单的高 */ private int mHeight; /** * 存放菜单项 */ private GridView mGridView; /** * 设置文字的最大长度,超过则会以"..."替代 */ private int mMaxStrLength = 4; /** * 菜单项点击事件 */ private OnMenuItemClickListener mListener; /** * 是否对过长字符串采取优化 */ private boolean mIsOptimizeTxt = true; /** * 保存菜单项 */ private ArrayList<MenuItem> mMenuItems = new ArrayList<MenuItem>(); public MenuView(Context context) { if (null == context) { throw new IllegalArgumentException(); } mContext = context; } /** * 设置图片资源 * @param imgRes */ public void setImageRes(int[] imgRes) { if (null != imgRes) { mImgRes = imgRes; } } /** * 设置菜单背景 * @param bgRes */ public void setBackgroundResource(int bgRes) { mBg = bgRes; } /** * 设置菜单项的文字 * @param txtRes 资源数组 */ public void setText(int[] txtRes) { if (null == txtRes) { return; } final Resources res = mContext.getResources(); final int length = txtRes.length; mTexts = new String[length]; for (int i = 0; i < length; i++) { mTexts[i] = res.getString(txtRes[i]); } } /** * 设置菜单项的文字 * @param txtRes */ public void setText(String[] texts) { mTexts = texts; } /** * 设置文字大小 * @param txtSize */ public void setTextSize(float txtSize) { mTxtSize = txtSize; } /** * 设置文字颜色 * @param color */ public void setTextColor(int color) { mTxtColor = color; } /** * 设置文本相对图片的对齐方式 * @param align */ public void setTextAlign(int align) { mAlign = align; } /** * 允许文本的最大长度 * @param length */ public void setMaxTextLength(int length) { mMaxStrLength = length; } /** * 设置是否对过长文本进行优化 * @param isOptimize */ public void isOptimizeText(boolean isOptimize) { mIsOptimizeTxt = isOptimize; } /** * 设置菜单动画 * @param animStyle */ public void setAnimStyle(int animStyle) { mAnimStyle = animStyle; }
/** * 设置菜单的宽度 * @param width */ public void setWidth(int width) { mWidth = width; } /** * 设置菜单的高度 * @param height */ public void setHeight(int height) { mHeight = height; } /** * 设置菜单被项被选中的效果 * @param selector */ public void setSelector(int selector) { mSelector = selector; } /** * 设置装载菜单内容的载体 * @param view */ public void setMenuConentView(GridView view) { mGridView = view; } /** * 显示菜单 * @return 显示成功返回true, 失败返回false */ public boolean show() { if (hide()) { return false; } final Context context = mContext; final int length = mImgRes.length; final int txtLength = mTexts.length; Point point = new Point(); if (length != 0 && txtLength != 0) { Point p1 = getTextMaxDimenstion(mTexts); Point p2 = getImageMaxDimension(mImgRes); switch (mAlign) { case MenuItem.TEXT_BOTTOM: case MenuItem.TEXT_TOP: point.x = Math.max(p1.x, p2.x); point.y = p1.y + p2.y; break; case MenuItem.TEXT_LEFT: case MenuItem.TEXT_RIGHT: point.x = p1.x + p2.x; point.y = Math.max(p1.y, p2.y); break; } } else { if (length != 0) { point = getImageMaxDimension(mImgRes); } else if (txtLength != 0) { point = getTextMaxDimenstion(mTexts); } } DisplayMetrics metrics = context.getResources().getDisplayMetrics(); int width = mWidth == 0 ? metrics.widthPixels : mWidth; float density = metrics.density; int imgWidth = point.x; int height = point.y; // 除去5dp的间距一行所能摆放图片的个数 int columns = (int) ((width - 5 * density) / (imgWidth + 5 * density)); int leng = length != 0 ? length : txtLength; int rows = columns == 0 ? 0 : leng / columns; if (columns * rows < leng) { rows += 1; } final LinearLayout layout = initLayout(context); GridView gridView = mGridView; if (null == gridView) { gridView = getContentView(context, columns); } else { setContentViewListener(gridView); } layout.addView(gridView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // TODO 对高度进行修正 int h = 0; if (mAlign == MenuItem.TEXT_LEFT || mAlign == MenuItem.TEXT_RIGHT) { h = (int) (height * rows + 5 * density); } else if (mAlign == MenuItem.TEXT_BOTTOM || mAlign == MenuItem.TEXT_TOP) { h = (int) ((height + 5 * density) * rows); } if (txtLength != 0) { h += 6 * density; } mPopup = new PopupWindow(context); mPopup.setWidth(width); mPopup.setHeight(mHeight == 0 ? h : mHeight); mPopup.setContentView(layout); mPopup.setFocusable(true); mPopup.setOutsideTouchable(true); mPopup.setTouchable(true); // 设置背景为null,就不会出现黑色背景,按返回键PopupWindow就会消失 mPopup.setBackgroundDrawable(null); if (mAnimStyle != 0) { mPopup.setAnimationStyle(mAnimStyle); } mPopup.showAtLocation(layout, Gravity.BOTTOM | Gravity.CENTER, 0, 0); return true; } private LinearLayout initLayout(Context context) { LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); layout.setFadingEdgeLength(0); layout.setGravity(Gravity.CENTER); layout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { hide(); } return false; } }); return layout; } /** * 初始数据,将数据加载到对应的View中 */ private void initData() { MenuItem item = new MenuItem(mContext); item.setTextAlign(mAlign); item.setTextColor(mTxtColor); item.setTextSize(mTxtColor); int txtLength = mTexts.length; int imgLength = mImgRes.length; if (txtLength != 0 && imgLength != 0) // 图片和文字同时存在的情况 { for (int i = 0; i < imgLength; i++) { MenuItem menuItem = new MenuItem(mContext, item); menuItem.setImageRes(mImgRes[i]); menuItem.setText(mTexts[i]); mMenuItems.add(menuItem); } } else { if (txtLength != 0) // 只有文字的情况 { for (int i = 0; i < txtLength; i++) { MenuItem menuItem = new MenuItem(mContext, item); menuItem.setText(mTexts[i]); mMenuItems.add(menuItem); } } else if (imgLength != 0) // 只有图片的情况 { for (int i = 0; i < imgLength; i++) { MenuItem menuItem = new MenuItem(mContext, item); menuItem.setImageRes(mImgRes[i]); mMenuItems.add(menuItem); } } } } /** * 初始化菜单内容组件 * @param context * @param columns 菜单的列数 * @return */ private GridView getContentView(Context context, int columns) { if (mMenuItems.isEmpty()) { initData(); } if (null != mGridView) { return mGridView; } GridView gridView = new GridView(context); gridView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); gridView.setAdapter(new MenuAdapter(mMenuItems)); gridView.setVerticalSpacing(0); gridView.setNumColumns(columns); gridView.setGravity(Gravity.CENTER); gridView.setVerticalScrollBarEnabled(false); if (mBg != 0) { gridView.setBackgroundResource(mBg); } if (mSelector != -1) { gridView.setSelector(mSelector); } gridView.setHorizontalScrollBarEnabled(false); setContentViewListener(gridView); return gridView; } /** * 注册事件 * @param gridView */ private void setContentViewListener(GridView gridView) { if (null == gridView.getOnItemClickListener()) { gridView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (null != mListener) { mListener.onMenuItemClick(parent, view, position); } hide(); } }); } gridView.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_MENU: hide(); break; } } return false; } }); } /** * 获取所有图片的最大的宽和高 * @param imgRes * @return */ private Point getImageMaxDimension(int[] imgRes) { final Point point = new Point(); for (int i = 0, length = imgRes.length; i < length; i++) { Bitmap tmp = BitmapFactory.decodeResource(mContext.getResources(), imgRes[i]); int width = tmp.getWidth(); int height = tmp.getHeight(); tmp.recycle(); tmp = null; if (point.x < width) { point.x = width; } if (point.y < height) { point.y = height; } } return point; } /** * 计算文本的最大长度 * @param txts * @return */ private Point getTextMaxDimenstion(String[] txts) { final Point point = new Point(); final Rect bounds = new Rect(); final Paint paint = new Paint(); float size = mTxtSize != -1 ? mTxtSize : mContext.getResources().getDisplayMetrics().density * 16; paint.setTextSize(size); paint.setColor(mTxtColor != -1 ? mTxtColor : Color.BLACK); if (mIsOptimizeTxt) // 对文字长度进行优化 { for (int i = 0, length = txts.length; i < length; i++) { String str = txts[i]; if (null == str) { str = ""; } else if (str.length() > mMaxStrLength) { // 对字符串长度进行控制 str = new StringBuilder().append(str.substring(0, mMaxStrLength)).append("...").toString(); } txts[i] = str; paint.getTextBounds(str, 0, str.length(), bounds); compareDimension(point, bounds.width(), bounds.height()); } } else // 对文字长度不做优化,此时要注意图片和文字同时存在时最终宽度的问题 { for (int i = 0, length = txts.length; i < length; i++) { String str = txts[i]; if (null == str) { str = ""; } txts[i] = str; paint.getTextBounds(str, 0, str.length(), bounds); compareDimension(point, bounds.width(), bounds.height()); } } return point; } /** * 比较并改变最大尺寸 * @param point 保存最大尺寸的对象 * @param width 宽 * @param height 高 */ private void compareDimension(Point point, int width, int height) { if (point.x < width) { point.x = width; } if (point.y < height) { point.y = height; } } /** * 隐藏菜单 * @return 隐藏成功返回true,失败返回false */ public boolean hide() { if (null != mPopup && mPopup.isShowing()) { mPopup.dismiss(); mPopup = null; if (null != mListener) { mListener.hideMenu(); } return true; } return false; } public void dismiss() { mMenuItems.clear(); mGridView = null; mTexts = new String[0]; mImgRes = new int[0]; mWidth = 0; mHeight = 0; } /** * 设置菜单项被选中监听器 * @param listener */ public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { mListener = listener; } /** * 菜单项目选中监听器 * @author maylian.mei * */ public interface OnMenuItemClickListener { /** * 菜单项被点击的会调用的方法 * @param parent * @param view * @param position */ public void onMenuItemClick(AdapterView parent, View view, int position); /** * 菜单隐藏会调用的方法 */ public void hideMenu(); } }