在工程的res\values路径下新建一个attrs.xml的文件,并在其中添加如下代码:
ChildCount:表示圆盘上可以放几个按钮,它可以控制按钮之间的间隔;
StartAngle:表示第一个按钮的起始角度(30就表示存30度开始)。
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做为开关按钮要设置的大一点,要完全遮挡上面的其他按钮的大小
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);
}
}
});
}
}