啥也不说先上图,线看效果,代码复制可用
第一:在values下创建 attrs.xml,写需要的自定义属性;
第二:在 自定义View第三个构造方法中,获取自定义属性;
第三:重写onMeasure【不是必须的】;
第四:重写onDraw【所有绘制代码都写在onDraw】;
一,定义线条宽度,间隔背景颜色,间隔角度,两个textview的属性等
<declare-styleable name="RoundRateView">
<attr name="rrv_circleWidth" format="dimension" />
<attr name="rrv_intervalColor" format="color" />
<attr name="rrv_aboveTextColor" format="color" />
<attr name="rrv_belowTextColor" format="color" />
<attr name="rrv_intervalAngle" format="dimension" />
<attr name="rrv_belowTextSize" format="dimension" />
<attr name="rrv_aboveTextSize" format="dimension" />
<attr name="rrv_isShowText" format="boolean" />
</declare-styleable>
二,如图,具体看下面代码
三,如图,具体看代码
四,如图,具体看代码
五,是view代码:
public class RoundRateView extends View {
private int mCircleWidth; //圆环宽度
private float intervalAngle = 0.5f;//间隔角度
private int belowTextSize = 60;//下面的文字字体大小
private int aboveTextSize = 30;//上面的文字字体大小
private int aboveTextColor = Color.GRAY;//上面的文字 默认灰色
private int belowTextColor = Color.BLACK;//下面的文字 默认黑色
private int intervalColor = Color.GRAY;//间隔颜色 默认灰色
private int noMoneyColor = Color.DKGRAY;//总金额为0 默认灰色
private boolean isShowText = true; //是否显示中间文字 默认显示
private Paint mPaint;//圆环画笔
private Paint textPaint;//文字画笔
private List<Float> angleList = new ArrayList<>(); //所有的角度 集合
private List<Integer> colorList = new ArrayList<>(); //所有的色值 集合
private RectF oval;//圆环所在区域
private double allMoney;//总金额
private int colors[] = {Color.parseColor("#41A8FF")
, Color.parseColor("#86C8FF")
, Color.parseColor("#FF8B13")
, Color.parseColor("#FFB971")
, Color.parseColor("#FF8A77")
, Color.parseColor("#EEE685")
, Color.parseColor("#EECBAD")
, Color.parseColor("#EEAEEE")
, Color.parseColor("#EE3B3B")
, Color.parseColor("#EDEDED")};
public RoundRateView(Context context) {
super(context);
init(context, null, 0);
}
public RoundRateView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public RoundRateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray array = context.getTheme().obtainStyledAttributes(attrs,R.styleable.RoundRateView,defStyleAttr,0);
int n = array.getIndexCount();
for(int i=0;i<n;i++){
int attr = array.getIndex(i);
switch (attr){//这些属性都在xml里面设置。此处是默认
case R.styleable.RoundRateView_rrv_circleWidth:
mCircleWidth = array.getDimensionPixelSize(attr, (int) dip2px(15));
if(mCircleWidth<2){//最小2
mCircleWidth = 2;
}
break;
case R.styleable.RoundRateView_rrv_intervalAngle:
intervalAngle = array.getDimensionPixelSize(attr, (int) dip2px(1));
break;
case R.styleable.RoundRateView_rrv_belowTextSize:
belowTextSize = array.getDimensionPixelSize(attr, (int) dip2px(60));
break;
case R.styleable.RoundRateView_rrv_aboveTextSize:
aboveTextSize = array.getDimensionPixelSize(attr, (int) dip2px(30));
break;
case R.styleable.RoundRateView_rrv_aboveTextColor:
aboveTextColor = array.getColor(attr, Color.GRAY);
break;
case R.styleable.RoundRateView_rrv_belowTextColor:
belowTextColor = array.getColor(attr,Color.BLACK);
break;
case R.styleable.RoundRateView_rrv_intervalColor:
intervalColor = array.getColor(attr,Color.GRAY);
break;
case R.styleable.RoundRateView_rrv_isShowText:
isShowText = array.getBoolean(attr,true);
break;
}
}
array.recycle();//定义后属性对象回收
mPaint = new Paint();//初始化圆环画笔
mPaint.setAntiAlias(true);//抗锯齿
mPaint.setStyle(Paint.Style.STROKE); //设置绘画空心(比如画圆时画的是空心圆而不是实心圆)
mPaint.setStrokeWidth(mCircleWidth);//设置画笔线宽
textPaint = new Paint();//文字画笔
textPaint.setAntiAlias(true);
textPaint.setDither(true);//防抖动
}
/**
* 设置数据
* @param list
*/
public void setList(List<Double> list) {
float allIntervalAngle = 0f ;//所有间隔加起来的角度
float allModuleAngle; //所有模块加起来的角度 allModuleAngle + allIntervalAngle=180;
if(list.size()>colors.length){//不能大于定义的颜色数组长度
return;
}
angleList.clear();
colorList.clear();
allMoney = 0d;//总资产
for(int i=0;i<list.size();i++){
allMoney = allMoney+list.get(i);//资产累加
}
if(list.size() == 1){//只有一条数据//如果只有一条数据,就不要间隔
angleList.add(180f);//半圆
colorList.add(colors[0]);
}else{
for(int i=0;i<list.size()-1;i++){
if(list.get(i)!=0){//数据不为0
allIntervalAngle+= intervalAngle; //间隔角度累加
}
}
if(allIntervalAngle == intervalAngle){ //如果只有一条数据不为0,就不要间隔颜色
angleList.add(180f);
colorList.add(colors[0]);
}else{
allModuleAngle = 180- allIntervalAngle;//所有的颜色模块角度=180-所有间隔角度
float angle = 0;//每个金额所占角度累加
for(int i=0;i<list.size();i++){
if(list.get(i)!=0){
float e = (float) (list.get(i)/allMoney * allModuleAngle);//每个金额所占角度
if(i == list.size()-1){//如果是最后一个色块,所占角度就是剩余全部的角度
this.angleList.add(allModuleAngle - angle);
}else{
angle+=e;
this.angleList.add(e);
this.angleList.add(intervalAngle);
}
this.colorList.add(colors[i]);
this.colorList.add(intervalColor);
}
}
}
}
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthPixels = this.getResources().getDisplayMetrics().widthPixels;//获取屏幕宽
int heightPixels = this.getResources().getDisplayMetrics().heightPixels;//获取屏幕高
int width = MeasureSpec.getSize(widthMeasureSpec);
int hedight = MeasureSpec.getSize(heightMeasureSpec);
int minWidth = Math.min(widthPixels, width);//控件宽高不超过屏幕宽高
int minHedight = Math.min(heightPixels, hedight);//控件宽高不超过屏幕宽高
setMeasuredDimension(Math.min(minWidth, minHedight), Math.min(minWidth, minHedight));//设置宽高中的最小数为控件的实际宽高
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCircle(canvas);
if(isShowText){//是否显示文本
drawText(canvas);
}
}
private void drawCircle(Canvas canvas) {
if(oval==null){
int min = Math.min(getWidth() - mCircleWidth/2,getHeight() - mCircleWidth/2);
oval = new RectF(mCircleWidth/2,mCircleWidth/2,min,min);
}
float startAngle = -180f;//经过试验,-180这个值就是9点方向的位置 -90这个值就是12点方向的位置
if(allMoney==0){
mPaint.setColor(noMoneyColor);//总金额为0,给默认颜色
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);//首位有弧度
canvas.drawArc(oval, -180, 180, false, mPaint);//sweepAngle 跨度180画半圆 360圆
return;
}else{
//画一个底层圆环,颜色和间隙颜色一样,因为间隙的色块和其他色块之间会有小缝隙
mPaint.setColor(intervalColor);
mPaint.setStrokeWidth(mCircleWidth - 1); //宽度减1是防止底色溢出
mPaint.setStrokeCap(Paint.Cap.BUTT);
canvas.drawArc(oval, -180, 180, false, mPaint);
//左边开始的圆弧(没有设置线条开头圆弧,结束矩形的api),所以首位画两个,没找到其他好方法,有大神知道吗请留言回复
mPaint.setColor(colorList.get(0));
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawArc(oval, -180, 1, false, mPaint);
//右边结束的圆弧
mPaint.setColor(colorList.get(angleList.size()-1));
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawArc(oval, 0, 1, false, mPaint);
}
mPaint.setStrokeWidth(mCircleWidth);
for(int i=0;i<angleList.size();i++){
mPaint.setColor(colorList.get(i));
mPaint.setStrokeCap(Paint.Cap.BUTT);
// mPaint.setStrokeCap(Paint.Cap.ROUND);
// if(i==0 || i==angleList.size()-1){
// mPaint.setStrokeCap(Paint.Cap.ROUND);
// }else{
// mPaint.setStrokeCap(Paint.Cap.BUTT);
// }
if(i>0){
startAngle += angleList.get(i-1);
}
canvas.drawArc(oval,startAngle,angleList.get(i),false,mPaint);
}
}
private void drawText(Canvas canvas) {
int center = getWidth()/2;
String percent = "总资产(元)";
textPaint.setTextSize(aboveTextSize);
//防止文字边界超过内环边界 上面的文字大小减小 下面的文字大小也跟着减小
while (textPaint.measureText(percent)>getWidth() - 2*mCircleWidth){
aboveTextSize--;
belowTextSize--;
textPaint.setTextSize(aboveTextSize);
}
textPaint.setTextAlign(Paint.Align.CENTER);// 设置文字居中,文字的x坐标要注意
textPaint.setColor(aboveTextColor);
textPaint.setStrokeWidth(0);// 注意此处一定要重新设置宽度为0,否则绘制的文字会重叠
Rect bounds = new Rect();// 文字边框
textPaint.getTextBounds(percent,0,percent.length(),bounds);// 获得绘制文字的边界矩形
Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt();// 获取绘制Text时的四条线
int baseline = (int) (center - dip2px(20f));//字体所在位置,在中心点上方20dp
// int baseline = center+ (fontMetricsInt.bottom - fontMetricsInt.top) / 2 -fontMetricsInt.bottom;
canvas.drawText(percent,center,baseline,textPaint);
//总金额
percent = String.valueOf(allMoney);
textPaint.setColor(belowTextColor); // 设置文字颜色
textPaint.setTextSize(belowTextSize);
textPaint.setFakeBoldText(true);//加粗
//防止下面的文字超出内环边界
while (textPaint.measureText(percent)>getWidth()-2f*mCircleWidth) {
belowTextSize--;
textPaint.setTextSize(belowTextSize);
}
textPaint.getTextBounds(percent, 0, percent.length(), bounds); // 获得绘制文字的边界矩形
Paint.FontMetricsInt fontMetrics1 = textPaint.getFontMetricsInt(); // 获取绘制Text时的四条线
int baseline1 = center + (fontMetrics1.bottom - fontMetrics1.top) / 2 - fontMetrics1.bottom;
// int baseline1 = center + (fontMetrics1.bottom - fontMetrics1.top) / 2 - fontMetrics1.bottom+38*2;
canvas.drawText(percent, center, baseline1, textPaint); // 绘制文字
}
public static float dip2px(float dipValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (dipValue * scale + 0.5f);
}
public void setIsShowMoney(boolean isShowMoney) {
this.isShowText = isShowMoney;
invalidate();
}
}
六,使用方法