Android自定义View:模仿Lofter图片加载(一)

前言

最近公司给了个需求,要求图片加载的时候显示加载进度。恰好,平时都比较喜欢用Lofter浏览一些图片,所以就有了个想法,就做个模仿Lofter图片加载的控件吧。

先看一下效果图

LofterImageView整体效果

可能上面的图网速太快,看不了什么效果,下面的图应该可以看清楚一点


LofterImageView控件效果

分析

什么都别说,先看看Lofter加载图片的截图,仔细分析一下如果是我们来实现的话,应该怎么做。


Lofter加载图片分析.png
  • 首先显示加载进度框
  • 接着图片加载完后支持缩放图片
  • 支持多图滑动预览

只有这三步,仅此而已;那这篇文章首先来分析实现加载进度框,也就是要写好LofterProgressView这个控件

自定义 ProgressBar

老规矩,先仔细分析一下Lofter的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);

背景的效果图就出来了


LofterProgressView_bg.png
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);

最后,整个效果图就是这样了

LofterProgressView控件
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)完成

最终自定义ProgressBar

附项目地址


谢谢阅读

下一篇文章,我将会结合Glide和本篇文章的ImageProgressView做一个带进度的图片预览控件。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 138,443评论 20 591
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 34,782评论 21 602
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 3,843评论 0 11
  • 目标:在2018/6/30日前挣够30万因为我会参加很多善知识和心灵成长的课程;和丈夫的关系和谐生活幸福美满。 感...
    净心Farhana阅读 12评论 0 0
  • 听李健唱歌 像是溪水流淌一模样 恰巧那晚有月光 照在你胸膛 六块腹肌摆成棋盘 在月光下 滚烫的欲望 无边无际,无尽无边
    胡老六阅读 152评论 0 2