前言
最近公司给了个需求,要求图片加载的时候显示加载进度。恰好,平时都比较喜欢用Lofter浏览一些图片,所以就有了个想法,就做个模仿Lofter图片加载的控件吧。
先看一下效果图
可能上面的图网速太快,看不了什么效果,下面的图应该可以看清楚一点
分析
什么都别说,先看看Lofter加载图片的截图,仔细分析一下如果是我们来实现的话,应该怎么做。
- 首先显示加载进度框
- 接着图片加载完后支持缩放图片
- 支持多图滑动预览
只有这三步,仅此而已;那这篇文章首先来分析实现加载进度框,也就是要写好LofterProgressView
这个控件
自定义 ProgressBar
老规矩,先仔细分析一下Lofter的ProgressBar
,上图
这个ProgressBar
控件有三个部分构成
- 背景:一个圆角矩形
- 进度条:一条圆弧
- 百分比:纯文字,没什么特别的
So,我们一步步来实现
首先,为了代码的统一,先定义两个私有方法(获取自定义配置initAttrs()
、初始化画笔initProSettings()
),在构造函数中执行这两个方法。
public LofterProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//先获取自定义的配置
initAttrs(context, attrs);
//再进行初始化相关的配置
initProSettings();
}
Step 1 绘制圆角矩形背景
背景这里我定义了三个配置项,宽度、颜色以及圆角的半径
mBgWidth = typedArray.getDimensionPixelSize(R.styleable.ProgressView_bgWidth, 100);
mBgColor = typedArray.getColor(R.styleable.ProgressView_bgColor, Color.WHITE);
mBgCornerRadius = typedArray.getDimensionPixelSize(R.styleable.ProgressView_bgCornerRadius, 20);
初始化画笔也没什么特别的,仅仅设置了颜色和填充模式而已
mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBgPaint.setColor(mBgColor);
mBgPaint.setStyle(Paint.Style.FILL);
接下来就要画矩形了,但是前提是要清楚矩形具体要画在哪里,也就是说要知道坐标的位置,onDraw()
和onMeasure()
中都不建议创建对象,因此我们在LofterProgressView
这个控件中创建mBgRect
的成员变量,并在initProSettings()
方法中将这个创建RectF
对象并赋给mBgRect
mBgRect = new RectF();
而在onMeasure()
中,我们只要设置一下矩形的坐标即可
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//中心的位置
centerX = getMeasuredWidth() / 2;
centerY = getMeasuredHeight() / 2;
//计算并设置好bg圆角矩形的位置
mBgRect.set(centerX - (mBgWidth / 2), centerY - (mBgWidth / 2), centerX + (mBgWidth / 2), centerY + (mBgWidth / 2));
...
}
最后,在onDraw()
中使用drawRoundRect()
,传入设置好坐标的矩形、圆角的xy轴半径以及画笔,就可以绘制圆角矩形背景了
//画背景,圆角矩形
canvas.drawRoundRect(mBgRect, mBgCornerRadius, mBgCornerRadius, mBgPaint);
背景的效果图就出来了
Step 2 绘制进度条圆弧
看下图,绘制圆弧这里有两个步骤:
- 圆环背景(静态,也就是进度为0的状态显示)
-
进度圆环(动态)
圆弧这里我定义了四个配置项,半径、静态圆环颜色、进度条圆弧颜色以及进度条圆弧的宽度
mInnerRadius = typedArray.getDimensionPixelSize(R.styleable.ProgressView_innerRadius, 50);
mEdgeColor = typedArray.getColor(R.styleable.ProgressView_edgeColor, Color.RED);
mRingColor = typedArray.getColor(R.styleable.ProgressView_ringColor, Color.BLUE);
mRingWidth = typedArray.getDimensionPixelSize(R.styleable.ProgressView_ringWidth, 10);
初始化画笔
//静态圆环背景画笔
mEdgePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mEdgePaint.setColor(mEdgeColor);
mEdgePaint.setStrokeCap(Paint.Cap.ROUND);
mEdgePaint.setStrokeWidth(mRingWidth);
mEdgePaint.setStyle(Paint.Style.STROKE);
//动态进度条圆弧画笔
mPercentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPercentPaint.setColor(mPercentColor);
mPercentPaint.setStyle(Paint.Style.FILL);
mPercentPaint.setTextSize(mPercentSize);
下面就先绘制静态的圆环,比较简单
//画静态圆环,相当于进度为0时候的显示
canvas.drawCircle(centerX, centerY, mInnerRadius, mEdgePaint);
第二就要绘制进度圆弧了,绘制圆弧需要用到它对应的外切矩形,就是恰好包着圆弧所在圆的矩形,在的代码里面就是RectF
这个类。因此,我们需要在onMeasure()
中确定矩形的坐标。
//圆弧外切的矩形(在)
private RectF mOval;
/**
* 初始化配置
*/
private void initProSettings(){
...
mOval = new RectF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
centerX = getMeasuredWidth() / 2;
centerY = getMeasuredHeight() / 2;
...
//计算外切矩形的位置
mOval.left = (centerX - mInnerRadius);
mOval.top = (centerY - mInnerRadius);
mOval.right = centerX + mInnerRadius;
mOval.bottom = centerY + mInnerRadius;
}
外切的矩形只是确定了圆弧所在的圆,但是这个圆弧究竟要画多少(或者说这个这个圆弧究竟要占整个圆的多少,几分之几呢),这里需要计算出来的是圆弧扫过的角度。
/**
* mPercent是指下载进度
* 圆,一周是360度
* 这里圆弧扫过的角度必须要乘以360
*/
progress = (float) mPercent / 100 * 360;
最后就是绘制进度了
//画进度条圆环
//第二个参数是指圆弧的起点在哪,这里是以3点钟方向为0度,因此我们这里写-90
//第三个参数true的意思就是要将圆弧与圆心连起来,也就是话一个扇形,画一个饼,这里我们不需要,设置为false
canvas.drawArc(mOval, -90, progress, false, mRingPaint);
上效果图(为了更显眼,进度条颜色我换了另外的颜色)
Step 3 绘制百分比文字
文字这里定义了两个配置项,颜色和大小
mPercentColor = typedArray.getColor(R.styleable.ProgressView_percentColor, Color.GRAY);
mPercentSize = typedArray.getDimensionPixelSize(R.styleable.ProgressView_percentSize, 30);
初始化画笔
//字体的画笔
mPercentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPercentPaint.setColor(mPercentColor);
mPercentPaint.setStyle(Paint.Style.FILL);
mPercentPaint.setTextSize(mPercentSize);
//计算字体的高度
Paint.FontMetrics fm = mPercentPaint.getFontMetrics();
mTextHeight = (int) Math.ceil(fm.descent - fm.ascent);
绘制字体(先调整一下文字的位置,使文字处于中间的位置)
//计算字体的宽度
mTextWidth = mPercentPaint.measureText(percentText, 0, percentText.length());
//画百分比
canvas.drawText(percentText, centerX - mTextWidth / 2, centerY + mTextHeight / 4, mPercentPaint);
最后,整个效果图就是这样了
Step 4 暴露接口给外部
这里只需要注意重绘就可以了
/**
* 直接从外部设置百分比
*
* @param percent 百分比
*/
public void setPercent(int percent) {
this.mPercent = percent;
//重绘
postInvalidate();
}
最后 模拟一下进度显示
xml 配置
<chengang.library.widget.LofterProgressView
android:id="@+id/pv2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:visibility="visible"
app:bgColor="@color/white"
app:bgCornerRadius="12dp"
app:bgWidth="70dp"
app:edgeColor="@color/default_edge_color"
app:innerColor="@color/white"
app:innerRadius="17dp"
app:percentColor="@color/gray"
app:percentSize="11sp"
app:ringColor="@color/default_ring_color"
app:ringWidth="2dp" />
模拟下载图片
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 101; i++) {
try {
lofterProgressView.setPercent(i);
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
});
最终自定义的ProgressBar
(LofterProgressView
)完成
附项目地址
谢谢阅读
下一篇文章,我将会结合Glide和本篇文章的ImageProgressView做一个带进度的图片预览控件。