自定义View实战--实现一个清新美观的加载按钮

本文详细介绍了如何实现一个自定义View——LoadButton,涉及自定义View的基本流程,包括属性的获取、尺寸测量和界面绘制。通过属性动画实现形态变换,深入探讨了MeasureSpec的EXACTLY和AT_MOST模式。LoadButton有多个状态,包括Initial、Folding、Loading、Successed和Error,每个状态都有相应的绘制逻辑。通过回调机制,LoadButton可以在加载成功或失败时通知调用者。
摘要由CSDN通过智能技术生成

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

在 Dribble 上偶然看到了一组交互如下:
这里写图片描述

当时在心里问自己能不能做,答案肯定是能做的,不过我比较懒,觉得中间那个伸缩变化要编写很多代码,所以懒得理。后来,为了不让自己那么浮躁,也为了锻炼自己的耐心程度,还是坚持实现它了。这个过程,觉得自己还是有所收获,把握了一些想当然的细节,输理了对于自定义 View 的流程。

我将这个自定义 View,起了一个名字叫做 LoadButton。

这篇文章涉及到的知识点有如下:

1. 自定义 View 时的基本流程,包含 attrs.xml 中属性的编写,构造方法中属性的获取,onMeasure() 中尺寸的测量。onDraw() 中界面的实现。
2. 可以让 Android 初学者再次感受一次回调机制的美妙。
3. 属性动画的基本使用。

第一步,先确定尺寸

先观察 LoadView 的形态。
这里写图片描述

上面的显示的是两种形状,一个是圆角矩形,另外一个就是圆。两个形态尺寸区别是,高相同,宽度不一致。

我们再进一步分析形态 1。
这里写图片描述

形态 1 可以看成是左右两个半圆和中间一个矩形。再回顾下示例图片中的动画表现。
这里写图片描述

圆角矩形最终变成了一个圆。我们可以用线框图来渐进表现它。
这里写图片描述

当进行动画时,中间的矩形部分不停地缩小,当它缩小为 0 时,形态 1 就转变成了形态 2。

上面的能够说明什么呢?说明 LoadButton 由 3 个部分组成,左右的半圆和中间的矩形,即使是形态 2 也可以看做是左右半圆和中间宽度为 0 的矩形组成。
这里写图片描述

细化尺寸

我们进一步讨论尺寸相关的情况。

我们知道对于普通开发者而言,自定义一个 View 测量尺寸的时候我们通常要关注的测量模式是 MeasureSpec.EXACTLY 和 MeasureSpec.AT_MOST 两种。要了解更多详细的信息可以阅读我写的这篇博文《长谈:关于 View Measure 测量机制,让我一次把话说完》。接下来,我们详细讨论一下这两种情况。

MeasureSpec.EXACTLY

当一个 View 的 layout_width 或者 layout_height 的取值为 match_parent 或 30dp 这样具体的数值时,这就表明它的测量模式是 MeasureSpec.EXACTLY。它已经获得了精确的数值了,按照常理我们是不应该再去干涉它,parent 给出的建议尺寸是什么,我们就把尺寸设置成什么,但是结合开发的实际情况来看,我们有一个底线,为了保证 LoadView 的完整性,也就是再差的情况下,parent 给出来的建议尺寸也不能小于形态 2。否则如下图情况就不是我们想要的了
这里写图片描述

MeasureSpec.AT_MOST

当一个 View 的 layout_width 或者 layout_height 的取值为 wrap_content 时,它的测量模式就是 MeasureSpec.AT_MOST,这个时候我们需要自己根据内容计算尺寸。而 LoadButton 的内容是什么呢?它的内容有 text 还有 加载成功或者加载失败的图片。因为图片大小在形态 2 中的圆形内可以确认。所以问题的关键就在于 LoadButton 文字内容宽高的尺寸测量。
这里写图片描述

text 内容自然是居中显示,然后它距离中间的 rect 上下左右间距也要考虑。这个时候的 rect 尺寸就是相对应的文字尺寸加上相对应方向上的 padding 值,这些 padding 值通过在 attrs.xml 中自定义属性然后在布局文件中赋予。

最后整体 LoadButton 尺寸自然是中间 rect 加上左右两个半圆的半径,但是这还不是最终的尺寸,最终的尺寸还是要和 parent 给的建议尺寸比较,不能大于它。

上面分析了尺寸测量相关,所以顺着思路进行的话,编码也只是水到渠成的事情了。

public class LoadButton extends View {
   

 @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    //用于保存最终尺寸
    int resultW = widthSize;
    int resultH = heightSize;

    // contentW contentH 用于确定中间矩形的尺寸
    int contentW = 0;
    int contentH = 0;

    if ( widthMode == MeasureSpec.AT_MOST ) {
        mTextWidth = (int) mTextPaint.measureText(mText);
        contentW += mTextWidth + mLeftRightPadding * 2 + mRadiu * 2;

        resultW = contentW < widthSize ? contentW : widthSize;
    }

    if ( heightMode == MeasureSpec.AT_MOST ) {
        contentH += mTopBottomPadding * 2 + mTextSize;
        resultH = contentH < heightSize ? contentH : heightSize;
    }

    resultW = resultW < 2 * mRadiu ? 2 * mRadiu : resultW;
    resultH = resultH < 2 * mRadiu ? 2 * mRadiu : resultH;

    // 修整圆形的半径
    mRadiu = resultH / 2;
    // 记录中间矩形的宽度值
    rectWidth = resultW - 2 * mRadiu;
    setMeasuredDimension(resultW,resultH);

    Log.d(TAG,"onMeasure: w:"+resultW+" h:"+resultH);
}

}

第二步,绘制

测量是在 onMeasure() 方法中进行,而绘制就是在 onDraw() 方法中进行的,这是 Android 开发者都知道的事情。所以这一节的重点在于 onDraw() 这个方法。
为了不给读者造成困扰,我先张贴自定的属性,及在构造方法中获取属性值的代码。其它的细节应该看名字就大概知道了。
attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LoadButton">
        <attr name="android:text" />
        <attr name="android:textSize" />
        <attr name="stroke_color" format="color|reference" />
        <attr name
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

frank909

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值