Android卫星菜单效果的实现方法
android小白第一次写博客,心情无比激动。下面给大家展示一下卫星菜单的实现。
1.简单介绍卫星菜单
在应用程序中,有很多展示菜单的方式,但其功能都是大同小异,这样一来,菜单的美观以及展示方式就显的尤为重要,卫星菜单就是很不错的一种。下面是本案例的gif图:
2.学习本案例需要的知识点
(1)动画
(2)自定义viewgroup
(3)自定义属性
a、attr.xml
b、在布局中使用自定义属性
c、在代码中获取自定义属性值
3.首先分析我们的卫星菜单需要那些自定义属性并书写代码
首先,菜单可以显示在屏幕的四个角,所以我们需要一个属性来确定它的位置,菜单在屏幕的四个角比较美观,在这里用到枚举。
其次,我们还需要一个展开半径,因此还需要自定义半径。
下面是attr.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="position"> <enum name="left_top" value="0" /> <enum name="left_bottom" value="1" /> <enum name="right_top" value="2" /> <enum name="right_bottom" value="3" /> </attr> <attr name="radius" format="dimension"/> <declare-styleable name="satemenu"> <attr name="radius" /> <attr name="position" /> </declare-styleable> </resources>
4.自定义viewgroup
–继承viewgroup 以相关属性
public class satemenu extends viewgroup implements view.onclicklistener { private int animationtime; //动画时间 private int radius; //展开半径 private int pos; //从自定义属性中获取的菜单位置 private state state; //菜单状态 private int l = 0, t = 0; //左上值 private view centerbtn = null; //展开按钮 private menuitemlistener menuitemlistener; //菜单项点击监听 private position position; //枚举型菜单位置 private enum position { //位置枚举 left_top, left_bottom, right_top, right_bottom } private enum state { //菜单状态枚举 open, colse }
–构造方法
public satemenu(context context) { //一个参数构造方法调用两个参数构造方法 this(context, null); } public satemenu(context context, attributeset attrs) { //两个参数构造方法调用三个个参数构造方法 this(context, attrs, 0); } public satemenu(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); animationtime = 500; //设置动画展开时间 typedarray a = context.gettheme().obtainstyledattributes(attrs, r.styleable.satemenu, defstyleattr, 0); //获取自定义属性值集合 radius = (int) a.getdimension(r.styleable.satemenu_radius, typedvalue.applydimension(typedvalue.complex_unit_dip, 100, getresources().getdisplaymetrics())); //获取半径并转化为像素值 state = state.colse; //设置菜单默认关闭 pos = a.getint(r.styleable.satemenu_position, 0); //获取位置 //将位置转化为枚举值 (这样就把无意义的int转化为有意义的枚举值) switch (pos) { case 0: position = position.left_top; break; case 1: position = position.left_bottom; break; case 2: position = position.right_top; break; case 3: position = position.right_bottom; break; } }
–重写onmeasure方法
@override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); int count = getchildcount(); //测量子view for (int i = 0; i < count; i++) { measurechild(getchildat(i), widthmeasurespec, heightmeasurespec); } }
–重写onlayout方法
@override protected void onlayout(boolean changed, int l, int t, int r, int b) { if (changed) btnlayout(); } private void btnlayout() { centerbtn = getchildat(0); if (position == position.right_bottom || position == position.right_top) { //如果菜单设置在屏幕的右侧,那么展开按钮的l值=viewgroup宽度-按钮宽度 l = getmeasuredwidth() - centerbtn.getmeasuredwidth(); } if (position == position.left_bottom || position == position.right_bottom) { //如果菜单设置在屏幕的下边,那么展开按钮的t值=viewgroup高度-按钮高度 t = getmeasuredheight() - centerbtn.getmeasuredheight(); } //设置展开按钮位置 centerbtn.layout(l, t, l + centerbtn.getmeasuredwidth(), t + centerbtn.getmeasuredheight()); childbtnlayout(); //设置子按钮位置 centerbtn.setonclicklistener(this); }
–设置子按钮位置需要一点点数学知识,下面我以主菜单在右下角为例,画一个简图,图片对应右侧第一个公式
private void childbtnlayout() { int childmunecount = getchildcount() - 1; //角度等于90度/子按钮个数-1 float a = (float) (math.pi / 2 / (childmunecount - 1)); int cl, ct; //分别是子按钮的 左 上 for (int i = 0; i < childmunecount; i++) { if (position == position.right_bottom || position == position.right_top) { cl = (int) (l - radius * math.cos(i * a)); } else { cl = (int) (l + radius * math.cos(i * a)); } if (position == position.left_top || position == position.right_top) { ct = (int) (t + radius * math.sin(i * a)); } else { ct = (int) (t - radius * math.sin(i * a)); } view childview = getchildat(i + 1); childview.layout(cl, ct, cl + childview.getmeasuredwidth(), ct + childview.getmeasuredheight()); childview.setonclicklistener(this); childview.settag(i); childview.setvisibility(view.gone); } }
–动画的展开与关闭,这里没有用属性动画,原理是:当用户关闭菜单的时候,将子按钮隐藏,打开才打的时候在把子按钮显示出来
private void changestate() { int childmunecount = getchildcount() - 1; //设置展开按钮旋转动画 animation animation = new rotateanimation(0, 360, centerbtn.getmeasuredwidth() / 2, centerbtn.getmeasuredheight() / 2); animation.setduration(animationtime); centerbtn.setanimation(animation); animation.start(); view childview; //子按钮有两个动画(位移、旋转),所以这里用到动画集,这里也涉及到一些数学知识,和之前设置子按钮位置差不多 animationset animationset; animation translateanimation; animation rotateanimation; int cl, ct; float a = (float) (math.pi / 2 / (childmunecount - 1)); if (state == state.open) { state = state.colse; for (int i = 0; i < childmunecount; i++) { if (position == position.right_bottom || position == position.right_top) cl = (int) (radius * math.cos(i * a)); else cl = (int) (-radius * math.cos(i * a)); if (position == position.left_top || position == position.right_top) ct = (int) (-radius * math.sin(i * a)); else ct = (int) (radius * math.sin(i * a)); childview = getchildat(i + 1); childview.setvisibility(view.gone); translateanimation = new translateanimation(0, cl, 0, ct); translateanimation.setduration(animationtime); rotateanimation = new rotateanimation(0, 360, childview.getmeasuredheight() / 2, childview.getmeasuredheight() / 2); rotateanimation.setduration(animationtime); animationset = new animationset(true); animationset.addanimation(rotateanimation); animationset.addanimation(translateanimation); childview.setanimation(animationset); animationset.start(); childview.setvisibility(view.gone); } } else { state = state.open; for (int i = 0; i < childmunecount; i++) { if (position == position.right_bottom || position == position.right_top) cl = (int) (radius * math.cos(i * a)); else cl = (int) (-radius * math.cos(i * a)); if (position == position.left_top || position == position.right_top) ct = (int) (-radius * math.sin(i * a)); else ct = (int) (radius * math.sin(i * a)); childview = getchildat(i + 1); childview.setvisibility(view.gone); translateanimation = new translateanimation(cl, 0, ct, 0); translateanimation.setduration(animationtime); rotateanimation = new rotateanimation(360, 0, childview.getmeasuredheight() / 2, childview.getmeasuredheight() / 2); rotateanimation.setduration(animationtime); animationset = new animationset(true); animationset.addanimation(rotateanimation); animationset.addanimation(translateanimation); childview.setanimation(animationset); animationset.start(); childview.setvisibility(view.visible); } } }
–写到这里我们的卫星菜单已经可以展现出来的,运行一下,效果还是不错的。美中不足的是,子按钮还没有点击事件,下面我们就将这个小小的不足补充一下。我们可以通过给子按钮添加点击事件来监听它,但点击之后要做的事情不可能写在viewgroup中,这就需要用接口进行回调。大家看一下在设置子按钮位置的时候有这样一句代码 childview.settag(i); 它的目的就是给子按钮添加索引,接下来看一下具体怎样实现的。
@override public void onclick(view v) { if (v.getid() == centerbtn.getid()) { changestate(); } else { if (menuitemlistener != null) { menuitemlistener.onclick((integer) v.gettag()); } } } public interface menuitemlistener { void onclick(int position); } public void setmenuitemlistener(menuitemlistener menuitemlistener) { this.menuitemlistener = menuitemlistener; }
–到这里我们已经完全实现了卫星菜单的所有功能,但大家有没有发现,一些菜单在展开之后,我们点击其他区域,菜单会自动收起来,所以我们还要给我们的viewgroup添加ontouchevent事件,在菜单展开的时候,他把菜单收起来,并将此次点击拦截。
@override public boolean ontouchevent(motionevent event) { if (state == state.open) { changestate(); return true; //拦截 } return super.ontouchevent(event); }
5.下面试用一下我们编写的卫星菜单,看一下成果。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:wzw="http://schemas.android.com/apk/res/com.satemenudemo" android:layout_width="match_parent" android:layout_height="match_parent"> <com.satemenudemo.satemenu android:id="@+id/menu_id" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="3dp" wzw:position="right_bottom" wzw:radius="150dp"> <imagebutton android:id="@+id/center_btn" android:layout_width="40dp" android:layout_height="40dp" android:background="@drawable/add" /> <imagebutton android:id="@+id/menu1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/find" /> <imagebutton android:id="@+id/menu2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shop" /> <imagebutton android:id="@+id/menu3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/people" /> <imagebutton android:id="@+id/menu4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/love" /> </com.satemenudemo.satemenu> </relativelayout>
mainactivity.java
public class mainactivity extends activity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); satemenu satemenu = (satemenu) findviewbyid(r.id.menu_id); satemenu.setmenuitemlistener(new satemenu.menuitemlistener() { @override public void onclick(int position) { toast.maketext(mainactivity.this, "-- "+position, toast.length_short).show(); } }); } }
以上所述是小编给大家介绍的android卫星菜单效果的实现方法,希望对大家有所帮助