Android-自定義View-自定義進度條
阿新 • • 發佈:2019-01-07
眼看6月到了,由於前段時間域名備案等原因,伺服器關閉了差不多一個月,所以沒更新文章,索性今天補一篇吧,準備寫一個簡單的自定義View,就拿進度條做這個需求吧,雖然簡單,但是也包含了基本自定義View的幾要素,比如自定義屬性、重寫測量、重寫繪製等功能。
需求分析:
- 1.進度通過繪製線條實現。
- 2.進度文字跟隨當前進度實時變化,並非一直顯示在固定位置。
- 3.控制元件未給出寬高屬性時,我們需要給出預設值,具體以文字大小而定。
- 4.為了滿足開發需求,自定義屬性要多,能夠最大程度的讓開發者控制View的某個屬性。
效果圖:
開始擼碼:
新建類繼承View,並實現構造方法:
public class ProgressBarView extends View {
public ProgressBarView(Context context) {
this(context, null);
}
public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
}
新建attrs.xml,並編寫自定義屬性:
<declare-styleable name="ProgressBarView">
<attr name="progress_left_color" format="color" />
<attr name="progress_center_color" format="color" />
<attr name="progress_right_color" format="color" />
<attr name="progress_left_height" format="dimension" />
<attr name="progress_right_height" format="dimension" />
<attr name="progress_text_size" format="dimension" />
<attr name="progress_current_progress" format="integer" />
</declare-styleable>
宣告變數,並接收自定義屬性的值(記得初始化方法在構造中呼叫):
/**
* 畫筆
*/
private Paint mPaint;
/**
* 預設進度值
*/
private int mProgress = 0;
/**
* 進度邊距
*/
private int mProgressPadding = dp2px(2);
/**
* 控制元件高度
*/
private int mHeight = 0;
/**
* 字型大小
*/
float mTextSize = 18f;
/**
* 左邊進度條顏色
*/
int mLeftColor = Color.RED;
/**
* 中間文字條顏色
*/
int mCenterColor = Color.RED;
/**
* 右邊進度條顏色
*/
int mRightColor = Color.RED;
/**
* 左邊進度條高度
*/
int mLeftHeight = dp2px(10);
/**
* 右邊進度條高度
*/
int mRightHeight = dp2px(10);
/**
* 初始化
*/
private void init(AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);//抗鋸齒
mPaint.setColor(Color.RED);//畫筆顏色
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
mLeftColor = ta.getColor(R.styleable.ProgressBarView_progress_left_color, mLeftColor);
mCenterColor = ta.getColor(R.styleable.ProgressBarView_progress_center_color, mCenterColor);
mRightColor = ta.getColor(R.styleable.ProgressBarView_progress_right_color, mRightColor);
mLeftHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_left_height, mLeftHeight);
mRightHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_right_height, mRightHeight);
mTextSize = ta.getDimension(R.styleable.ProgressBarView_progress_text_size, mTextSize);
mProgress = ta.getInt(R.styleable.ProgressBarView_progress_current_progress, mProgress);
ta.recycle();
mPaint.setTextSize(mTextSize);//設定文字大小,後續好測量文字高度
}
重寫 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法測量自身:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthVal = MeasureSpec.getSize(widthMeasureSpec);//預設使用者需要給出明確值,所以不判斷模式
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(widthVal, height);//設定了測量值後,可以獲取測量值。
}
/**
* 測量高度
*
* @param heightMeasureSpec
* @return
*/
private int measureHeight(int heightMeasureSpec) {
int result;
int mode = MeasureSpec.getMode(heightMeasureSpec);//得到測量模式
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {//使用者給了精確值
result = size;
} else { //MeasureSpec.UNSPECIFIED MeasureSpec.AT_MOST 未指定明確引數
int textHeight = (int) (mPaint.descent() - mPaint.ascent());//得到文字高度
result = getPaddingTop() + getPaddingBottom() + Math.max(mHeight, textHeight);//高度等於進度條高度和文字高度中最高的為準,並且加上padding值
if (mode == MeasureSpec.AT_MOST) {//給定了最大值
result = Math.min(result, size);
}
}
return result;
}
重寫 protected void onDraw(Canvas canvas)並實現自定義檢視繪製:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mHeight = getMeasuredHeight();
String str = mProgress + "%";
float textWidth = mPaint.measureText(str);//文字寬度
float width = getMeasuredWidth() - textWidth - getPaddingLeft() - getPaddingRight() - mProgressPadding * 2;//控制元件寬度-文字寬度-padding=進度條總寬度
float currentProgress = getPaddingLeft() + width * mProgress / 100;//當前進度應該繪製的位置
mPaint.setColor(mLeftColor);
mPaint.setStrokeWidth(mLeftHeight);//畫筆寬度
canvas.drawLine(getPaddingLeft(), mHeight / 2, currentProgress, mHeight / 2, mPaint);
int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
mPaint.setColor(mCenterColor);
canvas.drawText(str, currentProgress + mProgressPadding, mHeight / 2 + y, mPaint);
mPaint.setColor(mRightColor);
mPaint.setStrokeWidth(mRightHeight);
canvas.drawLine(currentProgress + textWidth + mProgressPadding * 2, mHeight / 2, getMeasuredWidth() - getPaddingRight(), mHeight / 2, mPaint);
}
在佈局中引用編寫的控制元件,並設定自定義屬性:
<view.peakchao.view.view.ProgressBarView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:progress_center_color="#ff00ff"
app:progress_current_progress="80"
app:progress_left_color="#f00"
app:progress_left_height="1dp"
app:progress_right_color="#0f0"
app:progress_right_height="2dp"
app:progress_text_size="30dp" />
至此控制元件已經能夠正常顯示了,但是由於目前只是寫個Demo,還有沒考慮的東西,比如各個屬性的get和set方法等,需要自己根據需求完善。
附上ProgressBarView完整程式碼:
/**
* Created by Chao 2018/6/1 on 17:49
* description
*/
public class ProgressBarView extends View {
/**
* 畫筆
*/
private Paint mPaint;
/**
* 預設進度值
*/
private int mProgress = 0;
/**
* 進度邊距
*/
private int mProgressPadding = dp2px(2);
/**
* 控制元件高度
*/
private int mHeight = 0;
/**
* 字型大小
*/
float mTextSize = 18f;
/**
* 左邊進度條顏色
*/
int mLeftColor = Color.RED;
/**
* 中間文字條顏色
*/
int mCenterColor = Color.RED;
/**
* 右邊進度條顏色
*/
int mRightColor = Color.RED;
/**
* 左邊進度條高度
*/
int mLeftHeight = dp2px(10);
/**
* 右邊進度條高度
*/
int mRightHeight = dp2px(10);
public ProgressBarView(Context context) {
this(context, null);
}
public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
/**
* 初始化
*/
private void init(AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);//抗鋸齒
mPaint.setColor(Color.RED);//畫筆顏色
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
mLeftColor = ta.getColor(R.styleable.ProgressBarView_progress_left_color, mLeftColor);
mCenterColor = ta.getColor(R.styleable.ProgressBarView_progress_center_color, mCenterColor);
mRightColor = ta.getColor(R.styleable.ProgressBarView_progress_right_color, mRightColor);
mLeftHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_left_height, mLeftHeight);
mRightHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_right_height, mRightHeight);
mTextSize = ta.getDimension(R.styleable.ProgressBarView_progress_text_size, mTextSize);
mProgress = ta.getInt(R.styleable.ProgressBarView_progress_current_progress, mProgress);
ta.recycle();
mPaint.setTextSize(mTextSize);//設定文字大小,後續好測量文字高度
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);//得到測量模式
int widthVal = MeasureSpec.getSize(widthMeasureSpec);//預設使用者需要給出明確值,所以不判斷模式
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(widthVal, height);//設定了測量值後,可以獲取測量值。
//mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
/**
* 測量高度
*
* @param heightMeasureSpec
* @return
*/
private int measureHeight(int heightMeasureSpec) {
int result;
int mode = MeasureSpec.getMode(heightMeasureSpec);//得到測量模式
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {//使用者給了精確值
result = size;
} else { //MeasureSpec.UNSPECIFIED MeasureSpec.AT_MOST 未指定明確引數
int textHeight = (int) (mPaint.descent() - mPaint.ascent());//得到文字高度
result = getPaddingTop() + getPaddingBottom() + Math.max(mHeight, textHeight);//高度等於進度條高度和文字高度中最高的為準,並且加上padding值
if (mode == MeasureSpec.AT_MOST) {//給定了最大值
result = Math.min(result, size);
}
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mHeight = getMeasuredHeight();
String str = mProgress + "%";
float textWidth = mPaint.measureText(str);//文字寬度
float width = getMeasuredWidth() - textWidth - getPaddingLeft() - getPaddingRight() - mProgressPadding * 2;//控制元件寬度-文字寬度-padding=進度條總寬度
float currentProgress = getPaddingLeft() + width * mProgress / 100;//當前進度應該繪製的位置
mPaint.setColor(mLeftColor);
mPaint.setStrokeWidth(mLeftHeight);//畫筆寬度
canvas.drawLine(getPaddingLeft(), mHeight / 2, currentProgress, mHeight / 2, mPaint);
int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
mPaint.setColor(mCenterColor);
canvas.drawText(str, currentProgress + mProgressPadding, mHeight / 2 + y, mPaint);
mPaint.setColor(mRightColor);
mPaint.setStrokeWidth(mRightHeight);
canvas.drawLine(currentProgress + textWidth + mProgressPadding * 2, mHeight / 2, getMeasuredWidth() - getPaddingRight(), mHeight / 2, mPaint);
}
private int dp2px(int val) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, val, getResources().getDisplayMetrics());
}
private int sp2px(int val) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, val, getResources().getDisplayMetrics());
}
}