Android实现自定义的卫星式菜单(弧形菜单)详解
程序员文章站
2024-03-07 22:08:09
一、前言
android 实现卫星式菜单也叫弧形菜单,主要要做的工作如下:
1.动画的处理
2.自定义viewgroup来实现卫星式菜单view
(1)...
一、前言
android 实现卫星式菜单也叫弧形菜单,主要要做的工作如下:
1.动画的处理
2.自定义viewgroup来实现卫星式菜单view
(1)自定义属性
a. 在attrs.xml中定义属性
b. 在布局中使用自定义属性
c. 在自定义view
中读取布局文件中的自定义属性
(2)onmeasure
测量 child
即测量主按钮以及菜单项
(3)onlayout
布局 child
即布局主按钮以及菜单项
(4)设置主按钮的选择动画
a.为菜单项menuitem
添加平移动画和旋转动画
b.实现菜单项menuitem
的点击动画
卫星式菜单效果截图:
二、实现
上面介绍了原理和效果图,下面来看看卫星菜单类的实现:
1.布局文件的实现
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:xcskin="http://schemas.android.com/apk/res/com.xc.xcskin" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" > <com.xc.xcskin.view.xcarcmenuview android:id="@+id/arcmenu" android:layout_width="150dp" android:layout_height="150dp" android:layout_alignparentbottom="true" android:layout_alignparentleft="true" xcskin:position="left_bottom" xcskin:radius="120dp" > <relativelayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/composer_button" > <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerinparent="true" android:src="@drawable/composer_icn_plus" /> </relativelayout> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_camera" android:tag="camera" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_music" android:tag="music" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_place" android:tag="place" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_sleep" android:tag="sleep" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_thought" android:tag="thought" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_with" android:tag="with" /> </com.xc.xcskin.view.xcarcmenuview> <com.xc.xcskin.view.xcarcmenuview android:id="@+id/arcmenu2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignparentbottom="true" android:layout_alignparentright="true" xcskin:position="right_bottom" xcskin:radius="150dp" > <relativelayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/composer_button" > <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerinparent="true" android:src="@drawable/composer_icn_plus" /> </relativelayout> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_camera" android:tag="camera" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_music" android:tag="music" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_place" android:tag="place" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_sleep" android:tag="sleep" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_thought" android:tag="thought" /> <imageview android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/composer_with" android:tag="with" /> </com.xc.xcskin.view.xcarcmenuview> </relativelayout>
2.卫星菜单类的实现
package com.xc.xcskin.view; import com.xc.xcskin.r; import android.content.context; import android.content.res.typedarray; import android.util.attributeset; import android.util.log; import android.util.typedvalue; import android.view.view; import android.view.view.onclicklistener; import android.view.viewgroup; import android.view.animation.alphaanimation; import android.view.animation.animation; import android.view.animation.animation.animationlistener; import android.view.animation.animationset; import android.view.animation.rotateanimation; import android.view.animation.scaleanimation; import android.view.animation.translateanimation; /** * 卫星式菜单view * @author caizhiming * */ public class xcarcmenuview extends viewgroup implements onclicklistener{ private static final int pos_left_top = 0; private static final int pos_left_bottom = 1; private static final int pos_right_top = 2; private static final int pos_right_bottom = 3; private position mposition = position.right_bottom; private int mradius; private status mstatus = status.close; //主菜的单按钮 private view mcbutton; private onmenuitemclicklistener monmenuitemclicklistener; /** * 菜单的状态枚举类 * @author caizhiming * */ public enum status{ open,close } /** * 菜单的位置枚举类 * @author caizhiming * */ public enum position{ left_top,left_bottom, right_top,right_bottom } /** * 点击子菜单项的回调接口 * @author caizhiming * */ public interface onmenuitemclicklistener { void onclick(view view, int pos); } public void setonmenuitemclicklistener( onmenuitemclicklistener onmenuitemclicklistener) { this.monmenuitemclicklistener = onmenuitemclicklistener; } public xcarcmenuview(context context) { this(context, null); // todo auto-generated constructor stub } public xcarcmenuview(context context, attributeset attrs) { this(context, attrs, 0); // todo auto-generated constructor stub } public xcarcmenuview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); // todo auto-generated constructor stub //获取自定义属性 typedarray a = context.gettheme().obtainstyledattributes(attrs, r.styleable.xcarcmenuview,defstyle,0); int pos = a.getint(r.styleable.xcarcmenuview_position , pos_right_bottom); switch (pos) { case pos_left_top: mposition = position.left_top; break; case pos_left_bottom: mposition = position.left_bottom; break; case pos_right_top: mposition = position.right_top; break; case pos_right_bottom: mposition = position.right_bottom; break; } mradius = (int) a.getdimension(r.styleable.xcarcmenuview_radius, (int) typedvalue.applydimension(typedvalue.complex_unit_dip, 150, getresources().getdisplaymetrics())); log.v("czm", "mposition = " + mposition + ",mradius = "+mradius); a.recycle(); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { // todo auto-generated method stub int count = getchildcount(); for(int i = 0; i < count; i ++){ measurechild(getchildat(i), widthmeasurespec, heightmeasurespec); } super.onmeasure(widthmeasurespec, heightmeasurespec); } @override protected void onlayout(boolean changed, int l, int t, int r, int b) { // todo auto-generated method stub if(changed){ layoutcbutton(); layoutmenuitems(); } } /** * 布局主菜单项 */ private void layoutcbutton() { // todo auto-generated method stub mcbutton = getchildat(0); mcbutton.setonclicklistener(this); int l = 0; int t = 0; int width = mcbutton.getmeasuredwidth(); int height = mcbutton.getmeasuredheight(); switch (mposition) { case left_top: l = 0; t = 0; break; case left_bottom: l = 0; t = getmeasuredheight() - height; break; case right_top: l = getmeasuredwidth() - width; t = 0; break; case right_bottom: l = getmeasuredwidth() - width; t = getmeasuredheight() - height; break; default: break; } mcbutton.layout(l, t, l + width, t + height); } /** * 布局菜单项 */ private void layoutmenuitems() { // todo auto-generated method stub int count = getchildcount(); for (int i = 0; i < count - 1; i++) { view child = getchildat(i + 1); int l = (int) (mradius * math.sin(math.pi / 2 / (count - 2) * i)); int t = (int) (mradius * math.cos(math.pi / 2 / (count - 2) * i)); int width = child.getmeasuredwidth(); int height = child.getmeasuredheight(); // 如果菜单位置在底部 左下,右下 if (mposition == position.left_bottom || mposition == position.right_bottom) { t = getmeasuredheight() - height - t; } // 右上,右下 if (mposition == position.right_top || mposition == position.right_bottom) { l = getmeasuredwidth() - width - l; } child.layout(l, t, l + width, t + height); child.setvisibility(view.gone); } } @override public void onclick(view v) { // todo auto-generated method stub mcbutton = findviewbyid(r.id.id_button); rotatecbutton(v,0,360,300); togglemenu(300); } /** * 切换菜单 */ public void togglemenu(int duration) { // todo auto-generated method stub // 为menuitem添加平移动画和旋转动画 int count = getchildcount(); for (int i = 0; i < count - 1; i++) { final view childview = getchildat(i + 1); childview.setvisibility(view.visible); // end 0 , 0 // start int cl = (int) (mradius * math.sin(math.pi / 2 / (count - 2) * i)); int ct = (int) (mradius * math.cos(math.pi / 2 / (count - 2) * i)); int xflag = 1; int yflag = 1; if (mposition == position.left_top || mposition == position.left_bottom) { xflag = -1; } if (mposition == position.left_top || mposition == position.right_top) { yflag = -1; } animationset animset = new animationset(true); animation trananim = null; // to open if (mstatus == status.close) { trananim = new translateanimation(xflag * cl, 0, yflag * ct, 0); childview.setclickable(true); childview.setfocusable(true); } else // to close { trananim = new translateanimation(0, xflag * cl, 0, yflag * ct); childview.setclickable(false); childview.setfocusable(false); } trananim.setfillafter(true); trananim.setduration(duration); trananim.setstartoffset((i * 100) / count); trananim.setanimationlistener(new animationlistener() { @override public void onanimationstart(animation animation) { } @override public void onanimationrepeat(animation animation) { } @override public void onanimationend(animation animation) { if (mstatus == status.close) { childview.setvisibility(view.gone); } } }); // 旋转动画 rotateanimation rotateanim = new rotateanimation(0, 720, animation.relative_to_self, 0.5f, animation.relative_to_self, 0.5f); rotateanim.setduration(duration); rotateanim.setfillafter(true); animset.addanimation(rotateanim); animset.addanimation(trananim); childview.startanimation(animset); final int pos = i + 1; childview.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { if (monmenuitemclicklistener != null) monmenuitemclicklistener.onclick(childview, pos); menuitemanim(pos - 1); changestatus(); } }); } // 切换菜单状态 changestatus(); } /** * 选择主菜单按钮 * */ private void rotatecbutton(view v, float start, float end, int duration) { // todo auto-generated method stub rotateanimation anim = new rotateanimation(start, end, animation.relative_to_self, 0.5f, animation.relative_to_self, 0.5f); anim.setduration(duration); anim.setfillafter(true); v.startanimation(anim); } /** * 添加menuitem的点击动画 * */ private void menuitemanim(int pos) { for (int i = 0; i < getchildcount() - 1; i++) { view childview = getchildat(i + 1); if (i == pos) { childview.startanimation(scalebiganim(300)); } else { childview.startanimation(scalesmallanim(300)); } childview.setclickable(false); childview.setfocusable(false); } } /** * 为当前点击的item设置变小和透明度增大的动画 * @param duration * @return */ private animation scalesmallanim(int duration) { animationset animationset = new animationset(true); scaleanimation scaleanim = new scaleanimation(1.0f, 0.0f, 1.0f, 0.0f, animation.relative_to_self, 0.5f, animation.relative_to_self, 0.5f); alphaanimation alphaanim = new alphaanimation(1f, 0.0f); animationset.addanimation(scaleanim); animationset.addanimation(alphaanim); animationset.setduration(duration); animationset.setfillafter(true); return animationset; } /** * 为当前点击的item设置变大和透明度降低的动画 */ private animation scalebiganim(int duration) { animationset animationset = new animationset(true); scaleanimation scaleanim = new scaleanimation(1.0f, 4.0f, 1.0f, 4.0f, animation.relative_to_self, 0.5f, animation.relative_to_self, 0.5f); alphaanimation alphaanim = new alphaanimation(1f, 0.0f); animationset.addanimation(scaleanim); animationset.addanimation(alphaanim); animationset.setduration(duration); animationset.setfillafter(true); return animationset; } /** * 切换菜单状态 */ private void changestatus() { mstatus = (mstatus == status.close ? status.open : status.close); } /** * 是否处于展开状态 * @return */ public boolean isopen() { return mstatus == status.open; } }
3.使用卫星式菜单类
package com.xc.xcskin; import android.app.activity; import android.os.bundle; import android.view.view; import android.widget.toast; import com.xc.xcskin.view.xcarcmenuview; import com.xc.xcskin.view.xcarcmenuview.onmenuitemclicklistener; import com.xc.xcskin.view.xcguaguakaview; import com.xc.xcskin.view.xcguaguakaview.oncompletelistener; /** * 使用并测试自定义卫星式菜单view * @author caizhiming * */ public class xcarcmenuviewdemo extends activity{ @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.xc_arcmenu_view_demo); xcarcmenuview view = (xcarcmenuview) findviewbyid(r.id.arcmenu); view.setonmenuitemclicklistener(new onmenuitemclicklistener() { @override public void onclick(view view, int pos) { // todo auto-generated method stub string tag = (string) view.gettag(); toast.maketext(xcarcmenuviewdemo.this, tag, toast.length_short).show(); } }); } }
三、总结
android实现自定义的卫星式菜单(弧形菜单)的内容到这就基本结束了,感兴趣的朋友们可以动手操作起来,只有自己实践了才能更深的理解,希望本文对大家能有所帮助。
下一篇: 探究dosbox打印字符时的bug问题