专栏名称: 郭霖
Android技术分享平台,每天都有优质技术文章推送。你还可以向公众号投稿,将自己总结的技术心得分享给大家。
目录
相关文章推荐
鸿洋  ·  WebView 经历的各种干货方案分享 ·  22 小时前  
stormzhang  ·  钟睒睒的喊话,能改变什么? ·  2 天前  
鸿洋  ·  为 TheRouter 的 AGP8 编译加个速 ·  2 天前  
郭霖  ·  Android Surface截图方法总结 ·  1 周前  
stormzhang  ·  打工人可以薅点羊毛​了 ·  6 天前  
51好读  ›  专栏  ›  郭霖

小清新加载等待控件

郭霖  · 公众号  · android  · 2017-06-07 08:01

正文

今日科技快讯

最近乐视负面新闻不断,有爆料称:乐视位于北京达美中心的办公地因未及时缴纳办公地费用已被停止物业一切服务。另外,A股上市公司明家联合发布公告称:子公司金源科技分别起诉乐视体育、乐视控股、乐视电子商务、乐视网拖欠广告款,且协商索要未果,合计欠款金额5892.94万元。

作者简介

本篇来自 Halohoop 的投稿,分享了自己的一个开源控件,效果挺不错的,希望大家喜欢。

Halohoop 的博客地址为:

http://halohoop.com/archives

正文

从锤子手机上看到的效果,锤子系统更新界面的入口按钮就是这个加载动画。效果图:

效果1

效果2

使用说明图:

  • half_rect_width:半个方块的宽度,单位dp

  • rect_divier_width:方块之间间隔宽度,单位dp

  • start_empty_position:初始空出的位置

  • is_clockwise:是否顺时针旋转

  • line_count:一行的数量,最少为3

  • fix_round_cornor:固定的方框的圆角半径

  • roll_round_cornor:旋转的方框的圆角半径,如果这两个圆角半径设置成不一样的值就会得到上面图1的效果,设置成一样就是图2.

  • roll_when_show_stop_when_hide:是否自动开始自定旋转,如果设置为 false,则需要手动调用 startRoll()方法(下文会提到)才会开始运动,设置为 true 则设置 View.Visibility 就会自动开始旋转。

  • square_color:方块的颜色。使用十六进制代码的形式(如:#333、#8e8e8e)

讲解实现方法之前,首先要说明一下方格的排列方式是从左到右,从上到下,也就是如果 line_count 设置为 3,那么方格的序号如下图:


实现思路:

自定义控件最主要的就是如何去准备要展示给用户看的东西,东西有了之后,我们在 onDraw方法 里面按部就班的画出来就可以了。接下来就带大家来走一走我准备的整个过程。其实整个过程就像做菜,准备材料(准备数据),加调味料(处理初始数据),翻炒(编写逻辑),这一切都是在锅中完成的,这个锅就是我们的 onDraw方法,我们把所有的一些都准备好,然后扔进锅(onDraw)里面。

最终的绘制分为两步:

  • 绘制固定的方块

  • 绘制滚动的方块

当运动的时候将固定的方框中的两个方块隐藏,然后让滚动的方块继承其中一个的位置,然后通过属性动画改变其位置的值以及旋转角度的值,最终调用 invalidate() 重绘让其动起来。

①(控件精髓就在此处)根据配置准备绘制的数据

处理自定义属性:

当选择空格位置不是外围的方块序号的时候,自动选择0位置,判断是否外围一圈的算法如下,纯数学知识:

绿色框出来的就是非外围的方块

初始化方块的方法:

两种方块都使用内部类定义,代码如下:

我们可以看到固定的方块 FixSquare 中有一个 next变量:

//指向下一个需要滚动的位置,顺时针和逆时针相反
FixSquare next;

因为我们需要将外围的一圈方块都链接起来,但是现在有一个问题就是外围的方块序号并不是按照 0、1、2… 排列的,因此我定义了一个 next变量 用于指定其下一个,这样一个接一个的就把外围连成一圈了。

算法如下,可能第一次看这个方法的小伙伴需要看一小会儿,因为需要适配行数 3 个以上的需求,因此都是动态变化的,因此都是一些数学公式,这里篇幅有限不一一讲解,大家可以顺着注释看看规律就很容易理解了,这个方法的主要目的就是为了让每个 FixSquare 的 “FixSquare next” 都赋上值,最终将外围都连成一圈,不要忘记考虑顺逆时针 isClockwise 这个变量哦:

固定方块的位置,分别使用 fixFixSquarePosition 和 fixRollSquarePosition 两个方法来固定 FixSquare 和 RollSquare:

对于方法 fixFixSquarePosition

  • 通过参数有控件的中点的x和y坐标,cx 和 cy,加上行数,方块的宽以及方块间隔;

  • 通过以上参数很容易就可以通过计算得出第0个方块的 left 和 top 值,分别是 firstRectLeft 和 firstRectTop;

  • 因为行数可能是奇数也可能是偶数,所以分为奇数和偶数两种计算方式;

  • 然后我把第一行的方块都固定下来之后,剩下的方块只需要往下平移即可固定下来了;

  • 第一个 for 循环表示行,第二个表示列,都是简单的数学计数知识,不过多阐述。

对于方法 fixRollSquarePosition

  • 因为我们已经从初始化的操作中知道哪一个位置是空的,startEmptyPosition;

  • 而且已经把外围的方块连成了环(通过next关联),上文的 linkTheOuterSquare 方法;

  • 因此可以很容易确定下来旋转的方块所要开始运动的初始位置。

②两种运动,平移 和 90度旋转

这里主要讲解一下思路,使用属性动画创建两个动画,一个是平移动画,一个是旋转动画,如下图,然后使用 AnimatorSet 将两个连接起来,同时运行。

平移动画

旋转动画

由于篇幅有限,加之方法比较长,这里不贴出,感兴趣的朋友可以去原码查看:

createTranslateValueAnimator方法 和 createRollValueAnimator方法;

其中值得关注的点是:需要考虑顺逆时针,以及实时更新旋转方块的旋转中心,因为平移过程中旋转中心也会跟着改变的,因此需要改变 RollSquare 的 cx 和 cy,具体的逻辑就在 setRollSquareRotateCenter方法 中,调用的时机当然就是在动画运动的过程中啦(见 onAnimationUpdate)。

③循环起来把

通过调用 startRoll方法,会创建一次动画,当动画结束的时候(onAnimationEnd),重新调用 startRoll方法,以达到循环的目的。这里相信大家都明白,就跟 handler 循环发送消息一样。

这里有一点需要注意的就是如果动画速度调的很快,那么会导致 ValueAnimator 动画对象频繁重复的创建,可能会有内存抖动的风险;因此建议使用者不要将速度调的太块,不过这个控件的后期的迭代我可能将这个动画对象换成始终只有一个 ValueAnimator 的情况。

④停止条件

在动画结束准备重新调用 startRoll方法 之前做一个变量判断,来控制是否需要循环调用,如下:

if (mAllowRoll) {
    startRoll();
}

当我们调用 stopRoll方法 的时候,mAllowRoll 会变为 false,调用 startRoll 的时候,mAllowRoll 会变为 true

⑤最后,画出来

上文也有提到,最终的绘制分为两步:

  • 绘制固定的方块

  • 绘制滚动的方块;

如果读者还有不明朗的地方,欢迎查看源码,并且给我提bug,一起为这个社区做出自己的微薄贡献。

控件源码:

https://github.com/halohoop/RollSquareView

更多

每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号: