本文标签: Android提高
用过UCWEB-Android版的人都应该对其特殊的menu有印象,把menu做成Tab-Menu(支持分页的Menu),可以容纳比Android传统的menu更丰富的内容(Android的menu超过6项则缩略在[更多]里),本文参考网上的例子(作者:CoffeeCole,email:longkefan@foxmail.com),对例子进行简化以及封装,使其作为一个复合控件融入自己的framework。
先来看看本文程序运行的效果:
TabMenu本身就是一个PopupWindow,PopupWindow上面放了两个GridView,第一个GridView就是分页标签,位于PopupWindow的顶部,第二个GridView是菜单,位于PopupWindow的主体。为了实现PopupWindow的弹出/退出的动画效果,本文使用了以下代码:
在工程的res文件夹里添加anim子目录,再新建文件popup_enter.xml:
新建文件popup_exit.xml:
在工程的values文件夹里新建文件popup_animation.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="PopupAnimation" parent="android:Animation">
<item name="android:windowEnterAnimation">@anim/popup_enter</item>
<item name="android:windowExitAnimation">@anim/popup_exit</item>
</style>
</resources>
main.xml的源码如下:
TabMenu的封装类TabMenu.java的源码如下:
mLayout = new LinearLayout(context);
mLayout.setOrientation(LinearLayout.VERTICAL);
//标题选项栏
gvTitle = new GridView(context);
gvTitle.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
gvTitle.setNumColumns(titleAdapter.getCount());
gvTitle.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
gvTitle.setVerticalSpacing(1);
gvTitle.setHorizontalSpacing(1);
gvTitle.setGravity(Gravity.CENTER);
gvTitle.setOnItemClickListener(titleClick);
gvTitle.setAdapter(titleAdapter);
gvTitle.setSelector(new ColorDrawable(Color.TRANSPARENT));//选中的时候为透明色
this.titleAdapter=titleAdapter;
//子选项栏
gvBody = new GridView(context);
gvBody.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
gvBody.setSelector(new ColorDrawable(Color.TRANSPARENT));//选中的时候为透明色
gvBody.setNumColumns(4);
gvBody.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
gvBody.setVerticalSpacing(10);
gvBody.setHorizontalSpacing(10);
gvBody.setPadding(10, 10, 10, 10);
gvBody.setGravity(Gravity.CENTER);
gvBody.setOnItemClickListener(bodyClick);
mLayout.addView(gvTitle);
mLayout.addView(gvBody);
//设置默认项
this.setContentView(mLayout);
this.setWidth(LayoutParams.FILL_PARENT);
this.setHeight(LayoutParams.WRAP_CONTENT);
this.setBackgroundDrawable(new ColorDrawable(colorBgTabMenu));// 设置TabMenu菜单背景
this.setAnimationStyle(aniTabMenu);
this.setFocusable(true);// menu菜单获得焦点 如果没有获得焦点menu菜单中的控件事件无法响应
}
public void SetTitleSelect(int index)
{
gvTitle.setSelection(index);
this.titleAdapter.SetFocus(index);
}
public void SetBodySelect(int index,int colorSelBody)
{
int count=gvBody.getChildCount();
for(int i=0;i<count;i++)
{
if(i!=index)
((LinearLayout)gvBody.getChildAt(i)).setBackgroundColor(Color.TRANSPARENT);
}
((LinearLayout)gvBody.getChildAt(index)).setBackgroundColor(colorSelBody);
}
public void SetBodyAdapter(MenuBodyAdapter bodyAdapter)
{
gvBody.setAdapter(bodyAdapter);
}
/**
* 自定义Adapter,TabMenu的每个分页的主体
*
*/
static public class MenuBodyAdapter extends BaseAdapter {
private Context mContext;
private int fontColor,fontSize;
private String[] texts;
private int[] resID;
/**
* 设置TabMenu的分页主体
* @param context 调用方的上下文
* @param texts 按钮集合的字符串数组
* @param resID 按钮集合的图标资源数组
* @param fontSize 按钮字体大小
* @param color 按钮字体颜色
*/
public MenuBodyAdapter(Context context, String[] texts,int[] resID, int fontSize,int fontColor)
{
this.mContext = context;
this.fontColor = fontColor;
this.texts = texts;
this.fontSize=fontSize;
this.resID=resID;
}
public int getCount() {
return texts.length;
}
public Object getItem(int position) {
return makeMenyBody(position);
}
public long getItemId(int position) {
return position;
}
private LinearLayout makeMenyBody(int position)
{
LinearLayout result=new LinearLayout(this.mContext);
result.setOrientation(LinearLayout.VERTICAL);
result.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.CENTER_VERTICAL);
result.setPadding(10, 10, 10, 10);
TextView text = new TextView(this.mContext);
text.setText(texts[position]);
text.setTextSize(fontSize);
text.setTextColor(fontColor);
text.setGravity(Gravity.CENTER);
text.setPadding(5, 5, 5, 5);
ImageView img=new ImageView(this.mContext);
img.setBackgroundResource(resID[position]);
result.addView(img,new LinearLayout.LayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT)));
result.addView(text);
return result;
}
public View getView(int position, View convertView, ViewGroup parent) {
return makeMenyBody(position);
}
}
/**
* 自定义Adapter,TabMenu的分页标签部分
*
*/
static public class MenuTitleAdapter extends BaseAdapter {
private Context mContext;
private int fontColor,unselcolor,selcolor;
private TextView[] title;
/**
* 设置TabMenu的title
* @param context 调用方的上下文
* @param titles 分页标签的字符串数组
* @param fontSize 字体大小
* @param fontcolor 字体颜色
* @param unselcolor 未选中项的背景色
* @param selcolor 选中项的背景色
*/
public MenuTitleAdapter(Context context, String[] titles, int fontSize,
int fontcolor,int unselcolor,int selcolor) {
this.mContext = context;
this.fontColor = fontcolor;
this.unselcolor = unselcolor;
this.selcolor=selcolor;
this.title = new TextView[titles.length];
for (int i = 0; i < titles.length; i++) {
title[i] = new TextView(mContext);
title[i].setText(titles[i]);
title[i].setTextSize(fontSize);
title[i].setTextColor(fontColor);
title[i].setGravity(Gravity.CENTER);
title[i].setPadding(10, 10, 10, 10);
}
}
public int getCount() {
return title.length;
}
public Object getItem(int position) {
return title[position];
}
public long getItemId(int position) {
return title[position].getId();
}
/**
* 设置选中的效果
*/
private void SetFocus(int index)
{
for(int i=0;i<title.length;i++)
{
if(i!=index)
{
title[i].setBackgroundDrawable(new ColorDrawable(unselcolor));//设置没选中的颜色
title[i].setTextColor(fontColor);//设置没选中项的字体颜色
}
}
title[index].setBackgroundColor(0x00);//设置选中项的颜色
title[index].setTextColor(selcolor);//设置选中项的字体颜色
}
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = title[position];
} else {
v = convertView;
}
return v;
}
}
}
testTabMenu介绍了数据的定义以及TabMenu的使用,源码如下:
bodyAdapter[1]=new TabMenu.MenuBodyAdapter(this,new String[] { "设置1", "设置2",
"设置3"}, new int[] { R.drawable.menu_edit,
R.drawable.menu_delete, R.drawable.menu_fullscreen},13, 0xFFFFFFFF);
bodyAdapter[2]=new TabMenu.MenuBodyAdapter(this,new String[] { "工具1", "工具2",
"工具3", "工具4" }, new int[] { R.drawable.menu_copy,
R.drawable.menu_cut, R.drawable.menu_normalmode,
R.drawable.menu_quit },13, 0xFFFFFFFF);
tabMenu=new TabMenu(this,
new TitleClickEvent(),
new BodyClickEvent(),
titleAdapter,
0x55123456,//TabMenu的背景颜色
R.style.PopupAnimation);//出现与消失的动画
tabMenu.update();
tabMenu.SetTitleSelect(0);
tabMenu.SetBodyAdapter(bodyAdapter[0]);
}
class TitleClickEvent implements OnItemClickListener{
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
selTitle=arg2;
tabMenu.SetTitleSelect(arg2);
tabMenu.SetBodyAdapter(bodyAdapter[arg2]);
}
}
class BodyClickEvent implements OnItemClickListener{
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
tabMenu.SetBodySelect(arg2,Color.GRAY);
String str="第"+String.valueOf(selTitle)+"栏/n/r"
+"第"+String.valueOf(arg2)+"项";
Toast.makeText(testTabMenu.this, str, 500).show();
}
}
@Override
/**
* 创建MENU
*/
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("menu");// 必须创建一项
return super.onCreateOptionsMenu(menu);
}
@Override
/**
* 拦截MENU
*/
public boolean onMenuOpened(int featureId, Menu menu) {
if (tabMenu != null) {
if (tabMenu.isShowing())
tabMenu.dismiss();
else {
tabMenu.showAtLocation(findViewById(R.id.LinearLayout01),
Gravity.BOTTOM, 0, 0);
}
}
return false;// 返回为true 则显示系统menu
}
}
声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: Android提高十八篇之自定义Menu(TabMenu)
Linux系统与内核学习群:194051772
WP建站技术学习交流群:194062106
[e01]没想到android的menu还可以这样,佩服!
2011-01-29 09:32[e01]
2011-01-29 09:33一如既往的顶虫哥[e01]
2011-01-29 11:55有源代码下载就更好了
2011-01-29 12:04顶
2011-01-29 13:16太给力了!!!!!!!
2011-01-30 08:47[e01]
2011-01-30 11:50回复 wyymaomi:
2011-01-30 21:51整个工程的源代码都在这篇文章里了….
[e01][e01][e01][e01][e01][e01][e01]
2011-01-31 10:03[e01][e01][e01][e01][e01][e01]
2011-01-31 10:44[e01]
2011-01-31 15:09学习了!
2011-02-01 13:10收藏了,将来会用得着
2011-02-03 11:54[e03][e01][e01][e01][e01][e01][e01]
2011-02-04 22:07[e01]
2011-02-06 22:20真的很不错,以后多关注楼主的博客
2011-02-08 19:42厉害,学习了,一直苦于研究这个功能的实现,但其效果不伦不类,今看楼主所写,实醍醐灌顶,在此感谢楼主分享![e03]
2011-02-09 14:38不错,有一定深度的文章;
2011-02-09 16:09先签个名
2011-02-11 11:37没有资源文件 [e08]
2011-02-16 11:08哈哈,之前做Me经常看楼楼主的文章~
2011-02-22 15:37现在做Android也要来提高提高~[e01][e01][e01]
[e01][e03]又进一步优化里,不错不错。
2011-03-13 19:05想知道你哪个gif动画怎么做的?
参考价值很高…[e01]
2011-03-14 15:45回复 ameyume:这个简单。[e04]
2011-03-23 14:10学习..要搞一个Gallery3D弹出式菜单的效果..正好看到GV的文章,非常感谢。[e06]
2011-03-23 14:11[e01]
2011-03-28 14:46学习,东西很好,但是有个疑问,popup_animation.xml 这个文件怎么用的啊? 不是很理解。
2011-03-31 18:18[e03][e03][e01][e01]
2011-04-02 09:01[e01]
2011-04-06 14:38太棒了[e01]
2011-04-07 14:41不错,学习了~[e01][e01]
2011-04-08 19:55C初级程序员拜读!目前为止还看不懂,不在一个层次.
2011-04-11 20:11终于看懂了。。太牛叉了[e03][e03]
2011-04-19 20:44太不错了,一直关注gv哥的文章,观点新颖,同样的效果不一样的方法![e03]
2011-04-20 16:22牛[e03][e03][e03][e03][e03][e03][e03][e03]
2011-04-26 11:42我顶,你太牛了~~[e01][e03]
2011-05-07 09:27神作,Gridview真是很强大啊
2011-05-18 10:58[e01][e01][e01][e01][e01][e01][e01][e01][e01]
2011-05-19 21:08[e07]
2011-06-07 14:24报错 而且DDMS的log不显示错误消息
膜拜啊 膜拜啊 ~~~~
在EOE看到前辈发的源码 在这里修改以后更加给力了~~~
2011-06-24 22:21呵呵,这有个bug 有时候,popupwindow会出现假死的现象。呵呵
2011-07-15 18:03很实用谢谢
2011-07-20 10:48写的不错,但还满足不了我的要求呀。我想让其中的一项不可点击,不知道楼主有没有好办法,
2011-07-22 15:05[reply]w550114195[/reply]
2011-07-23 09:21setEnable(false)
博主空间的下载栏目有这个项目打包好的源程序下载么?如果自己建工程再复制粘贴的话感觉还是麻烦了些。。。
2011-07-30 09:57我测试时,onMenuOpened方法只调用一次,没法在按menu键时,使popupwindow关闭
2011-08-03 11:40[reply]SnowGeneral[/reply]
2011-08-06 19:25恩 我 也发现了 这个 ,无法用menu 关闭pop
LZ这个方法也是参考别人的。
奇怪….我的就只显示menu…
2011-08-11 09:58把你的代码全贴进去也是一样..
主页面配置文件:[code=html]
<?xml version="1.0" encoding="utf-8"?>
<TabHost android:id="@android:id/tabhost" android:tag="tabhost" android:layout_width="fill_parent"
android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:id="@+id/linear"
..
</TabHost>
[/code]
代码:tabMenu.showAtLocation(findViewById(android.R.id.tabhost),
Gravity.BOTTOM, 0, 0);
[reply]bxxasn[/reply]
2011-08-11 10:15代码在onMenuOpened函数中..
就只显示了一个menu…自定义menu.没有显示出来.
非常精彩的自定义组合控件实例, 虽然单个知识点很多人都会,但是整合在一起使用才是真正的运用,学习了,多谢
2011-08-11 13:57没想到android的menu还可以这样,佩服!
2011-08-20 11:29new int[] { R.drawable.menu_test,R.drawable.menu_bookmark}
里面的文件没有。楼主能否将工程给我发一份。 邮箱hgh411@126.com. 谢谢
2011-08-27 17:03[reply]viptmd[/reply]
2011-09-03 11:21这个需要改POPUPWINDOW的源码才可以,这里有:http://www.roman10.net/?p=551
博主,我借鉴你的源码,在原有的基础上,改动了ANDROID本身的POPUPWINDOW, 使得MENU KEY PRESS可以用,希望你不会介意。
2011-09-03 11:26TabMenu.java and Main.java完全是照搬你的,我在博文中也有提到,在最后也提供了你原文的链接。我的文章可以在这里找到:http://www.roman10.net/?p=551
[reply]romanDIP[/reply]
2011-09-03 15:03呵呵,谢谢你,看你了的 文章!解决,哈哈!
[reply]romanDIP[/reply]
2011-09-04 13:53在文章里写明引用就OK了
[reply]hgh411[/reply]
2011-09-04 13:54可以随便用其他图片代替的,本文主要演示代码和功能,图片就随便了
支持,楼主确实比较厉害,这篇文章,我比较佩服楼主的控件全部都是使用代码写的,这个自定义控件的包装比较好,会学习引用的,谢谢你,祝开心。。。
2011-09-16 13:59[reply]bawanglb[/reply]
2011-09-17 10:57写了十几年代码,慢慢就养成这个习惯
請問,單獨設定其中一個選項的enable該如何設置?
2011-09-21 05:33除了顶!我知道如何形容我此时的心情!楼主 很强大,学习了!谢谢楼主的分享
2011-09-28 18:47[reply]wanli_smile[/reply]
2011-09-28 18:55加我微博吧,我微博收听率不高啊
LZ,如果菜单的布局不是代码写的,而是在XML里面的,应该怎么改?我自己在改写的过程中遇到一个问题,MenuTitle显示出来了,但MenuBody一直就显示不出来!
2011-10-11 11:22终于找到了,谢谢楼主分享
2011-10-25 13:35崇拜哦!!!!微薄好是多少呀!!
2011-10-25 16:28[reply]NCxuqiao[/reply]
2011-10-25 19:14http://t.qq.com/hellogv
写的很好 受教了
2011-10-28 15:41有个问题啊。。。 系统的菜单是,点击一下出来,再点一下就消失了。。。 可是你这个看你代码是做了这个处理,但是很明显是不行的
2011-10-28 16:12[reply]xiaobao1026[/reply]
2011-10-29 09:14不行可以改嘛,我这个只是演示基本功能的例子,具体细节还要开发者自己增加的
必须顶啊,我先看看,修改一下,非常感谢
2011-11-25 11:07不错哦
2011-12-14 18:22顶一下 。。。很强大很漂亮的菜单。。。比系统那样添加的漂亮多了。。。
2011-12-19 16:06如何实现上下左右选中效果,改了半天毫无成果,求赐教
2011-12-20 14:31学习了
2012-01-13 10:44楼主,main.xml中如果加了 android:orientation="vertical"好像会出错,不知道这个如何优化好呢?
2012-01-30 14:35[reply]yangjiantong1988[/reply]
2012-01-30 14:55不好意思,后来测试一下又不会报错了!感谢!
[reply]romanDIP[/reply]
2012-01-31 09:28哥们,你发的这个链接是啥网站啊,一直都打不开!
从您的开源~~无私的分享中学到了不少有价值的东西~~~不得不感谢您下~~~~谢谢了~~
2012-02-03 23:03本人在参考了一片文章,试了一下,加两个方法就可以实现点击menu进行关闭了,以下是添加的代码:
在TabMenu类的构造方法里面添加一下代码就可以了
mLayout.setFocusableInTouchMode(true);
mLayout.setOnKeyListener(new android.view.View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if ((keyCode == KeyEvent.KEYCODE_MENU) && (isShowing())) {
dismiss();
return true;
}
return false;
}
});
参考文章:“#友盟杯#教你如何创建类似QQ的android弹出菜单http://www.apkbus.com/android-18034-1-1.html”
这里下源码要积分,就一分不多,不过我有下载了代码,如需代码可以QQ找我:1723501078,如有什么错误也可联系我,谢谢!
谢谢楼主分享代码哦!好人啊……
2012-02-06 22:55试试!
2012-02-22 15:30请问如何在点击选项之后去掉背景阴影的效果,只有点击的时候才显示呢?
2012-03-08 16:50这个效果真的不错
2012-03-12 16:26[reply]bxxasn[/reply]
2012-03-13 16:11在TabHost中我也遇到这种问题,显示不出来自定义的!你解决了吗?怎么解决的?
好高深
2012-03-13 17:08果然强大!!!
2012-03-23 21:34习惯布局文件用XML写,楼主竟然用代码给写出来了,一时间看不懂,仔细揣摩貌似理解了点,甚是欣慰。
但是遇到了一个问题,就是这两个类TabMenu ,TestTabMenu 是否要在manifest中注册?
我注册了,啥也没显示。。。
愁人诶
学习啦,就是不知能不能学懂
2012-04-05 11:22学长 请教一个其他的问题:我在一个页面上实现了上下两个popupwindow 进入页面的时候 点击menu键 两个弹窗同时弹出 但问题是 当我再点击menu时没有了反应。 点击back键的时候 可以消失 但只能一次消失一个 而且打印的log并不现实 。。。
2012-04-10 14:47楼主可否把资源文件提供下啊.
2012-05-29 13:54可以通过语句将这两个分开吗
2012-07-25 14:47我的意思是 嵌套还是那么嵌套 但是我想要的效果是 分页标签在下面 gridmenu在上面
之后 有办法再在屏幕上方加一个textview吗 我自己做的时候分页标签老是把textview挡住 用什么属性可以做到呢
一直是自己在写这个东西,实现和楼主差不多啦,只是没有上面的分页按钮而已,今天才看到楼主的代码,果然比我写的要好
2012-09-13 14:234.0.3无法运行啊,很奇怪!
2012-09-17 09:18[reply]caodaye12121[/reply]
2012-09-22 09:18过奖了,谢谢关注!
学习学习
2012-10-08 11:25谢谢,找了好久终于找到了,您的文章对我帮助很大。
2012-10-11 08:49