Android实现圆形控制盘 圆形排列按钮ControlPanelView

 

效果图:

Android实现圆形控制盘 圆形排列按钮ControlPanelView_第1张图片

第一步 自定义控件命名空间属性设置:

在工程的res\values路径下新建一个attrs.xml的文件,并在其中添加如下代码:


    
        
        
    

ChildCount:表示圆盘上可以放几个按钮,它可以控制按钮之间的间隔;
StartAngle:表示第一个按钮的起始角度(30就表示存30度开始)。

第二步 自定义一个父布局ControlPanelView

package com.example.guo.bianmin.ui.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;

import com.example.guo.bianmin.R;

public class ControlPanelView extends ViewGroup {

    private Paint paint;
    private boolean isopen = false;//控件的开关状态
    private int bj;//圆形面板的当前半径,开关动画时会用到
    private ValueAnimator openAnimator;//打开的动画
    private ValueAnimator offAnimator;//关闭的动画

    public ControlPanelView(Context context) {
        this(context, null);
    }

    public ControlPanelView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ControlPanelView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initCustomAttrs(context, attrs);
        paint = new Paint();
        paint.setAntiAlias(true);
    }

    /**
     * 获取自定义属性
     */
    int esChildCount = 9;//预计圆环上按钮的个数
    int startAngle = -30;//第一个按钮的角度

    private void initCustomAttrs(Context context, AttributeSet attrs) {
        //获取自定义属性。
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ControlPanelView);
        //预计孩子的个数
        esChildCount = (int) ta.getFloat(R.styleable.ControlPanelView_ChildCount, 2);
        startAngle = (int) ta.getFloat(R.styleable.ControlPanelView_StartAngle, 0);
        ta.recycle();
    }

    int radius;//控件的最大半径
    int ItemRadius;//孩子控件的最大半径
    int centralX;//控件的圆心点
    int centralY;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthmode = MeasureSpec.getMode(widthMeasureSpec);
        int widthsize = MeasureSpec.getSize(widthMeasureSpec);
        int heightmode = MeasureSpec.getMode(heightMeasureSpec);
        int heightsize = MeasureSpec.getSize(heightMeasureSpec);
        int count = getChildCount();
        //从1开始,第0个作为中间的开关按钮
        for (int i = 0; i < count; i++) {
            View view = getChildAt(i);
            measureChild(view, widthMeasureSpec, heightMeasureSpec);
            //子视图的最大半径(除去第一个中间开关按钮)
            if (i != count - 1) {
                ItemRadius = Math.max(ItemRadius, (Math.max(view.getMeasuredWidth(), view.getMeasuredHeight()) / 2));
            }
        }
        radius = Math.min(widthsize, heightsize) / 2 - ItemRadius;
        centralX = widthsize / 2;
        centralY = heightsize / 2;
        //这里就不判断模式了,一般不会有人把控件设成wrap_content
        setMeasuredDimension(widthsize, heightsize);
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        int count = getChildCount();
        //间隔角度
        int interval = 360 / esChildCount;
        for (int j = 0; j < count; j++) {
            float[] xy = getCoordinatePoint(bj, startAngle + j * interval);
            View view = getChildAt(j);
            int wb = view.getMeasuredWidth() / 2;
            int hb = view.getMeasuredHeight() / 2;
            //最后一个子view不列到圆环上,而是放在圆圈中心做为开关按钮
            if (j == count - 1) {
                view.layout(centralX - wb, centralY - hb, centralX + wb, centralY + hb);
            } else {
                view.layout((int) xy[0] - wb, (int) xy[1] - hb, (int) xy[0] + wb, (int) xy[1] + hb);
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL);
        paint.setAlpha(30);
        canvas.drawCircle(centralX, centralY, bj, paint);
    }


    /**
     * 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
     */
    public float[] getCoordinatePoint(int radius, float cirAngle) {
        int mCenterX = centralX;
        int mCenterY = centralY;
        float[] point = new float[2];

        double arcAngle = Math.toRadians(cirAngle); //将角度转换为弧度
        if (cirAngle < 90) {
            point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
        } else if (cirAngle == 90) {
            point[0] = mCenterX;
            point[1] = mCenterY + radius;
        } else if (cirAngle > 90 && cirAngle < 180) {
            arcAngle = Math.PI * (180 - cirAngle) / 180.0;
            point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
        } else if (cirAngle == 180) {
            point[0] = mCenterX - radius;
            point[1] = mCenterY;
        } else if (cirAngle > 180 && cirAngle < 270) {
            arcAngle = Math.PI * (cirAngle - 180) / 180.0;
            point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
        } else if (cirAngle == 270) {
            point[0] = mCenterX;
            point[1] = mCenterY - radius;
        } else {
            arcAngle = Math.PI * (360 - cirAngle) / 180.0;
            point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
        }
        return point;
    }

    /**
     * 切换控件开关状态
     *
     * @return
     */
    public boolean switchControlPanel() {
        isopen = !isopen;
        if (isopen) {
            open();
        } else {
            off();
        }
        return isopen;
    }

    /**
     * 打开动画
     */
    private void open() {
        if (offAnimator != null) {
            offAnimator.end();
        }
        if (openAnimator == null) {
            openAnimator = ValueAnimator.ofInt(0, radius);
        }
        openAnimator.setDuration(500);
        openAnimator.setInterpolator(new OvershootInterpolator());
        openAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                bj = (int) valueAnimator.getAnimatedValue();
                invalidate();
                requestLayout();
            }
        });
        openAnimator.start();
    }

    /**
     * 关闭动画
     */
    private void off() {
        if (openAnimator != null) {
            openAnimator.end();
        }
        if (offAnimator == null) {
            offAnimator = ValueAnimator.ofInt(radius, 0);
        }
        offAnimator = ValueAnimator.ofInt(radius, 0);
        offAnimator.setDuration(500);
        offAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                bj = (int) valueAnimator.getAnimatedValue();
                invalidate();
                requestLayout();
            }
        });
        offAnimator.start();
    }
}

 

第三步 布局文件

最后一个imageView做为开关按钮要设置的大一点,要完全遮挡上面的其他按钮的大小



    

        

        

        

        

        

        
        
    

第四步 Activity中使用

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;

import com.example.guo.bianmin.R;
import com.example.guo.bianmin.ui.view.ControlPanelView;

public class testActicivity extends Activity{

    private ControlPanelView controlPanelView;
    private ImageView swechiv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        controlPanelView = findViewById(R.id.controlPanelView);
        swechiv = findViewById(R.id.swechiv);
        swechiv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                boolean isopen=controlPanelView.switchControlPanel();
                if(isopen){
                    swechiv.setImageResource(R.drawable.zhankai);
                }else{
                    swechiv.setImageResource(R.drawable.shouqi);
                }
            }
        });
    }
}

 

你可能感兴趣的:(Android实现圆形控制盘 圆形排列按钮ControlPanelView)