1.系统ProgressBar样式实现原理分析
普通的一直圆形转的ProgressBar
<style name="Widget.ProgressBar">
<item name="indeterminateOnly">true</item>
<item name="indeterminateDrawable">@drawable/progress_medium_white</item>
<item name="indeterminateBehavior">repeat</item>
<item name="indeterminateDuration">3500</item>
<item name="minWidth">48dip</item>
<item name="maxWidth">48dip</item>
<item name="minHeight">48dip</item>
<item name="maxHeight">48dip</item>
<item name="mirrorForRtl">false</item>
</style>
上面的“@drawable/progress_medium_white” 是一个帧动画
水平的ProgressBar
<style name="Widget.ProgressBar.Horizontal">
<item name="indeterminateOnly">false</item>
<item name="progressDrawable">@drawable/progress_horizontal</item>
<item name="indeterminateDrawable">@drawable/progress_indeterminate_horizontal</item>
<item name="minHeight">20dip</item>
<item name="maxHeight">20dip</item>
<item name="mirrorForRtl">true</item>
</style>
@drawable/progress_indeterminate_horizontal:是一个帧动画
@drawable/progress_horizontal:是一个LayerDrawable,主要由一个表示底色的Drawable,和表示Progress的ClipDrawable组成
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dip" />
<gradient
.....................
/>
</shape>
</item>
....................
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dip" />
<gradient
.....................
/>
</shape>
</clip>
</item>
</layer-list>
小结:
一直旋转的系统ProgressBar,其实只是一直播放的帧动画
水平的ProgressBar,是由于ClipDrawable根据Progress,从而改变ClipDrawable的Level
2.方案选择
- 参考系统水平ProgressBar的样式实现,自定义特殊的Drawable,传入ProgressBar(由于是系统实现,优先考虑)
- 继承ProgressBar,重写里面的Draw方法
3.方案一
- 自定义一个Drawable,重写里面的Draw方法,绘制一个圆角矩形作为底色
- 再自定义一个Drawable,重写里面的Draw方法,根据Level值,绘制一个当前的进度
- 通过LayerDrawable把这两个Drawable层叠起来,得到最后的LayerDrawable
- 调用ProgressBar的setProgressDrawable,设置该LayerDrawable
结果:失败。通过代码调用ProgressBar的setProgressDrawable发现调用无效,不解。
4.方案二
- 生成一个矩形:RectF rectF = new RectF(mPathWidth,mPathWidth,getWidth()-mPathWidth,getHeight()-mPathWidth);
注意:因为是只要边框,所以生成的矩形要减去边框的宽度 - 生成圆角矩形的Path
- 利用PathMeasure,获取Path的某一部分,并绘制到Canvas
public class RoundCornerProgressBar extends ProgressBar {
private Paint mBackgroundPaint = new Paint();
private Paint mProgressPaint = new Paint();
private float mRadius;
private int mPathWidth;
private float mStartOffsetPercent;
构造函数(略)
@Override
public void onDraw(Canvas canvas) {
drawBackground(canvas);
drawProgress(canvas);
}
private void drawBackground(Canvas canvas){
RectF rectF = new RectF(mPathWidth,mPathWidth,getWidth()-mPathWidth,getHeight()-mPathWidth);
canvas.drawRoundRect(rectF, mRadius, mRadius, mBackgroundPaint);
}
private void drawProgress(Canvas canvas){
RectF rectF = new RectF(mPathWidth,mPathWidth,getWidth()-mPathWidth,getHeight()-mPathWidth);
Path path = new Path();
path.addRoundRect(rectF, mRadius, mRadius, Path.Direction.CW);
Path mRenderPaths = new Path();
PathMeasure mPathMeasure = new PathMeasure(path, false);
float pathOffset = mPathMeasure.getLength() * (getProgress()/100f);
float startOffset = mPathMeasure.getLength() * mStartOffsetPercent;
if(pathOffset + startOffset < mPathMeasure.getLength()){
if (mPathMeasure.getSegment(startOffset, startOffset + pathOffset, mRenderPaths, true)) {
canvas.drawPath(mRenderPaths, mProgressPaint);
}
} else {
if (mPathMeasure.getSegment(startOffset, mPathMeasure.getLength(), mRenderPaths, true)) {
canvas.drawPath(mRenderPaths, mProgressPaint);
}
if (mPathMeasure.getSegment(0, pathOffset + startOffset - mPathMeasure.getLength(), mRenderPaths, true)) {
canvas.drawPath(mRenderPaths, mProgressPaint);
}
}
}
}
网友评论