Android 圆形旋转菜单

我的视频课程:《FFmpeg打造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个菜单而设计的,其他个数的自己还需要精简或更改一些代码,相信自己改出来的会更有收获的~~)。

Github:CircleMenu

CSDN:CircleMenu

欢迎下载和Star

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ywl5320

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值