Android 圆形旋转菜单

来源:互联网 发布:mac ps怎么导入字体 编辑:程序博客网 时间:2024/06/05 07:13

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


实现思路:

从图中可以看出,这三个(或更多,需要自己再实现)菜单是围绕着中心点旋转的,旋转分为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个菜单而设计的,其他个数的自己还需要精简或更改一些代码,相信自己改出来的会更有收获的~~)。

Github:CircleMenu

CSDN:CircleMenu

欢迎下载和Star

0 0
原创粉丝点击