Android 圆形旋转菜单【转】

来自:http://blog.csdn.net/ywl5320/article/details/52449392

最近帮朋友做了一个动画菜单,感觉有一定的实用价值,就在此给大家分享一下,先看看效果:源码下载地址在末尾


实现思路:

从图中可以看出,这三个(或更多,需要自己再实现)菜单是围绕着中心点旋转的,旋转分为2层,背景旋转和菜单旋转,背景旋转可以直接用旋转动画来实现;菜单的旋转是在以中心点为圆心的圆环上,所以这里用了根据旋转角度求此点在直角坐标系中的坐标点的函数(x = r * cos(rotation* 3.14 / 180) 和y = r * sin(rotation* 3.14 / 180) ),然后根据获取到的点的位置来设置菜单的位置就能实现这种效果。由此可见 数学是很重要的 哈哈~~

有了思路我们就能用代码来实现了:

1、首先自定义View继承相对布局并重写构造函数

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by ywl on 2016/8/7. 
  3.  */  
  4. public class CircleMenuLayout extends RelativeLayout {  
  5.   
  6.     public CircleMenuLayout(Context context) {  
  7.         this(context, null);  
  8.     }  
  9.   
  10.     public CircleMenuLayout(Context context, AttributeSet attrs) {  
  11.         this(context, attrs, 0);  
  12.     }  
  13.   
  14.     /** 
  15.      * 初始化布局 把旋转背景和中心点添加进去 
  16.      * @param context 
  17.      * @param attrs 
  18.      * @param defStyleAttr 
  19.      */  
  20.     public CircleMenuLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  21.         super(context, attrs, defStyleAttr);  
  22.         this.context = context;  
  23.         layoutInflater = LayoutInflater.from(context);  
  24.         menuitems = new ArrayList<View>();  
  25.         centerview = new View(context);//中心点  
  26.         centerview.setId(ID_CENTER_VIEW);  
  27.         LayoutParams lp = new LayoutParams(00);  
  28.         lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);  
  29.         addView(centerview, lp); //添加中心的 用于旋转定位  
  30.   
  31.         progressBar = new ProgressBar(context);//旋转的背景  
  32.         LayoutParams lp2 = new LayoutParams(dip2px(context, 90), dip2px(context, 90));  
  33.         lp2.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);  
  34.         addView(progressBar, lp2);  
  35.         progressBar.setIndeterminateDrawable(context.getResources().getDrawable(R.mipmap.icon_circle_menu));  
  36.     }  
  37. }  
构造函数中添加中心定位点和旋转背景图片,并设置合适的大小。

2、根据传入的图片数组和菜单名字数组,生成菜单原始位置效果。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 菜单的数量 和 半径 名字 和图片 这里只为3个菜单做了适配 
  3.      * @param size 
  4.      * @param center_distance 
  5.      */  
  6.     public void initMenuItem(int size, int center_distance, String[] titles, int[] imgs)  
  7.     {  
  8.         radus = 360f / size;  
  9.         int width = dip2px(context, 50); //菜单宽度  
  10.         int height = dip2px(context, 50);//菜单高度  
  11.         for(int i = 0; i < size; i++) //循环添加布局  
  12.         {  
  13.             int top = 0;  
  14.             int left = 0;  
  15.   
  16.             top = -(int)(Math.sin(radus * i * 3.1415f / 180) * center_distance); //r   *   cos(ao   *   3.14   /180   )  
  17.             left = -(int)(Math.cos(radus * i * 3.1415f / 180) * center_distance); //计算位置点  
  18.             LayoutParams lp = new LayoutParams(dip2px(context, 50), dip2px(context, 50));  
  19.             View view = layoutInflater.inflate(R.layout.item_circle_menu, thisfalse);  
  20.             view.setTag(i);  
  21.             TextView tvname = (TextView) view.findViewById(R.id.tv_name);  
  22.             ImageView ivimg = (ImageView) view.findViewById(R.id.img);  
  23.             tvname.setText(titles[i]);  
  24.             ivimg.setImageResource(imgs[i]);  
  25.             view.setOnClickListener(new OnClickListener() {  
  26.                 @Override  
  27.                 public void onClick(View v) {//根据点击的区域 旋转菜单  
  28.                     if(!isrun) {  
  29.                         tag = (int) v.getTag();  
  30.                         currentPosition = tag;  
  31.                         if(tag == 0)  
  32.                         {  
  33.                             finishdus = -360;  
  34.                         }  
  35.                         else if(tag == 1)  
  36.                         {  
  37.                             finishdus = -120;  
  38.                         }  
  39.                         else if(tag == 2)  
  40.                         {  
  41.                             finishdus = -240;  
  42.                         }  
  43.                         LayoutParams lp = (LayoutParams) v.getLayoutParams();  
  44.                         int l = lp.leftMargin;  
  45.                         int t = lp.topMargin;  
  46.   
  47.                         if (t > -dip2px(context, 5) && l > -dip2px(context, 5)) {  
  48.                             oldradus = 120f;  
  49.                             isright = false;  
  50.                         } else if (t >  -dip2px(context, 5) && l <  -dip2px(context, 5)) {  
  51.                             oldradus = 120f;  
  52.                             isright = true;  
  53.                         } else if (t <  -dip2px(context, 5)) {  
  54.                             oldradus = 0f;  
  55.                         }  
  56.                         sub = 0;  
  57.                         circleMenu(8, dip2px(context, 45), oldradus, isright);  
  58.                           
  59.                     }  
  60.                 }  
  61.             });  
  62.             lp.addRule(RelativeLayout.BELOW, centerview.getId());  
  63.             lp.addRule(RelativeLayout.RIGHT_OF, centerview.getId());  
  64.             lp.setMargins(-width / 2 + top, -height / 2 + left, 00);  
  65.             addView(view, lp);  
  66.             menuitems.add(view);  
  67.         }  
  68.   
  69.         handler.postDelayed(runnable, 0);  
  70.     }  

根据菜单的数量循环计算每个菜单的位置,然后在相应的位置添加相应的菜单就可以实现菜单的初始化了。这里为每个菜单添加了点击事件,但是只适配了3个菜单的情况,至于其他数量的菜单,可以自己来改或者写一个通用的方法来计算点击位置。

3、背景旋转动画:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     * 根据度数来旋转菜单 菜单中心都在一个圆上面 采用圆周运动来旋转 
  3.     * @param offserradius 
  4.     * @param center_distance 
  5.     * @param d 
  6.     * @param right 
  7.     */  
  8.    public void circleMenu(float offserradius, int center_distance, float d, boolean right)  
  9.    {  
  10.     if(oldradus != 0)  
  11.     {  
  12.         progressBar.clearAnimation();  
  13.         if(isright)  
  14.         {  
  15.             mRotateUpAnim = new RotateAnimation(bgdus, bgdus + 120,  
  16.                     Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,  
  17.                     0.5f);  
  18.             bgdus += 120;  
  19.         }  
  20.         else  
  21.         {  
  22.             mRotateUpAnim = new RotateAnimation(bgdus, bgdus - 120,  
  23.                     Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,  
  24.                     0.5f);  
  25.             bgdus -= 120;  
  26.         }  
  27.           
  28.         lir = new LinearInterpolator();  
  29.         mRotateUpAnim.setDuration(350);  
  30.         mRotateUpAnim.setFillAfter(true);  
  31.         mRotateUpAnim.setInterpolator(lir);  
  32. //        mRotateUpAnim.setRepeatCount(Animation.INFINITE);  
  33.         progressBar.startAnimation(mRotateUpAnim);  
  34.     }  
  35.        circleMenuItem(offserradius, center_distance, d, right);  
  36.    }  
这个比较简单,就是根据旋转的角度,启用旋转动画。

4、旋转菜单:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 菜单旋转 
  3.      * @param offserradius 
  4.      * @param center_distance 
  5.      * @param d 
  6.      * @param right 
  7.      */  
  8.     public void circleMenuItem(float offserradius, int center_distance, float d, boolean right)  
  9.     {  
  10.         sub += offserradius;  
  11.         if(sub > d)  
  12.         {  
  13.             if(onMenuItemSelectedListener != null)  
  14.             {  
  15.                 onMenuItemSelectedListener.onMenuItemOnclick(tag);  
  16.             }  
  17.             isrun = false;  
  18.             return;  
  19.         }  
  20.         if(right) {  
  21.             offsetradus -= offserradius;  
  22.         }  
  23.         else  
  24.         {  
  25.             offsetradus += offserradius;  
  26.         }  
  27.         int size = menuitems.size();  
  28.         int width = dip2px(context, 50);  
  29.         int height = dip2px(context, 50);  
  30.         for(int i = 0; i < size; i++)  
  31.         {  
  32.             if(Math.abs(sub - d) <= 8)  
  33.             {  
  34.                 offsetradus = finishdus;  
  35.             }  
  36.             LayoutParams lp = (LayoutParams) menuitems.get(i).getLayoutParams();  
  37.             float ds = radus * i + offsetradus;  
  38.             int top = -(int)(Math.sin(ds * 3.1415f / 180) * center_distance); //r   *   cos(ao   *   3.14   /180   )  
  39.             int left = -(int)(Math.cos(ds * 3.1415f / 180) * center_distance);  
  40.             lp.setMargins(-width / 2 + top, -height / 2 + left, 00);  
  41.             menuitems.get(i).requestLayout();  
  42.         }  
  43.   
  44.         if(sub <= d) {  
  45.             isrun = true;  
  46.             offsetradus = offsetradus % 360;  
  47.             handler.postDelayed(runnable, 5);  
  48.         }  
  49.         else  
  50.         {  
  51.             if(onMenuItemSelectedListener != null)  
  52.             {  
  53.                 onMenuItemSelectedListener.onMenuItemOnclick(tag);  
  54.             }  
  55.             isrun = false;  
  56.         }  
  57.     }  
这里旋转是根据初始化时每个菜单所在的位置来求的旋转角度,然后启动handler来动递加或递减角度来求响应的位置,就实现了动画效果。

5、手动设置菜单项(有局限,没有通用性):

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 设置旋转到哪个菜单项 
  3.      * @param tag 
  4.      */  
  5.     public void setCurrentTag(int tag)  
  6.     {  
  7.         if(currentPosition == tag)  
  8.         {  
  9.             return;  
  10.         }  
  11.         if(tag == 0)  
  12.         {  
  13.             finishdus = -360;  
  14.         }  
  15.         else if(tag == 1)  
  16.         {  
  17.             finishdus = -120;  
  18.         }  
  19.         else if(tag == 2)  
  20.         {  
  21.             finishdus = -240;  
  22.         }  
  23.   
  24.         if(currentPosition == 0//当前是0  
  25.         {  
  26.             if(tag == 1)  
  27.             {  
  28.                 oldradus = 120f;  
  29.                 isright = true;  
  30.             }  
  31.             else if(tag == 2)  
  32.             {  
  33.                 oldradus = 120f;  
  34.                 isright = false;  
  35.             }  
  36.         }  
  37.         else if(currentPosition == 1)  
  38.         {  
  39.             if(tag == 2)  
  40.             {  
  41.                 oldradus = 120f;  
  42.                 isright = true;  
  43.             }  
  44.             else if(tag == 0)  
  45.             {  
  46.                 oldradus = 120f;  
  47.                 isright = false;  
  48.             }  
  49.         }  
  50.         else if(currentPosition == 2)  
  51.         {  
  52.             if(tag == 0)  
  53.             {  
  54.                 oldradus = 120f;  
  55.                 isright = true;  
  56.             }  
  57.             else if(tag == 1)  
  58.             {  
  59.                 oldradus = 120f;  
  60.                 isright = false;  
  61.             }  
  62.         }  
  63.   
  64.         currentPosition = tag;  
  65.         this.tag = tag;  
  66.         sub = 0;  
  67.         circleMenu(8, dip2px(context, 45), oldradus, isright);  
  68.   
  69.     }  

这样就可以实现旋转效果了。

6、调用方法:

(1)布局文件:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <com.ywl5320.circlemenu.CircleMenuLayout  
  2.         android:id="@+id/cml_menu"  
  3.         android:layout_width="150dp"  
  4.         android:layout_height="150dp"  
  5.         android:layout_centerHorizontal="true"  
  6.         android:layout_alignParentBottom="true"  
  7.         android:layout_marginBottom="92dp"/>  
(2)菜单布局文件:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"   
  4.     android:layout_width="100dp"  
  5.     android:layout_height="100dp"  
  6.     android:padding="5dp"  
  7.     android:gravity="center">  
  8.       
  9.     <ImageView  
  10.         android:id="@+id/img"  
  11.         android:layout_width="25dp"  
  12.         android:layout_height="25dp"  
  13.         android:scaleType="fitXY"/>  
  14.     <TextView  
  15.         android:id="@+id/tv_name"  
  16.         android:layout_width="wrap_content"  
  17.         android:layout_height="wrap_content"  
  18.         android:text="菜单项"  
  19.         android:textSize="9sp"  
  20.         android:gravity="center"  
  21.         android:textColor="#ffffff"/>  
  22. </LinearLayout>  

(3)Activity中调用

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="white-space:pre">    </span>cmlmenu = (CircleMenuLayout) findViewById(R.id.cml_menu);  
  2.         btn = (Button) findViewById(R.id.btn);  
  3.         cmlmenu.initDatas(titles, imgs);  
  4.         cmlmenu.setOnMenuItemSelectedListener(new CircleMenuLayout.OnMenuItemSelectedListener() {  
  5.             @Override  
  6.             public void onMenuItemOnclick(int code) {  
  7.                 if(code == 0)//  
  8.                 {  
  9.                     Toast.makeText(MainActivity.this"支付宝", Toast.LENGTH_SHORT).show();  
  10.                 }  
  11.                 else if(code == 1)  
  12.                 {  
  13.                     Toast.makeText(MainActivity.this"银联", Toast.LENGTH_SHORT).show();  
  14.                 }  
  15.                 else if(code == 2)  
  16.                 {  
  17.                     Toast.makeText(MainActivity.this"微信", Toast.LENGTH_SHORT).show();  
  18.                 }  
  19.             }  
  20.         });  
OK,就完成了三个菜单旋转效果(注:这里仅仅是为了3个菜单而设计的,其他个数的自己还需要精简或更改一些代码,相信自己改出来的会更有收获的~~)。

Github:CircleMenu

CSDN:CircleMenu

欢迎下载和Star

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值