可旋转的圆形菜单栏

本文介绍了一个可旋转的圆形菜单栏设计,菜单项能动态添加,并且随着手势旋转。菜单视图在圆形轨迹中分布,利用三角函数计算位置,并通过属性动画实现旋转效果。在旋转过程中,菜单项不可点击,静止时则可点击。同时提供了示例代码和demo地址。

可旋转的圆形菜单栏

一个圆形的可旋转的菜单栏。功能:可动态添加菜单view,菜单view随着手势的旋转而旋转,菜单view可点击。
自定义一个RelativeLayout,添加的菜单view被放置在一个圆形轨迹中,随着手势旋转而旋转。
在这里插入图片描述
代码:动态添加菜单view

 private List<View> list = new ArrayList<>();
    public void initView() {
        int size = 5;
        for (int i=0;i<size;i++){
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.test,null);
            TextView textView = (TextView) view.findViewById(R.id.textview);
            textView.setText(i+"");
            list.add(view);
        }
        myView.updateView(list);
    }

更新view并把view放在一个圆形轨迹中,通过三角函数公式,计算出x,y的坐标,通过属性动画放置菜单view

public void updateView(List<View> list){
        if(list==null||list.size()==0){
            return;
        }
        this.list = list;
        int i = 0;
        for(View childView : list){
            addView(childView);
            childView.setTag(i++);
            childView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,v.getTag()+"...",Toast.LENGTH_SHORT).show();
                }
            });
        }
        requestLayout();
        instance.post(new Runnable() {
            @Override
            public void run() {
                animation(instance.list);
            }
        });

    }
    public void animation(List<View> views) {
        for (int i = 0; i < views.size(); i++) {
            int angle = (wAngle / (views.size()+1)) * (i + 1);
            if(wAngle == 180){
                angle = (wAngle / (views.size()+1)) * (i + 1);
            }else if(wAngle == 360){
                angle = (wAngle / (views.size())) * (i + 1);
            }
            Point point = caclulatePoint(angle);
            ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(views.get(i), "translationX", 0.0f, point.x).setDuration(0);
            ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(views.get(i), "translationY", 0.0f, -point.y).setDuration(0);
            AnimatorSet set = new AnimatorSet();
            //set.setInterpolator(new OvershootInterpolator());
            set.playTogether(objectAnimatorX, objectAnimatorY);
            set.start();
        }
    }
public Point caclulatePoint(int angle) {
        Point point = new Point();
        double b = Math.toRadians(angle);
        double cos = Math.cos(b);
        double sin = Math.sin(b);
        double x = raduis * sin;
        double y = raduis * cos;
        point.set((int) x, (int) y);
        //Log.e("values", "values:" + x + "---" + y + "---" + angle);
        return point;
    }

初始菜单view的位置

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        for (int i = 0;i<getChildCount();i++){
            View view = instance.getChildAt(i);
            int cLeft = getMeasuredWidth()/2-view.getMeasuredWidth()/2;
            int cTop = getMeasuredHeight()/2-view.getMeasuredHeight()/2;
            int cRight = cLeft+view.getMeasuredWidth();
            int cBottom = cTop+view.getMeasuredHeight();
            Log.e("cLeft","cLeft:"+cLeft+"........."+cTop+"........."+cRight+"........"+cBottom);
            view.layout(cLeft,cTop,cRight,cBottom);
        }
    }

根据菜单view的大小和旋转的角度(360/菜单view数量)计算出最小旋转半径和最小父view的宽度和高度,这样的好处是当菜单view多的时候,旋转半径会加大,保证菜单view不会重叠在一起;

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int count = this.getChildCount();
        if(count==0){
            return;
        }
        Log.e("count","count:"+count);
        for (int i = 0; i < count; i++) {
            View view = getChildAt(i);
            measureChild(view, widthMeasureSpec, heightMeasureSpec);
        }
        View childView = null;
        int childH = 0;
        int childW = 0;
        if(count>0){
            childView = getChildAt(0);
            childH = childView.getMeasuredHeight();
            childW = childView.getMeasuredWidth();
        }
        //最小弦长
        double minL = Math.sqrt(childW*childW+childH*childH);
        int angle = wAngle /count;
        //最小半径 目的是动态设置布局的宽高,以防止子view重叠在一起
        double minR = minL/(2*Math.sin(Math.toRadians(angle)))+minL/2;
        raduis = minR;
        Log.e("minR","minR:"+minR);
        //最小宽高;
        int parentW = (int)(minR+childH/2)*2;
        if(appWidthHeight<parentW){
            appWidthHeight = parentW;
        }
        setMeasuredDimension((int)appWidthHeight,(int)appWidthHeight);
    }

根据手势计算出旋转的角度(余弦定理)通过setRotation方法可实现旋转,当父view旋转的时候,子view在线程里自转达到菜单view的旋转

@Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction() & MotionEvent.ACTION_MASK;
        if (action == MotionEvent.ACTION_DOWN) {
            oriLeft = v.getLeft();
            oriRight = v.getRight();
            oriTop = v.getTop();
            oriBottom = v.getBottom();
            lastY = (int) event.getRawY();
            lastX = (int) event.getRawX();
            oriRotation = v.getRotation();
            Log.d(TAG, "ACTION_DOWN: " + oriRotation);
        }
        delDrag(v, event, action);
        invalidate();

        return false;
    }

    /**
     * 处理拖动事件
     *
     * @param v
     * @param event
     * @param action
     */

    protected void delDrag(View v, MotionEvent event, int action) {
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                int dx = (int) event.getRawX() - lastX;
                int dy = (int) event.getRawY() - lastY;
                Point center = new Point(oriLeft + (oriRight - oriLeft) / 2, oriTop + (oriBottom - oriTop) / 2);
                Point first = new Point(lastX, lastY);
                Point second = new Point((int) event.getRawX(), (int) event.getRawY());
                oriRotation += angle(center, first, second);
                Log.e("oriRotation","oriRotation:"+oriRotation);
                if(Float.isNaN(oriRotation)){
                    return;
                }
                v.setRotation(oriRotation);
                //View view = instance.getChildAt(getChildCount()-1);
                //view.setRotation(-oriRotation);
                instance.post(new ViewRunnables());
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                /*float endRotation = instance.getRotation();
                int angle = (wAngle / (getChildCount()+1));
                if(endRotation<(angle-wAngle)||endRotation>(wAngle-angle)){
                    //oriRotation = 0;
                    ObjectAnimator animator = ObjectAnimator.ofFloat(instance,"rotation",endRotation,endRotation,0);
                    animator.setDuration(300).start();
                    animator.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            for (int i = 0;i<getChildCount();i++){
                                if(i!=(getChildCount()-1)){
                                }
                                View view = instance.getChildAt(i);
                                view.setRotation(0);
                            }
                        }
                    });
                }*/
                //instance.post(new ViewRunnables());
                break;
        }
    }

    public class ViewRunnables implements Runnable {
        @Override
        public void run() {
           for (int i = 0;i<getChildCount();i++){
               if(i!=(getChildCount()-1)){
               }
               View view = instance.getChildAt(i);
               view.setRotation(-oriRotation);
           }
        }
    }

滑动时菜单view不可点击,静止时可点击

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()){
            case MotionEvent.ACTION_MOVE:
                return  true;
            case MotionEvent.ACTION_DOWN:
                oriLeft = instance.getLeft();
                oriRight = instance.getRight();
                oriTop = instance.getTop();
                oriBottom = instance.getBottom();
                lastY = (int) ev.getRawY();
                lastX = (int) ev.getRawX();
                oriRotation = instance.getRotation();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

demo地址

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值