
設(shè)計(jì)圖.png
項(xiàng)目需要以上設(shè)計(jì)圖中進(jìn)度條樣式,百度了下沒有找到相同的,只能自己寫一個(gè)。先上完成的效果圖

截屏1.png

截屏2.png

截屏3.png

截屏4.png
上代碼
package com.wsf.myprogressbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.text.Layout;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.view.View;
/**
* 進(jìn)度條上方文字view
*
* @author wsf
*/
public class TextProgressView extends View {
private Paint paint;
private float percent;
private float total;
/**
* 進(jìn)度條文字顏色
*/
private int textColor;
/**
* 進(jìn)度條數(shù)字顏色
*/
private int numColor;
/**
* 進(jìn)度條數(shù)字
*/
private int number;
public TextProgressView(Context context) {
this(context, null);
}
public TextProgressView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TextProgressView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
paint.setAntiAlias(true);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
R.styleable.TextProgressView);
//獲取自定義屬性和默認(rèn)值
textColor = mTypedArray.getColor(R.styleable.TextProgressView_textColor, getResources().getColor(R.color.thimcolor));
numColor = mTypedArray.getColor(R.styleable.TextProgressView_numColor, getResources().getColor(R.color.color_ffb202));
mTypedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(getResources().getColor(R.color.thimcolor));// 設(shè)置累計(jì)顏色
//用SpannableString設(shè)置字體不同顏色大小
SpannableString spannableString = new SpannableString("剩余天數(shù): " + getNumber() + "天");
spannableString.setSpan(new ForegroundColorSpan(numColor), 5, spannableString.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(sp2px(16)), 5, spannableString.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
paint.setColor(textColor);// 設(shè)置累計(jì)顏色
paint.setTextSize(sp2px(16));
//計(jì)算文本寬高
Rect rect = new Rect();
String text = "剩余天數(shù): " + getNumber() + "天";
paint.getTextBounds(text, 0, text.length(), rect);
float textWidth = rect.width();
float textHeight = rect.height() + (2 * sp2px(4));
paint.setStrokeWidth(sp2px(1));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
Path path = new Path();
//先畫文本框
if (total != 0) {
if (percent == 0) { //如果進(jìn)度為0,則從0,0開始畫
path.moveTo(0, 0);
path.lineTo(0, textHeight);
path.lineTo((getWidth() * percent / total), getHeight());
path.lineTo((getWidth() * percent / total) + sp2px(5), textHeight);
path.lineTo(textWidth, textHeight);
path.lineTo(textWidth, 0);
path.close();
} else if (percent == total) {//如果進(jìn)度等于總和,則文本框右側(cè)貼著畫布右側(cè)
path.moveTo(getWidth() - textWidth, 0);
path.lineTo(getWidth() - textWidth, textHeight);
path.lineTo((getWidth() * percent / total) - sp2px(5), textHeight);
path.lineTo((getWidth() * percent / total), getHeight());
path.lineTo(getWidth(), textHeight);
path.lineTo(getWidth(), 0);
path.close();
} else {//其他情況按正常畫
path.moveTo((getWidth() * percent / total) - (textWidth * percent / total), 0);
path.lineTo((getWidth() * percent / total) - (textWidth * percent / total), textHeight);
if ((getWidth() * percent / total) - sp2px(5) < 0) {
path.lineTo(0, textHeight);
} else {
path.lineTo((getWidth() * percent / total) - sp2px(5), textHeight);
}
path.lineTo((getWidth() * percent / total), getHeight());
if ((getWidth() * percent / total) + sp2px(5) > getWidth()) {
path.lineTo(getWidth(), textHeight);
} else {
path.lineTo((getWidth() * percent / total) + sp2px(5), textHeight);
}
path.lineTo((getWidth() * percent / total) + (textWidth * (total - percent) / total), textHeight);
path.lineTo((getWidth() * percent / total) + (textWidth * (total - percent) / total), 0);
path.close();
}
} else {//如果總和為0,直接在最右端畫文本框
path.moveTo(getWidth() - textWidth, 0);
path.lineTo(getWidth() - textWidth, textHeight);
path.lineTo((getWidth() * percent / total) - sp2px(5), textHeight);
path.lineTo((getWidth() * percent / total), getHeight());
if ((getWidth() * percent / total) + sp2px(5) > getWidth()) {
path.lineTo(getWidth(), textHeight);
} else {
path.lineTo((getWidth() * percent / total) + sp2px(5), textHeight);
path.lineTo(getWidth(), textHeight);
}
path.lineTo(getWidth(), 0);
path.close();
}
canvas.drawPath(path, paint);
//畫文本
TextPaint paint1 = new TextPaint();
paint1.setTextSize(sp2px(12));
paint1.setColor(textColor);
paint1.setAntiAlias(true);
StaticLayout layout = new StaticLayout(spannableString, paint1, getWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
canvas.save();
if (total != 0) {
if (percent == 0){
canvas.translate(0 + sp2px(10), 0); //不知道為什么文字的畫布位置靠左,所以加10是為了文字可以顯示到文字框的中間
} else if (percent == total) {
canvas.translate(getWidth() - textWidth + sp2px(10), 0);
} else {
canvas.translate((getWidth() * percent / total) - (textWidth * percent / total) + sp2px(10), 0);
}
} else {
canvas.translate(getWidth() - textWidth + sp2px(10), 0);
}
layout.draw(canvas);
canvas.restore();
}
public float getPercent() {
return percent;
}
public void setPercent(float percent) {
this.percent = percent;
}
public float getTotal() {
return total;
}
public void setTotal(float total) {
this.total = total;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
private int dp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().density;
return (int) (v * value + 0.5f);
}
private int sp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (v * value + 0.5f);
}
}
package com.wsf.myprogressbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* 進(jìn)度條
*
* @author wsf
*/
public class DayProgressView extends View {
/**
* 畫筆對(duì)象的引用
*/
private Paint paint;
/**
* 進(jìn)度
*/
private float mRate;
/**
* 總共
*/
private float total;
/**
* 進(jìn)度條左側(cè)顏色
*/
private int leftColor;
/**
* 進(jìn)度條右側(cè)顏色
*/
private int rightColor;
/**
* 進(jìn)度條右側(cè)顏色
*/
private int defaultColor;
/**
* 進(jìn)度條左右兩端弧度
*/
private float mRadius;
private Path mPath;
private Path mPath1;
private Path mPath2;
public DayProgressView(Context context) {
this(context, null);
}
public DayProgressView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DayProgressView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
paint.setAntiAlias(true);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
R.styleable.DayProgressView);
// 獲取自定義屬性和默認(rèn)值
mRadius = mTypedArray.getDimension(R.styleable.DayProgressView_rectRadius, dp2px(10));
leftColor = mTypedArray.getColor(R.styleable.DayProgressView_leftColor, getResources().getColor(R.color.thimcolor));
rightColor = mTypedArray.getColor(R.styleable.DayProgressView_rightColor, getResources().getColor(R.color.color_ffb202));
defaultColor = mTypedArray.getColor(R.styleable.DayProgressView_defaultColor, getResources().getColor(R.color.white));
mTypedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//先畫背景色
paint.setColor(defaultColor);// 設(shè)置累計(jì)顏色
paint.setStyle(Paint.Style.FILL);//設(shè)置填滿
mPath2 = new Path();
mPath2.moveTo(0, 0);
//畫白色背景圖
RectF RoundRect = new RectF(0, 0, getHeights(canvas), getHeights(canvas));
//左端畫弧形,從270的角度開始逆時(shí)針畫180度
mPath2.arcTo(RoundRect, 270, -180);
mPath2.lineTo(getWidths(canvas), getHeights(canvas));
RectF RoundRect1 = new RectF(getWidths(canvas) - getHeights(canvas), 0, getWidths(canvas), getHeights(canvas));
//右端畫弧形,從90的角度開始逆時(shí)針畫180度
mPath2.arcTo(RoundRect1, 90, -180);
mPath2.close();
canvas.drawPath(mPath2,paint);
if (mRate == total){//當(dāng)前進(jìn)度等于全部時(shí),代表全部完成,左側(cè)顏色填充滿
paint.setColor(leftColor);// 藍(lán)色
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(mPath2,paint);
}else if (mRate == 0){//當(dāng)前進(jìn)度等于0時(shí),代表位開始,右側(cè)顏色填充滿
paint.setColor(rightColor);// 橙色
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(mPath2,paint);
}else {
//畫左側(cè)進(jìn)度條
paint.setColor(leftColor);// 藍(lán)色
paint.setStyle(Paint.Style.FILL);
mPath = new Path();
//從0,0位置開始畫
mPath.moveTo(0, 0);
//畫左端半圓,直徑是畫布的高
RectF rectF = new RectF(0, 0, getHeights(canvas), getHeights(canvas));
//左端畫弧形,從270的角度開始逆時(shí)針畫180度
mPath.arcTo(rectF, 270, -180);
//左右兩種顏色中間間隙是 getHeights(canvas) / 4
if (getWidths(canvas) * mRate / total < getHeights(canvas)/2){//當(dāng)進(jìn)度條的左側(cè)顏色小于左端半圓寬度時(shí),默認(rèn)是至少顯示左側(cè)半圓
mPath.lineTo(getHeights(canvas)/2 + getHeights(canvas) / 4, 0);
}else if (getWidths(canvas) - getHeights(canvas)/2 < getWidths(canvas) * mRate / total){//當(dāng)進(jìn)度條的右側(cè)顏色小于右端半圓寬度時(shí),默認(rèn)是至少顯示右側(cè)半圓
mPath.lineTo(getWidths(canvas) - getHeights(canvas) / 2 - getHeights(canvas) / 4 * 2, getHeights(canvas));
mPath.lineTo(getWidths(canvas) - getHeights(canvas) / 2 - getHeights(canvas) / 4, 0);
}else {//其他情況按正常進(jìn)度畫
mPath.lineTo((getWidths(canvas)) * mRate / total - getHeights(canvas) / 4, getHeights(canvas));
mPath.lineTo((getWidths(canvas)) * mRate / total, 0);
}
mPath.close();
canvas.drawPath(mPath, paint);
//畫右側(cè)進(jìn)度條
paint.setColor(rightColor);
paint.setStyle(Paint.Style.FILL);
mPath1 = new Path();
//右側(cè)判斷同左側(cè)判斷
if (getWidths(canvas) * mRate / total < getHeights(canvas)/2){
mPath1.moveTo(getHeights(canvas)/2 + getHeights(canvas) / 4 + getHeights(canvas) / 4, 0);
mPath1.lineTo(getHeights(canvas)/2 + getHeights(canvas) / 4, getHeights(canvas));
}else if (getWidths(canvas) - getHeights(canvas)/2 < getWidths(canvas) * mRate / total){
mPath1.moveTo(getWidths(canvas) - getHeights(canvas) / 2, 0);
mPath1.lineTo(getWidths(canvas) - getHeights(canvas) / 2 - getHeights(canvas) / 4, getHeights(canvas));
}else {
mPath1.moveTo(getWidths(canvas) * mRate / total + getHeights(canvas) / 4, 0);
mPath1.lineTo(getWidths(canvas) * mRate / total, getHeights(canvas));
}
mPath1.lineTo(getWidths(canvas), getHeights(canvas));
RectF rectF1 = new RectF(getWidths(canvas) - getHeights(canvas), 0, getWidths(canvas), getHeights(canvas));
//右端畫弧形,從90的角度開始逆時(shí)針畫180度
mPath1.arcTo(rectF1, 90, -180);
mPath1.close();
canvas.drawPath(mPath1, paint);
}
}
public float getRate() {
return mRate;
}
public void setRate(float mRate) {
this.mRate = mRate;
}
public int getLeftColor() {
return leftColor;
}
public void setLeftColor(int leftColor) {
this.leftColor = leftColor;
}
public int getRightColor() {
return rightColor;
}
public void setRightColor(int rightColor) {
this.rightColor = rightColor;
}
public float getRadius() {
return mRadius;
}
public void setRadius(float mRadius) {
this.mRadius = mRadius;
}
public int getDefaultColor() {
return defaultColor;
}
public void setDefaultColor(int defaultColor) {
this.defaultColor = defaultColor;
}
public float getTotal() {
return total;
}
public void setTotal(float total) {
this.total = total;
}
private float getWidths(Canvas canvas) {
return (float) canvas.getWidth();
}
private float getHeights(Canvas canvas) {
return (float) canvas.getHeight();
}
private int sp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (v * value + 0.5f);
}
private int dp2px(int value) {
float v = getContext().getResources().getDisplayMetrics().density;
return (int) (v * value + 0.5f);
}
}
這里是將下方的進(jìn)度條和上方顯示的文字分開寫了,因?yàn)轫?xiàng)目時(shí)間緊張,合在一起計(jì)算太麻煩,分開寫好計(jì)算,等以后有時(shí)間再合并,因?yàn)槭欠珠_顯示并且左右兩端是半圓形中間有斜著的分隔,所以在左右兩端文本框箭頭指示位置有問題,不過影響不大,除了合并,暫時(shí)沒想到好的辦法,有哪位朋友有方法可以評(píng)論告訴我,十分感謝!
package com.wsf.myprogressbar;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private EditText et_total;
private EditText et_rate;
private Button btn_refresh;
private DayProgressView dpvClassdetail;
private TextProgressView tpvClassdetail;
private TextView tvClassDetailStarttime;
private TextView tvClassDetailEndtime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_total = findViewById(R.id.et_total);
et_rate = findViewById(R.id.et_rate);
btn_refresh = findViewById(R.id.btn_refresh);
dpvClassdetail = findViewById(R.id.dpv_classdetail);
tpvClassdetail = findViewById(R.id.tpv_classdetail);
tvClassDetailStarttime = findViewById(R.id.tv_class_detail_starttime);
tvClassDetailEndtime = findViewById(R.id.tv_class_detail_endtime);
et_total.setText(10 + "");
et_rate.setText(3 + "");
refresh();
btn_refresh.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
refresh();
}
});
}
private void refresh() {
if (!TextUtils.isEmpty(et_total.getText().toString()) && !TextUtils.isEmpty(et_rate.getText().toString())) {
int total = Integer.parseInt(et_total.getText().toString());
int rate = Integer.parseInt(et_rate.getText().toString());
dpvClassdetail.setTotal(total);
if (total - rate < 0) {
dpvClassdetail.setRate(0);
tpvClassdetail.setPercent(0);
} else {
dpvClassdetail.setRate(rate);
tpvClassdetail.setPercent(rate);
}
tpvClassdetail.setNumber(total - rate);
tpvClassdetail.setTotal(total);
tpvClassdetail.postInvalidate();
dpvClassdetail.postInvalidate();
tvClassDetailStarttime.setText("開始時(shí)間 \n" + "2020.4.20");
tvClassDetailEndtime.setText("截止時(shí)間 \n" + "2020.5.20");
} else {
Toast.makeText(MainActivity.this, "請(qǐng)?zhí)顚懣偤团c進(jìn)度", Toast.LENGTH_LONG).show();
}
}
}
這里是mainActivity中使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<EditText
android:id="@+id/et_total"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="請(qǐng)?zhí)顚懣偤?
android:padding="5dp" />
<EditText
android:id="@+id/et_rate"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="請(qǐng)?zhí)顚戇M(jìn)度"
android:padding="5dp" />
<Button
android:id="@+id/btn_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="5dp"
android:text="刷新" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:orientation="horizontal"
android:paddingLeft="5dp"
android:paddingTop="15dp"
android:paddingRight="5dp"
android:paddingBottom="15dp">
<TextView
android:id="@+id/tv_class_detail_starttime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:text=""
android:textColor="#666666"
android:textSize="12sp" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="10dp"
android:layout_weight="1">
<com.wsf.myprogressbar.DayProgressView
android:id="@+id/dpv_classdetail"
android:layout_width="match_parent"
android:layout_height="11dp"
android:layout_alignParentBottom="true" />
<com.wsf.myprogressbar.TextProgressView
android:id="@+id/tpv_classdetail"
android:layout_width="match_parent"
android:layout_height="33dp" />
</RelativeLayout>
<TextView
android:id="@+id/tv_class_detail_endtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:text=""
android:textColor="#666666"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
demo中xml文件