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做一个带进度的图片预览控件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容

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