欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android圆形旋转菜单开发实例

程序员文章站 2024-03-06 09:01:19
最近帮朋友做了一个动画菜单,感觉有一定的实用价值,就在此给大家分享一下,先看看效果: 实现思路: 从图中可以看出,这三个(或更多,需要自己再实现)菜单是围绕着中心点...

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

Android圆形旋转菜单开发实例

实现思路:

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

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

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

/** 
 * created by ywl on 2016/8/7. 
 */ 
public class circlemenulayout extends relativelayout { 
  public circlemenulayout(context context) { 
    this(context, null); 
  } 
  public circlemenulayout(context context, attributeset attrs) { 
    this(context, attrs, 0); 
  } 
  /** 
   * 初始化布局 把旋转背景和中心点添加进去 
   * @param context 
   * @param attrs 
   * @param defstyleattr 
   */ 
  public circlemenulayout(context context, attributeset attrs, int defstyleattr) { 
    super(context, attrs, defstyleattr); 
    this.context = context; 
    layoutinflater = layoutinflater.from(context); 
    menuitems = new arraylist<view>(); 
    centerview = new view(context);//中心点 
    centerview.setid(id_center_view); 
    layoutparams lp = new layoutparams(0, 0); 
    lp.addrule(relativelayout.center_in_parent, relativelayout.true); 
    addview(centerview, lp); //添加中心的 用于旋转定位 
    progressbar = new progressbar(context);//旋转的背景 
    layoutparams lp2 = new layoutparams(dip2px(context, 90), dip2px(context, 90)); 
    lp2.addrule(relativelayout.center_in_parent, relativelayout.true); 
    addview(progressbar, lp2); 
    progressbar.setindeterminatedrawable(context.getresources().getdrawable(r.mipmap.icon_circle_menu)); 
  } 
} 

构造函数中添加中心定位点和旋转背景图片,并设置合适的大小。

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

/** 
   * 菜单的数量 和 半径 名字 和图片 这里只为3个菜单做了适配 
   * @param size 
   * @param center_distance 
   */ 
  public void initmenuitem(int size, int center_distance, string[] titles, int[] imgs) 
  { 
    radus = 360f / size; 
    int width = dip2px(context, 50); //菜单宽度 
    int height = dip2px(context, 50);//菜单高度 
    for(int i = 0; i < size; i++) //循环添加布局 
    { 
      int top = 0; 
      int left = 0; 
      top = -(int)(math.sin(radus * i * 3.1415f / 180) * center_distance); //r  *  cos(ao  *  3.14  /180  ) 
      left = -(int)(math.cos(radus * i * 3.1415f / 180) * center_distance); //计算位置点 
      layoutparams lp = new layoutparams(dip2px(context, 50), dip2px(context, 50)); 
      view view = layoutinflater.inflate(r.layout.item_circle_menu, this, false); 
      view.settag(i); 
      textview tvname = (textview) view.findviewbyid(r.id.tv_name); 
      imageview ivimg = (imageview) view.findviewbyid(r.id.img); 
      tvname.settext(titles[i]); 
      ivimg.setimageresource(imgs[i]); 
      view.setonclicklistener(new onclicklistener() { 
        @override 
        public void onclick(view v) {//根据点击的区域 旋转菜单 
          if(!isrun) { 
            tag = (int) v.gettag(); 
            currentposition = tag; 
            if(tag == 0) 
            { 
              finishdus = -360; 
            } 
            else if(tag == 1) 
            { 
              finishdus = -120; 
            } 
            else if(tag == 2) 
            { 
              finishdus = -240; 
            } 
            layoutparams lp = (layoutparams) v.getlayoutparams(); 
            int l = lp.leftmargin; 
            int t = lp.topmargin; 
            if (t > -dip2px(context, 5) && l > -dip2px(context, 5)) { 
              oldradus = 120f; 
              isright = false; 
            } else if (t > -dip2px(context, 5) && l < -dip2px(context, 5)) { 
              oldradus = 120f; 
              isright = true; 
            } else if (t < -dip2px(context, 5)) { 
              oldradus = 0f; 
            } 
            sub = 0; 
            circlemenu(8, dip2px(context, 45), oldradus, isright); 
          } 
        } 
      }); 
      lp.addrule(relativelayout.below, centerview.getid()); 
      lp.addrule(relativelayout.right_of, centerview.getid()); 
      lp.setmargins(-width / 2 + top, -height / 2 + left, 0, 0); 
      addview(view, lp); 
      menuitems.add(view); 
    } 
    handler.postdelayed(runnable, 0); 
  } 

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

3、背景旋转动画:

/** 
  * 根据度数来旋转菜单 菜单中心都在一个圆上面 采用圆周运动来旋转 
  * @param offserradius 
  * @param center_distance 
  * @param d 
  * @param right 
  */ 
  public void circlemenu(float offserradius, int center_distance, float d, boolean right) 
  { 
  if(oldradus != 0) 
  { 
    progressbar.clearanimation(); 
    if(isright) 
    { 
      mrotateupanim = new rotateanimation(bgdus, bgdus + 120, 
          animation.relative_to_self, 0.5f, animation.relative_to_self, 
          0.5f); 
      bgdus += 120; 
    } 
    else 
    { 
      mrotateupanim = new rotateanimation(bgdus, bgdus - 120, 
          animation.relative_to_self, 0.5f, animation.relative_to_self, 
          0.5f); 
      bgdus -= 120; 
    } 
    lir = new linearinterpolator(); 
    mrotateupanim.setduration(350); 
    mrotateupanim.setfillafter(true); 
    mrotateupanim.setinterpolator(lir); 
//    mrotateupanim.setrepeatcount(animation.infinite); 
    progressbar.startanimation(mrotateupanim); 
  } 
    circlemenuitem(offserradius, center_distance, d, right); 
  } 

这个比较简单,就是根据旋转的角度,启用旋转动画。

4、旋转菜单:

/** 
   * 菜单旋转 
   * @param offserradius 
   * @param center_distance 
   * @param d 
   * @param right 
   */ 
  public void circlemenuitem(float offserradius, int center_distance, float d, boolean right) 
  { 
    sub += offserradius; 
    if(sub > d) 
    { 
      if(onmenuitemselectedlistener != null) 
      { 
        onmenuitemselectedlistener.onmenuitemonclick(tag); 
      } 
      isrun = false; 
      return; 
    } 
    if(right) { 
      offsetradus -= offserradius; 
    } 
    else 
    { 
      offsetradus += offserradius; 
    } 
    int size = menuitems.size(); 
    int width = dip2px(context, 50); 
    int height = dip2px(context, 50); 
    for(int i = 0; i < size; i++) 
    { 
      if(math.abs(sub - d) <= 8) 
      { 
        offsetradus = finishdus; 
      } 
      layoutparams lp = (layoutparams) menuitems.get(i).getlayoutparams(); 
      float ds = radus * i + offsetradus; 
      int top = -(int)(math.sin(ds * 3.1415f / 180) * center_distance); //r  *  cos(ao  *  3.14  /180  ) 
      int left = -(int)(math.cos(ds * 3.1415f / 180) * center_distance); 
      lp.setmargins(-width / 2 + top, -height / 2 + left, 0, 0); 
      menuitems.get(i).requestlayout(); 
    } 
    if(sub <= d) { 
      isrun = true; 
      offsetradus = offsetradus % 360; 
      handler.postdelayed(runnable, 5); 
    } 
    else 
    { 
      if(onmenuitemselectedlistener != null) 
      { 
        onmenuitemselectedlistener.onmenuitemonclick(tag); 
      } 
      isrun = false; 
    } 
  } 

这里旋转是根据初始化时每个菜单所在的位置来求的旋转角度,然后启动handler来动递加或递减角度来求响应的位置,就实现了动画效果。

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

/** 
   * 设置旋转到哪个菜单项 
   * @param tag 
   */ 
  public void setcurrenttag(int tag) 
  { 
    if(currentposition == tag) 
    { 
      return; 
    } 
    if(tag == 0) 
    { 
      finishdus = -360; 
    } 
    else if(tag == 1) 
    { 
      finishdus = -120; 
    } 
    else if(tag == 2) 
    { 
      finishdus = -240; 
    } 
    if(currentposition == 0) //当前是0 
    { 
      if(tag == 1) 
      { 
        oldradus = 120f; 
        isright = true; 
      } 
      else if(tag == 2) 
      { 
        oldradus = 120f; 
        isright = false; 
      } 
    } 
    else if(currentposition == 1) 
    { 
      if(tag == 2) 
      { 
        oldradus = 120f; 
        isright = true; 
      } 
      else if(tag == 0) 
      { 
        oldradus = 120f; 
        isright = false; 
      } 
    } 
    else if(currentposition == 2) 
    { 
      if(tag == 0) 
      { 
        oldradus = 120f; 
        isright = true; 
      } 
      else if(tag == 1) 
      { 
        oldradus = 120f; 
        isright = false; 
      } 
    } 
    currentposition = tag; 
    this.tag = tag; 
    sub = 0; 
    circlemenu(8, dip2px(context, 45), oldradus, isright); 
  } 

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

6、调用方法:

(1)布局文件:

<com.ywl5320.circlemenu.circlemenulayout 
    android:id="@+id/cml_menu" 
    android:layout_width="150dp" 
    android:layout_height="150dp" 
    android:layout_centerhorizontal="true" 
    android:layout_alignparentbottom="true" 
    android:layout_marginbottom="92dp"/> 

(2)菜单布局文件:

<?xml version="1.0" encoding="utf-8"?> 
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:orientation="vertical"  
  android:layout_width="100dp" 
  android:layout_height="100dp" 
  android:padding="5dp" 
  android:gravity="center"> 
  <imageview 
    android:id="@+id/img" 
    android:layout_width="25dp" 
    android:layout_height="25dp" 
    android:scaletype="fitxy"/> 
  <textview 
    android:id="@+id/tv_name" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="菜单项" 
    android:textsize="9sp" 
    android:gravity="center" 
    android:textcolor="#ffffff"/> 
</linearlayout> 

(3)activity中调用

<span style="white-space:pre">  </span>cmlmenu = (circlemenulayout) findviewbyid(r.id.cml_menu); 
    btn = (button) findviewbyid(r.id.btn); 
    cmlmenu.initdatas(titles, imgs); 
    cmlmenu.setonmenuitemselectedlistener(new circlemenulayout.onmenuitemselectedlistener() { 
      @override 
      public void onmenuitemonclick(int code) { 
        if(code == 0)// 
        { 
          toast.maketext(mainactivity.this, "支付宝", toast.length_short).show(); 
        } 
        else if(code == 1) 
        { 
          toast.maketext(mainactivity.this, "银联", toast.length_short).show(); 
        } 
        else if(code == 2) 
        { 
          toast.maketext(mainactivity.this, "微信", toast.length_short).show(); 
        } 
      } 
    }); 

ok,就完成了三个菜单旋转效果(注:这里仅仅是为了3个菜单而设计的,其他个数的自己还需要精简或更改一些代码,相信自己改出来的会更有收获的~~)。

以上所述是小编给大家介绍的android圆形旋转菜单开发实例,希望对大家有所帮助