android PopupWindow重写系统菜单

来源:互联网 发布:淘宝拖鞋棉 编辑:程序博客网 时间:2024/06/06 10:27

个人感觉系统的菜单不是很好看,所以每次在应用添加菜单的时候总是自己用PopupWindow重写一个菜单,于是乎,写多了,自已想把它整理出来,以后可以重用。今天贴出来,与大家共享,写的不好的地方请指正。另外效率及扩展性方面还不够好,只是将平常用到的总结在一起,仅供参考。

先上图:



下面贴代码:

下面是整个菜单,主要是控制菜单的显示和消失,另外对文字和图片(大小最好都一样,否则最后效果有点难看)分别做了适配,其中对文字的长度进行了处理。相对于前一篇文章android PopupWindow模拟Windows开始菜单显示消失效果又介绍了PopupWindow的一些用法。另外还对菜单的高度的值进行了修正,因为菜单里面是用GridView进行适配的,如果GridView的高度比整个菜单的高度小那么就会出现滑动,很不好看,这里要注意一下,分别用H屏的和W屏的模拟器用图片、文字、图片+文字测试过,都没有问题。大家有什么更好的方法可以在下面留言。

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(){@Overridepublic 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(){@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id){if (null != mListener){mListener.onMenuItemClick(parent, view, position);}hide();}});}gridView.setOnKeyListener(new OnKeyListener(){@Overridepublic 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();}}

下面是自定义的菜单项,扩展性不是很好,只是把常用的写进去了。

package com.jacp.menu.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Point;import android.text.TextUtils;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;/** * 菜单项 * @author maylian7700@126.com * */public class MenuItem{private LinearLayout mLayout;/** * 文本在图片的下方显示 */public static final int TEXT_BOTTOM = 0x0;/** * 文本在图片的上方显示 */public static final int TEXT_TOP = 0x1;/** * 文本在图片的左边显示 */public static final int TEXT_LEFT = 0x2;/** * 文本在图片的右边显示 */public static final int TEXT_RIGHT = 0x3;/** * 文本的对齐方式 */private int mAlign = TEXT_BOTTOM;/** * 文本 */private String mText;/** * 文本颜色 */private int mTextColor;/** * 文本大小 */private int mTextSize;/** * 图片的资源ID */private int mImgRes;private Context mContext;public MenuItem(Context context){this(context, 0, null, 0, 0, TEXT_BOTTOM);}public MenuItem(Context context, int imgRes, String text, int textColor, int textSize, int align){mImgRes = imgRes;mText = text;mTextColor = textColor;mTextSize = textSize;mAlign = align;mContext = context;}public MenuItem(Context context, MenuItem item){this(context, 0, null, item.getTextColor(), item.getTextSize(), item.getAlign());}/** * 初始化菜单项 * @param context */private void initlayout(){Context context = mContext;mLayout = new LinearLayout(context);mLayout.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));mLayout.setGravity(Gravity.CENTER);TextView textView = getTextView(context);ImageView imageView = getImageView(context);if (null != textView && null != imageView){Point point = getImageDimension(context, mImgRes);switch (mAlign){case TEXT_BOTTOM: // 文本居下mLayout.setOrientation(LinearLayout.VERTICAL);mLayout.addView(imageView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, point.y));mLayout.addView(textView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));break;case TEXT_TOP:// 文本居上mLayout.setOrientation(LinearLayout.VERTICAL);mLayout.addView(textView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));mLayout.addView(imageView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, point.y));break;case TEXT_LEFT:// 文本居左mLayout.setOrientation(LinearLayout.HORIZONTAL);mLayout.addView(textView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));mLayout.addView(imageView, new ViewGroup.LayoutParams(point.x, ViewGroup.LayoutParams.MATCH_PARENT));break;case TEXT_RIGHT:// 文本居右mLayout.setOrientation(LinearLayout.HORIZONTAL);mLayout.addView(imageView, new ViewGroup.LayoutParams(point.x, ViewGroup.LayoutParams.MATCH_PARENT));mLayout.addView(textView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));break;}}else{if (null != textView){mLayout.addView(textView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));}else if (null != imageView){Point point = getImageDimension(context, mImgRes);mLayout.addView(imageView, new ViewGroup.LayoutParams(point.x, point.y));}}}/** * 获取图片的尺寸 * @param context * @param res * @return */private Point getImageDimension(Context context, int res){Point point = new Point();Bitmap bm = BitmapFactory.decodeResource(context.getResources(), mImgRes);point.x = bm.getWidth();point.y = bm.getHeight();bm.recycle();bm = null;return point;}/** * 设置图片资源 * @param imgRes */public void setImageRes(int imgRes){mImgRes = imgRes;}/** * 设置文本大小 * @param size */public void setTextSize(int size){mTextSize = size;}/** * 设置文本颜色 * @param color */public void setTextColor(int color){mTextColor = color;}/** * 设置文本内容 * @param text */public void setText(String text){mText = text;}/** * 设置文本的对齐方式 * @param align */public void setTextAlign(int align){mAlign = align;}public String getText(){return mText;}public int getTextColor(){return mTextColor;}public int getTextSize(){return mTextSize;}/** * 创建TextView * @param context * @return */private TextView getTextView(Context context){if (TextUtils.isEmpty(mText)){return null;}TextView txtView = new TextView(context);if (mTextColor != 0){txtView.setTextColor(mTextColor);}if (mTextSize != 0){txtView.setTextSize(mTextSize);}txtView.setText(mText);txtView.setGravity(Gravity.CENTER);return txtView;}public int getAlign(){return mAlign;}/** * 创建ImageView * @param context * @return */private ImageView getImageView(Context context){if (mImgRes == 0){return null;}ImageView view = new ImageView(context);view.setImageResource(mImgRes);return view;}/** * 获取菜单项目的内容 * @return */public View getView(){initlayout();return mLayout;}}

最后一个是把数据适配进去:

package com.jacp.menu.view;import java.util.ArrayList;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;/** *  * @author maylian7700@126.com * */public class MenuAdapter extends BaseAdapter{private ArrayList<MenuItem> mMenuItems;public MenuAdapter(ArrayList<MenuItem> menuItems){mMenuItems = menuItems;}@Overridepublic int getCount(){return mMenuItems.size();}@Overridepublic Object getItem(int position){return position;}@Overridepublic long getItemId(int position){return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent){if (null == convertView){convertView = mMenuItems.get(position).getView();}return convertView;}}


另外还有动画配置,本来打算全部用代码搞定,结果发现给PopupWindow设置动画用代码搞不定,下面是给PopupWindow添加动画的配置:

在res\values\styles.xml文件中添加以下代码:

<resources>    <style name="popup_in_out" parent="android:Animation">        <item name="android:windowEnterAnimation">@anim/anim_down_in</item>        <item name="android:windowExitAnimation">@anim/anim_down_out</item>    </style></resources>

再在res\anim目录新建两个文件anim_down_in.xml和anim_down_out.xml,分别对应以下代码:

anim_down_in.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"><translate android:fromYDelta="100%" android:toYDelta="0%"android:duration="300"></translate></set>

anim_down_out.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"><translate android:fromYDelta="0%" android:toYDelta="100%"android:duration="300"></translate></set>


下面是一个测试的Activity,如果要设置菜单背景透明,只要将R.drawable.bg做成透明图片即可:

package com.jacp.menu;import android.app.Activity;import android.os.Bundle;import android.view.KeyEvent;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import com.jacp.menu.view.MenuView;import com.jacp.menu.view.MenuView.OnMenuItemClickListener;public class MyBottomMenuActivityActivity extends Activity implements OnClickListener {    private final int[] mImgRes = { R.drawable.cancel, R.drawable.cancel, R.drawable.cancel, R.drawable.cancel, R.drawable.cancel, R.drawable.cancel, R.drawable.cancel, R.drawable.cancel, R.drawable.cancel };private MenuView mMenuView;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);                findViewById(R.id.show_menu).setOnClickListener(this);    }        private void showMenu()    {    if (null == mMenuView)    {    mMenuView = new MenuView(this);//    mMenuView.setWidth(300);    mMenuView.setImageRes(mImgRes);    mMenuView.setAnimStyle(R.style.popup_in_out);    mMenuView.setBackgroundResource(R.drawable.bg);    mMenuView.setText(getResources().getStringArray(R.array.menu_text));    mMenuView.setOnMenuItemClickListener(new OnMenuItemClickListener()    {@Overridepublic void onMenuItemClick(AdapterView<?> parent, View view,int position){}@Overridepublic void hideMenu(){mMenuView.dismiss();mMenuView = null;}        });    }    mMenuView.show();    }    @Overridepublic void onClick(View v){switch (v.getId()){case R.id.show_menu:showMenu();break;}}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event){if (event.getAction() == KeyEvent.ACTION_DOWN){switch (keyCode){case KeyEvent.KEYCODE_MENU:showMenu();break;}}return super.onKeyDown(keyCode, event);}}

Demo下载地址:http://download.csdn.net/detail/maylian7700/3792526

原创粉丝点击