Android(安卓)自定义进度条
16lz
2022-01-02
Android 自定义进度条
文章目录
- Android 自定义进度条
- 效果
- 目录
- 前言
- 正文
- `HorizontalProgressBarWithNumber`
- 实现
- 总结
- 感谢
效果
国际惯例,效果图奉上
目录
前言
写在前面,由于之前其实已经写了部分自定义View的方法,所以本来应该按照之前的系列,来进行下载暂停动画进度条,但是我把之前的圆形进度条和开始暂停动画效果合并后,出现了一点小问题,让我发现之前写的自定义View
,没有使我真正的了解自定义View
,那么我觉得还是有很大的问题;那么之后依旧会努力的写自定义View,初步先写静态的自定义View
,之后,加上动画的自定义VIew
,后续在加上相应的触摸事件,努力的把自定义VIew
的方法方式全部给记录下来;努力的融汇贯通。
正文
HorizontalProgressBarWithNumber
横向的进度条;里面有详细的注释
-
首先继承自
ProgressBar
,这个是对基础的ProgressBar
进行进一步的自定义,public class HorizontalProgressBarWithNumber extends ProgressBar{ ******}
-
当我们想要自定义
VIew
的时候,先要构思效果,那么就要设置各种属性,如下
/** * 设置各种默认值 */private static final int DEFAULT_TEXT_SIZE = 10;private static final int DEFAULT_TEXT_COLOR = 0XFFFC00D1;private static final int DEFAULT_COLOR_UNREACHED_COLOR = 0xFFd3d6da;private static final int DEFAULT_HEIGHT_REACHED_PROGRESS_BAR = 2;private static final int DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR = 2;private static final int DEFAULT_SIZE_TEXT_OFFSET = 10;/** * painter of all drawing things 所有画图所用的画笔 */protected Paint mPaint = new Paint();/** * color of progress number 进度号码的颜色 */protected int mTextColor = DEFAULT_TEXT_COLOR;/** * size of text (sp) 文本的大小 */protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);/** * offset of draw progress 进度条文本补偿宽度 */protected int mTextOffset = dp2px(DEFAULT_SIZE_TEXT_OFFSET);/** * height of reached progress bar 进度条高度 */protected int mReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_REACHED_PROGRESS_BAR);/** * color of reached bar 成功的文本颜色 */protected int mReachedBarColor = DEFAULT_TEXT_COLOR;/** * color of unreached bar 未完成的bar颜色 */protected int mUnReachedBarColor = DEFAULT_COLOR_UNREACHED_COLOR;/** * height of unreached progress bar 未覆盖的进度条高度 */protected int mUnReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR);/** * view width except padding 除padding外的视图宽度 */protected int mRealWidth;protected boolean mIfDrawText = true;protected static final int VISIBLE = 0;
- 自定义构造函数
public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs){this(context, attrs, 0);}public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs,int defStyle){super(context, attrs, defStyle);obtainStyledAttributes(attrs);//初始化参数mPaint.setTextSize(mTextSize);//文本大小mPaint.setColor(mTextColor);//文本颜色}
- 接下来就是初始化的代码:
/** * get the styled attributes 获取属性的样式 * * @param attrs */private void obtainStyledAttributes(AttributeSet attrs){// init values from custom attributesfinal TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.HorizontalProgressBarWithNumber);mTextColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_text_color,DEFAULT_TEXT_COLOR);mTextSize = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_text_size,mTextSize);mReachedBarColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_reached_color,mTextColor);mUnReachedBarColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_unreached_color,DEFAULT_COLOR_UNREACHED_COLOR);mReachedProgressBarHeight = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_reached_bar_height,mReachedProgressBarHeight);mUnReachedProgressBarHeight = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_unreached_bar_height,mUnReachedProgressBarHeight);mTextOffset = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_text_offset,mTextOffset);int textVisible = attributes.getInt(R.styleable.HorizontalProgressBarWithNumber_progress_text_visibility,VISIBLE);if (textVisible != VISIBLE){mIfDrawText = false;}attributes.recycle();}
- 上面当中的参数和属性都在
attr.xml
中声明的,如下
<resources> <declare-styleable name="HorizontalProgressBarWithNumber"> <attr name="progress_unreached_color" format="color" /> <attr name="progress_reached_color" format="color" /> <attr name="progress_reached_bar_height" format="dimension" /> <attr name="progress_unreached_bar_height" format="dimension" /> <attr name="progress_text_size" format="dimension" /> <attr name="progress_text_color" format="color" /> <attr name="progress_text_offset" format="dimension" /> <attr name="progress_text_visibility" format="enum"> <enum name="visible" value="0" /> <enum name="invisible" value="1" /> attr> declare-styleable> <declare-styleable name="RoundProgressBarWidthNumber"> <attr name="radius" format="dimension" /> declare-styleable>resources>
-
接下来,我们要重写
onMeasure
方法,在其中获取父控件传递过来的参数protected synchronized void onMeasure(int widthMeasureSpec,int heightMeasureSpec){int width = MeasureSpec.getSize(widthMeasureSpec);int height = measureHeight(heightMeasureSpec);//高度setMeasuredDimension(width, height);//必须调用该方法来存储View经过测量的到的宽度和高度mRealWidth = getMeasuredWidth() - getPaddingRight() - getPaddingLeft();//真正的宽度值是减去左右padding}
-
其中
measureHeight()
方法是获取视图的高度,视图的样式中拥有进度条和文本,要判断文本的高度和进度条的高度;
/** * EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比如我们指定了确定的dp值和macth_parent的情况。 *AT_MOST:当前控件不能超过一个固定的最大值,一般是wrap_content的情况。 *UNSPECIFIED:当前控件没有限制,要多大就有多大,这种情况很少出现。 * @param measureSpec * @return 视图的高度 */private int measureHeight(int measureSpec){int result = 0;int specMode = MeasureSpec.getMode(measureSpec);//父布局告诉我们控件的类型int specSize = MeasureSpec.getSize(measureSpec);//父布局传过来的视图大小if (specMode == MeasureSpec.EXACTLY){result = specSize;} else{/** * mPaint.descent() 最高点的高度 * mPaint.ascent() 最低点的高度 */float textHeight = (mPaint.descent() - mPaint.ascent());// 设置文本的高度/** * Math.abs() 返回绝对值 * Math.max 返回最大值 * Math.min 返回最小值 */result = (int) (getPaddingTop() + getPaddingBottom() + Math.max(Math.max(mReachedProgressBarHeight,mUnReachedProgressBarHeight), Math.abs(textHeight)));if (specMode == MeasureSpec.AT_MOST){result = Math.min(result, specSize);}}return result;}
- 以上全部完成后,就可以开始
onDraw()
方法了;
/** * 开始画 */@Overrideprotected synchronized void onDraw(Canvas canvas){canvas.save();/** * 设置偏移后的坐标原点 以原来为基础上偏移后, 例如: (100,100), translate(1,1), 坐标原点(101,101); */canvas.translate(getPaddingLeft(), getHeight() / 2);boolean noNeedBg = false;float radio = getProgress() * 1.0f / getMax();//设置进度float progressPosX = (int) (mRealWidth * radio);//设置当前进度的宽度String text = getProgress() + "%";//设置文本// mPaint.getTextBounds(text, 0, text.length(), mTextBound);float textWidth = mPaint.measureText(text);//返回文本的宽度float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;//设置文本的高度if (progressPosX + textWidth > mRealWidth){//当文本和当前进度的宽度大于bar的宽度时progressPosX = mRealWidth - textWidth;noNeedBg = true;}// draw reached bar 画出barfloat endX = progressPosX - mTextOffset / 2;//绘制已到达的进度if (endX > 0){//绘制已到达的进度mPaint.setColor(mReachedBarColor);mPaint.setStrokeWidth(mReachedProgressBarHeight);canvas.drawLine(0, 0, endX, 0, mPaint);}// draw progress bar// measure text boundif (mIfDrawText){//绘制文本mPaint.setColor(mTextColor);canvas.drawText(text, progressPosX, -textHeight, mPaint);}// draw unreached barif (!noNeedBg){//绘制未到达的进度条float start = progressPosX + mTextOffset / 2 + textWidth;mPaint.setColor(mUnReachedBarColor);mPaint.setStrokeWidth(mUnReachedProgressBarHeight);canvas.drawLine(start, 0, mRealWidth, 0, mPaint);}canvas.restore();}
- 在分享两个转换的方法
/** * dp 2 px * * @param dpVal */protected int dp2px(int dpVal){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal, getResources().getDisplayMetrics());}/** * sp 2 px * * @param spVal * @return */protected int sp2px(int spVal){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal, getResources().getDisplayMetrics());}
- 最后自定义
View
全部实现,奉上全部代码
public class HorizontalProgressBarWithNumber extends ProgressBar{/** * 设置各种默认值 */private static final int DEFAULT_TEXT_SIZE = 10;private static final int DEFAULT_TEXT_COLOR = 0XFFFC00D1;private static final int DEFAULT_COLOR_UNREACHED_COLOR = 0xFFd3d6da;private static final int DEFAULT_HEIGHT_REACHED_PROGRESS_BAR = 2;private static final int DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR = 2;private static final int DEFAULT_SIZE_TEXT_OFFSET = 10;/** * painter of all drawing things 所有画图所用的画笔 */protected Paint mPaint = new Paint();/** * color of progress number 进度号码的颜色 */protected int mTextColor = DEFAULT_TEXT_COLOR;/** * size of text (sp) 文本的大小 */protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);/** * offset of draw progress 进度条文本补偿宽度 */protected int mTextOffset = dp2px(DEFAULT_SIZE_TEXT_OFFSET);/** * height of reached progress bar 进度条高度 */protected int mReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_REACHED_PROGRESS_BAR);/** * color of reached bar 成功的文本颜色 */protected int mReachedBarColor = DEFAULT_TEXT_COLOR;/** * color of unreached bar 未完成的bar颜色 */protected int mUnReachedBarColor = DEFAULT_COLOR_UNREACHED_COLOR;/** * height of unreached progress bar 未覆盖的进度条高度 */protected int mUnReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR);/** * view width except padding 除padding外的视图宽度 */protected int mRealWidth;protected boolean mIfDrawText = true;protected static final int VISIBLE = 0;public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs){this(context, attrs, 0);}public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs,int defStyle){super(context, attrs, defStyle);obtainStyledAttributes(attrs);//初始化参数mPaint.setTextSize(mTextSize);//文本大小mPaint.setColor(mTextColor);//文本颜色}@Overrideprotected synchronized void onMeasure(int widthMeasureSpec,int heightMeasureSpec){int width = MeasureSpec.getSize(widthMeasureSpec);int height = measureHeight(heightMeasureSpec);//高度setMeasuredDimension(width, height);//必须调用该方法来存储View经过测量的到的宽度和高度mRealWidth = getMeasuredWidth() - getPaddingRight() - getPaddingLeft();//真正的宽度值是减去左右padding}/** * EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比如我们指定了确定的dp值和macth_parent的情况。 *AT_MOST:当前控件不能超过一个固定的最大值,一般是wrap_content的情况。 *UNSPECIFIED:当前控件没有限制,要多大就有多大,这种情况很少出现。 * @param measureSpec * @return 视图的高度 */private int measureHeight(int measureSpec){int result = 0;int specMode = MeasureSpec.getMode(measureSpec);//父布局告诉我们控件的类型int specSize = MeasureSpec.getSize(measureSpec);//父布局传过来的视图大小if (specMode == MeasureSpec.EXACTLY){result = specSize;} else{/** * mPaint.descent() 最高点的高度 * mPaint.ascent() 最低点的高度 */float textHeight = (mPaint.descent() - mPaint.ascent());// 设置文本的高度/** * Math.abs() 返回绝对值 * Math.max 返回最大值 * Math.min 返回最小值 */result = (int) (getPaddingTop() + getPaddingBottom() + Math.max(Math.max(mReachedProgressBarHeight,mUnReachedProgressBarHeight), Math.abs(textHeight)));if (specMode == MeasureSpec.AT_MOST){result = Math.min(result, specSize);}}return result;}/** * get the styled attributes 获取属性的样式 * * @param attrs */private void obtainStyledAttributes(AttributeSet attrs){// init values from custom attributesfinal TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.HorizontalProgressBarWithNumber);mTextColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_text_color,DEFAULT_TEXT_COLOR);mTextSize = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_text_size,mTextSize);mReachedBarColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_reached_color,mTextColor);mUnReachedBarColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_unreached_color,DEFAULT_COLOR_UNREACHED_COLOR);mReachedProgressBarHeight = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_reached_bar_height,mReachedProgressBarHeight);mUnReachedProgressBarHeight = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_unreached_bar_height,mUnReachedProgressBarHeight);mTextOffset = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_text_offset,mTextOffset);int textVisible = attributes.getInt(R.styleable.HorizontalProgressBarWithNumber_progress_text_visibility,VISIBLE);if (textVisible != VISIBLE){mIfDrawText = false;}attributes.recycle();}/** * 开始画 */@Overrideprotected synchronized void onDraw(Canvas canvas){canvas.save();/** * 设置偏移后的坐标原点 以原来为基础上偏移后, 例如: (100,100), translate(1,1), 坐标原点(101,101); */canvas.translate(getPaddingLeft(), getHeight() / 2);boolean noNeedBg = false;float radio = getProgress() * 1.0f / getMax();//设置进度float progressPosX = (int) (mRealWidth * radio);//设置当前进度的宽度String text = getProgress() + "%";//设置文本// mPaint.getTextBounds(text, 0, text.length(), mTextBound);float textWidth = mPaint.measureText(text);//返回文本的宽度float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;//设置文本的高度if (progressPosX + textWidth > mRealWidth){//当文本和当前进度的宽度大于bar的宽度时progressPosX = mRealWidth - textWidth;noNeedBg = true;}// draw reached bar 画出barfloat endX = progressPosX - mTextOffset / 2;//设置文本补偿宽度if (endX > 0){mPaint.setColor(mReachedBarColor);mPaint.setStrokeWidth(mReachedProgressBarHeight);canvas.drawLine(0, 0, endX, 0, mPaint);}// draw progress bar// measure text boundif (mIfDrawText){mPaint.setColor(mTextColor);canvas.drawText(text, progressPosX, -textHeight, mPaint);}// draw unreached barif (!noNeedBg){float start = progressPosX + mTextOffset / 2 + textWidth;mPaint.setColor(mUnReachedBarColor);mPaint.setStrokeWidth(mUnReachedProgressBarHeight);canvas.drawLine(start, 0, mRealWidth, 0, mPaint);}canvas.restore();}/** * dp 2 px * * @param dpVal */protected int dp2px(int dpVal){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal, getResources().getDisplayMetrics());}/** * sp 2 px * * @param spVal * @return */protected int sp2px(int spVal){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal, getResources().getDisplayMetrics());}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mRealWidth = w - getPaddingRight() - getPaddingLeft();}}
实现
xml
界面
mainActivity()
实现
public class MainActivity extends Activity {private HorizontalProgressBarWithNumber mProgressBar;private Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {int progress = mProgressBar.getProgress();mProgressBar.setProgress(++progress);if (progress >= 100) {mHandler.removeMessages(MSG_PROGRESS_UPDATE);}mHandler.sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE, 100);};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mProgressBar = (HorizontalProgressBarWithNumber) findViewById(R.id.id_progressbar01);mHandler.sendEmptyMessage(MSG_PROGRESS_UPDATE);}
- 以上就是全部功能的实现了
总结
自定义View
- 先构思效果
- 根据效果 , 声明配置相应参数
- 想好怎么计算
View
的宽度和高度 - 如果画出来
- 开始做吧
感谢
这个是从鸿阳大神的Github上找到的例子,嗯,值得学习,感谢鸿阳大神;
更多相关文章
- Android应用开发——TextView控件属性列表
- 详细整理iOS中UITableView的性能优化
- Android(安卓)TextView属性详解
- android 弹出的软键盘遮挡住EditText文本框的解决方案
- Android(安卓)控件四 EditText 控件
- 理解android中ListFragment和Loader
- android EditText中的inputType
- Android中TextView和EditView常用属性设置
- Android(安卓)TextView使用及性能优化