啥也不說先上圖,線看效果,代碼複製可用
第一:在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();
}
}
六,使用方法