之前在学习自定义View的时候看到鸿洋_的 《Android 打造形形色色的进度条 实现可以如此简单》 中自带百分比的进度条,于是照着例子自己实现了一下。下面是View的样子:
大家都知道自定义View的主要步骤:
- 自定义View的一些属性
- 在构造器中初始化属性
- 重写onMeasure()方法
- 重写onDraw()方法
下面就来实现第一步:
先在values文件夹中新建attrs.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="PercentProgressView"> <attr name="progress_color" format="color|reference"></attr> <attr name="progress" format="integer|float"></attr> <attr name="total" format="integer|float"></attr> <attr name="total_color" format="color|reference"></attr> <attr name="text_size" format="dimension"></attr> <attr name="progress_height" format="dimension"></attr> <attr name="text_offset" format="dimension"></attr> </declare-styleable> </resources>
|
好了我差不多就定义以上几种属性,有需要的可以在后面再添加。这样我们的第一步就完成了。下面我们就来看看第二步吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| private Paint mPaint;
private int mProgressColor;
private int mTotalColor;
private int mWidth;
private int mHeight;
private float mProgress;
private float mTotal;
private float mTextSize;
private float mTextOffset;
private float mProgressHeight;
private int realWidth;
private static final float TEXT_SIZE = 20;
private static final float TEXT_OFFSET = 10;
private static final float DEFAULT_PROGRESS_HEIGHT = 4;
public PercentProgressView(Context context) { this(context, null); }
public PercentProgressView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public PercentProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PercentProgressView); mProgressColor = a.getColor(R.styleable.PercentProgressView_progress_color, Color.RED); mTotalColor = a.getColor(R.styleable.PercentProgressView_total_color, Color.GRAY); mProgress = a.getFloat(R.styleable.PercentProgressView_progress, 0f); mTotal = a.getFloat(R.styleable.PercentProgressView_total, 100f); float defaultTextSize = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, TEXT_SIZE, getResources() .getDisplayMetrics()); mTextSize = a.getDimension(R.styleable.PercentProgressView_text_size, defaultTextSize); float defaultProgressHeight = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, DEFAULT_PROGRESS_HEIGHT, getResources() .getDisplayMetrics()); mProgressHeight = a.getDimension(R.styleable.PercentProgressView_progress_height, defaultProgressHeight); float defaultTextOffset = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, TEXT_OFFSET, getResources() .getDisplayMetrics()); mTextOffset = a.getDimension(R.styleable.PercentProgressView_text_offset, defaultTextOffset); a.recycle(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTextSize(mTextSize); mPaint.setStrokeWidth(mProgressHeight); mPaint.setColor(mProgressColor); }
|
在第二步中我们主要做的就是把上一步定义的属性在构造器中初始化,设置一些默认值以及创建一个新的Paint对象。其实并没什么难度,都是一些重复性的东西。
接下来要做的就是重写onMeasure()方法来测量View。宽度我们可以设置为match_parent,高度为可自定义,所以我们要测量一下高度。使用MeasureSpec.getMode(heightMeasureSpec)来判断用户设置的模式,如果是 MeasureSpec.EXACTLY 则不直接返回 MeasureSpec.getSize(heightMeasureSpec) 就可以了,不然的话要比较文字和进度条的高度,取两者的最大值。最后调用setMeasuredDimension(width,height)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); mWidth = MeasureSpec.getSize(widthMeasureSpec); mHeight = measureHeight(mode, size); setMeasuredDimension(mWidth, mHeight); realWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); }
private int measureHeight(int mode, int size) { int result; if (mode == MeasureSpec.EXACTLY) { result = size; } else { int h = (int) (getPaddingBottom() + getPaddingTop() + Math.max(mProgressHeight, Math.abs(mPaint.descent() - mPaint.ascent()))); result = h; if (mode == MeasureSpec.AT_MOST) { result = Math.min(h, size); } } return result; }
|
上面三步完成之后就到了最后的重点onDraw()方法了。根据思路我们应该先画出已完成进度的矩形,再画出百分比文字,最后画出未完成的进度。需要注意的是绘制文字的时候Y轴起点为文字的baseline,而不是文字的顶部。下面给出了绘制时大概的思路图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);
canvas.save(); mPaint.setColor(mProgressColor); float p = getPercent(); String s = (int) (p * 100) + "%"; float textWidth = mPaint.measureText(s); canvas.drawRect(getPaddingLeft(), (mHeight - mProgressHeight) / 2, getPaddingLeft() + (realWidth - textWidth - mTextOffset) * p, (mHeight + mProgressHeight) / 2, mPaint); float textHeight = Math.abs((mPaint.descent() + mPaint.ascent()) / 2); canvas.drawText(s, getPaddingLeft() + (realWidth - textWidth - mTextOffset) * p + mTextOffset / 2, textHeight + mHeight / 2, mPaint); mPaint.setColor(mTotalColor);
canvas.drawRect(getPaddingLeft() + (realWidth - textWidth - mTextOffset) * p + mTextOffset + textWidth, (mHeight - mProgressHeight) / 2, mWidth - getPaddingRight(), (mHeight + mProgressHeight) / 2, mPaint); canvas.restore(); }
|