Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 98e7c10

Browse files
author
dinuscxj
committed
add chinese help document
1 parent 979291e commit 98e7c10

File tree

4 files changed

+303
-3
lines changed

4 files changed

+303
-3
lines changed

README-ZH.md

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
2+
## LoadingDrawable
3+
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-LoadingDrawable-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/3450)
4+
5+
一些酷炫的加载动画, 可以与任何View配合使用,作为加载动画或者Progressbar, 此外很适合与[RecyclerRefreshLayout](https://github.com/dinuscxj/RecyclerRefreshLayout)
6+
配合使用作为刷新的loading 动画
7+
8+
![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleJumpDrawable.gif?width=300)
9+
![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleRotateDrawable.gif?width=300)
10+
11+
## 功能
12+
#### 圆形滚动系列
13+
* GearLoadingDrawable
14+
* WhorlLoadingDrawable
15+
* LevelLoadingDrawable
16+
* MaterialLoadingDrawable
17+
18+
#### 圆形跳动系列
19+
* SwapLoadingDrawable
20+
* GuardLoadingRenderer
21+
* DanceLoadingRenderer
22+
* CollisionLoadingDrawable
23+
24+
## 代办事项
25+
当我感觉bug比较少的时候,我会添加一个gradle依赖。 所以在推上去之前希望大家多提提建议和bug.
26+
27+
## 用法
28+
#### Gradle
29+
```
30+
compile project(':library')
31+
```
32+
#### 在代码里
33+
用在ImageView中
34+
```java
35+
ImageView.setImageDrawable(new LoadingDrawable(new GearLoadingRenderer(Context)));
36+
ImageView.setImageDrawable(new LoadingDrawable(new WhorlLoadingRenderer(Context)));
37+
ImageView.setImageDrawable(new LoadingDrawable(new LevelLoadingRenderer(Context)));
38+
ImageView.setImageDrawable(new LoadingDrawable(new MaterialLoadingRenderer(Context)));
39+
```
40+
41+
用在View中
42+
```java
43+
View.setBackground(new LoadingDrawable(new GearLoadingRenderer(Context)));
44+
View.setBackground(new LoadingDrawable(new WhorlLoadingRenderer(Context)));
45+
View.setBackground(new LoadingDrawable(new LevelLoadingRenderer(Context)));
46+
View.setBackground(new LoadingDrawable(new MaterialLoadingRenderer(Context)));
47+
```
48+
## 详解
49+
#### 概述
50+
这个项目是基于Drawable编写的动画加载库,Drawable具有轻量级的、高效性、复用性强的特点。缺点就是使用是有门槛的
51+
如果你对于Drawable的特性不是特别了解, 和View配合使用会有诸多麻烦,建议使用前先调研一下Drawable最为背景(background)
52+
和作为ImageView的内容时的区别。本项目主要采用了策略模式(Strategy)通过给LoadingDrawable设置不同的LoadingRenderer(渲染器)
53+
来绘制不同的加载动画。
54+
55+
#### LoadingDrawable
56+
LoadingDrawable这个类继承Drawable并实现接口Animatable(我感觉写Drawable相关的动画都会实现的接口),构造函数必须传入
57+
LoadingRenderer的子类。并通过回调Callback与LoadingRenderer进行交互。
58+
59+
#### LoadingRenderer
60+
LoadingRenderer主要负责给LoadingDrawable绘制的。 核心方法 draw(Canvas, Rect) 和 computeRender(float),
61+
其中draw(Canvas, Rect)顾名思义,负责绘制, computeRender 负责计算当前的进度需要绘制的形状的大小,位置,其参数
62+
是有类内部的成员变量mRenderAnimator负责传递。
63+
64+
#### 圆形滚动系列
65+
圆形滚动系列(GearLoadingRenderer、WhorlLoadingRenderer、LevelLoadingRenderer、MaterialLoadingRenderer)代码
66+
相似度很高,无非都是不断改变绘制弧度的大小和绘制的位置。所以只详细讲解MaterialLoadingRenderer(下图的第二个动画)。
67+
![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleRotateDrawable.gif?width=300)
68+
首先draw方法进行详解, 详见下面代码注释:
69+
```java
70+
public void draw(Canvas canvas, Rect bounds) {
71+
//给画笔设置颜色
72+
mPaint.setColor(mCurrentColor);
73+
//保存画布
74+
int saveCount = canvas.save();
75+
//围绕bounds中心旋转画布mGroupRotation角度
76+
canvas.rotate(mGroupRotation, bounds.exactCenterX(), bounds.exactCenterY());
77+
RectF arcBounds = mTempBounds;
78+
arcBounds.set(bounds);
79+
//这个绘制圆环总要设置的,无论在View的onDraw 还是在Drawable 的draw方法里都是不能紧贴边界绘制圆环的
80+
//否则会发现所绘制圆环的边界有一半被裁剪掉
81+
arcBounds.inset(mStrokeInset, mStrokeInset);
82+
//这句主要是为了防止canvas调用drawArc方法绘制sweepAngle为0时闪烁的问题
83+
if (mStartTrim == mEndTrim) {
84+
mStartTrim = mEndTrim + getMinProgressArc();
85+
}
86+
//下面代码是这个动画的核心代码, Material效果的动画无非就是通过不断改变绘制弧度的开始角度和绘制弧度的大小
87+
float startAngle = (mStartTrim + mRotation) * DEGREE_360;
88+
float endAngle = (mEndTrim + mRotation) * DEGREE_360;
89+
float sweepAngle = endAngle - startAngle;
90+
canvas.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
91+
canvas.restoreToCount(saveCount);
92+
}
93+
```
94+
对于mStartTrim和mEndTrim是如何计算的呢?
95+
96+
```java
97+
public void computeRender(float renderProgress) {
98+
//绘制的最小弧度数
99+
final float minProgressArc = getMinProgressArc();
100+
//下面这三行主要是为了让此次动画起始点是上次动画的结束点,因为每次动画的结束点不是明确的
101+
final float originEndTrim = mOriginEndTrim;
102+
final float originStartTrim = mOriginStartTrim;
103+
final float originRotation = mOriginRotation;
104+
//更新所绘制弧度的颜色,从本次动画的最后20%进行颜色渐变切换
105+
updateRingColor(renderProgress);
106+
//动画的前50% 不断增大开始角度的大小(不改变结束角度的大小)从而不断增大绘制弧度的大小
107+
if (renderProgress <= START_TRIM_DURATION_OFFSET) {
108+
float startTrimProgress = (renderProgress) / START_TRIM_DURATION_OFFSET;
109+
mStartTrim = originStartTrim + ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress));
110+
}
111+
//动画的后50% 不断增大结束角度的大小(不改变开始角度的大小)从而不断减小绘制弧度的大小
112+
if (renderProgress > START_TRIM_DURATION_OFFSET) {
113+
float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
114+
mEndTrim = originEndTrim + ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress));
115+
}
116+
//下面这两行用于旋转画布是绘制的弧度看起来是在不断转动
117+
mGroupRotation = ((FULL_ROTATION / NUM_POINTS) * renderProgress) + (FULL_ROTATION * (mRotationCount / NUM_POINTS));
118+
mRotation = originRotation + (ROTATION_FACTOR * renderProgress);
119+
invalidateSelf();
120+
}
121+
```
122+
123+
#### 圆形跳动系列
124+
圆形跳动系列(CollisionLoadingRenderer,DanceLoadingRenderer, GuardLoadingRenderer, SwapLoadingRenderer)所设计的数学知识比较多,
125+
需要对圆、抛物线、直线的函数有一定的了解, 并且会计算交点。其中(CollisionLoadingRenderer,SwapLoadingRenderer)相对比较简单,
126+
(DanceLoadingRenderer, GuardLoadingRenderer)比较复杂,这两个相同点:都是圆与直线之间的动画处理,
127+
不同点:DanceLoadingRender设计的状态变换更多,而GuardLoadingRenderer设计的知识点难度更大。 所以这里对GuardLoadingRenderer()(下图第三那个)
128+
进行详解。希望大家也可以尝试对代码进行分析,只有这样你才会进步的更快。 分析代码的能力对于程序员的成长非常大。废话不多说了,
129+
![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleJumpDrawable.gif?width=300)
130+
首先还是对 draw方法进行解释:
131+
``` java
132+
@Override
133+
public void draw(Canvas canvas, Rect bounds) {
134+
//初始化arcBounds 设置inset 保证所绘制圆环不会被裁减
135+
RectF arcBounds = mTempBounds;
136+
arcBounds.set(bounds);
137+
arcBounds.inset(mStrokeInset, mStrokeInset);
138+
//mCurrentBounds保存当前可安全绘制区域
139+
mCurrentBounds.set(arcBounds);
140+
//保存画布的状态
141+
int saveCount = canvas.save();
142+
//不断改变绘制弧度的开始角度和绘制弧度的大小
143+
float startAngle = (mStartTrim + mRotation) * 360;
144+
float endAngle = (mEndTrim + mRotation) * 360;
145+
float sweepAngle = endAngle - startAngle;
146+
if (sweepAngle != 0) {
147+
mPaint.setColor(mColor);
148+
mPaint.setStyle(Paint.Style.STROKE);
149+
canvas.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
150+
}
151+
//绘制水波纹 初始半径大小就是圆环的半径, 最大是圆环半径的2倍,
152+
//通过mWaveProgress不断扩大半径和减少绘制水波纹的透明度
153+
if (mWaveProgress < 1.0f) {
154+
mPaint.setColor(Color.argb((int) (Color.alpha(mColor) * (1.0f - mWaveProgress)),
155+
Color.red(mColor), Color.green(mColor), Color.blue(mColor)));
156+
mPaint.setStyle(Paint.Style.STROKE);
157+
float radius = Math.min(arcBounds.width(), arcBounds.height()) / 2.0f;
158+
canvas.drawCircle(arcBounds.centerX(), arcBounds.centerY(), radius * (1.0f + mWaveProgress), mPaint);
159+
}
160+
//绘制跳动球的位置 只是简单的绘制Circle
161+
if (mPathMeasure != null) {
162+
mPaint.setColor(mBallColor);
163+
mPaint.setStyle(Paint.Style.FILL);
164+
canvas.drawCircle(mCurrentPosition[0], mCurrentPosition[1], mSkipBallSize * mScale, mPaint);
165+
}
166+
canvas.restoreToCount(saveCount);
167+
}
168+
```
169+
像这种涉及数学较多的,核心代码都在计算中
170+
```java
171+
public void computeRender(float renderProgress) {
172+
//动画的前START_TRIM_DURATION_OFFSET 不断减少结束角度的大小(不改变开始角度的大小)从而不断增大绘制弧度的大小
173+
//并不断增大mRotation(反向增大, START_TRIM_INIT_ROTATION、 START_TRIM_MAX_ROTATION 都是负数)是反向旋转
174+
if (renderProgress <= START_TRIM_DURATION_OFFSET) {
175+
final float startTrimProgress = (renderProgress) / START_TRIM_DURATION_OFFSET;
176+
mEndTrim = -MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
177+
mRotation = START_TRIM_INIT_ROTATION + START_TRIM_MAX_ROTATION
178+
* MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
179+
invalidateSelf();
180+
return ;
181+
}
182+
//动画在(START_TRIM_DURATION_OFFSET, WAVE_DURATION_OFFSET]之间不断扩大水波纹的半径
183+
if (renderProgress <= WAVE_DURATION_OFFSET && renderProgress > START_TRIM_DURATION_OFFSET) {
184+
final float waveProgress = (renderProgress - START_TRIM_DURATION_OFFSET)
185+
/ (WAVE_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
186+
mWaveProgress = ACCELERATE_INTERPOLATOR.getInterpolation(waveProgress);
187+
invalidateSelf();
188+
return;
189+
}
190+
//动画在(WAVE_DURATION_OFFSET, BALL_SKIP_DURATION_OFFSET]之间通过PathMeasure获取当前跳动的小球
191+
//应该所在的坐标,不熟悉PathMeasure需要google和baidu一下了。做复杂动画必须了解的知识点
192+
if (renderProgress <= BALL_SKIP_DURATION_OFFSET && renderProgress > WAVE_DURATION_OFFSET) {
193+
if (mPathMeasure == null) {
194+
mPathMeasure = new PathMeasure(createSkipBallPath(), false);
195+
}
196+
final float ballSkipProgress = (renderProgress - WAVE_DURATION_OFFSET)
197+
/ (BALL_SKIP_DURATION_OFFSET - WAVE_DURATION_OFFSET);
198+
mPathMeasure.getPosTan(ballSkipProgress * mPathMeasure.getLength(), mCurrentPosition, null);
199+
mWaveProgress = 1.0f;
200+
invalidateSelf();
201+
return;
202+
}
203+
//动画在(BALL_SKIP_DURATION_OFFSET, BALL_SCALE_DURATION_OFFSET]之间通过mScale缩放跳动小球的半径
204+
if (renderProgress <= BALL_SCALE_DURATION_OFFSET && renderProgress > BALL_SKIP_DURATION_OFFSET) {
205+
final float ballScaleProgress =
206+
(renderProgress - BALL_SKIP_DURATION_OFFSET)
207+
/ (BALL_SCALE_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);
208+
if (ballScaleProgress < 0.5f) {
209+
mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(ballScaleProgress * 2.0f);
210+
} else {
211+
mScale = 2.0f - ACCELERATE_INTERPOLATOR.getInterpolation((ballScaleProgress - 0.5f) * 2.0f) * 2.0f;
212+
}
213+
invalidateSelf();
214+
return;
215+
}
216+
//动画的在[BALL_SCALE_DURATION_OFFSET, 1.0f]不断增加结束角度的大小(不改变开始角度的大小)从而不断减小绘制弧度的大小
217+
//并不断加大mRotation(正向增大, END_TRIM_INIT_ROTATION、 END_TRIM_MAX_ROTATION 都是正数)从而正向旋转
218+
if (renderProgress >= BALL_SCALE_DURATION_OFFSET) {
219+
final float endTrimProgress =
220+
(renderProgress - BALL_SKIP_DURATION_OFFSET)
221+
/ (END_TRIM_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);
222+
mEndTrim = -1 + MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
223+
mRotation = END_TRIM_INIT_ROTATION + END_TRIM_MAX_ROTATION
224+
* MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
225+
//重置参数,防止不必要的绘制
226+
mScale = 1.0f;
227+
mPathMeasure = null;
228+
invalidateSelf();
229+
return;
230+
}
231+
}
232+
//小球跳动路径的核心路径计算函数
233+
//圆的公式 x^2 + y^2 = radius^2 --> y = sqrt(radius^2 - x^2) 或 y = -sqrt(radius^2 - x^2
234+
private Path createSkipBallPath() {
235+
//绘制圆环的半径
236+
float radius = Math.min(mCurrentBounds.width(), mCurrentBounds.height()) / 2.0f;
237+
//绘制圆环的半径的平方
238+
float radiusPow2 = (float) Math.pow(radius, 2.0f);
239+
//原点x坐标
240+
float originCoordinateX = mCurrentBounds.centerX();
241+
//原点y坐标
242+
float originCoordinateY = mCurrentBounds.centerY();
243+
//跳动的小球的x坐标取样点
244+
float[] coordinateX = new float[] {0.0f, 0.0f, -0.8f * radius, 0.75f * radius,
245+
-0.45f * radius, 0.9f * radius, -0.5f * radius};
246+
//跳动的小球的y坐标正负值取样点(y坐标可能呢正负)
247+
float[] sign = new float[] {1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f};
248+
Path path = new Path();
249+
//由x坐标计算y坐标的公式见函数开头
250+
for (int i = 0; i < coordinateX.length; i++) {
251+
//第一个点是moveTo
252+
if (i == 0) {
253+
path.moveTo(
254+
originCoordinateX + coordinateX[i],
255+
originCoordinateY + sign[i]
256+
* (float) Math.sqrt(radiusPow2 - Math.pow(coordinateX[i], 2.0f)));
257+
continue;
258+
}
259+
path.lineTo(
260+
originCoordinateX + coordinateX[i],
261+
originCoordinateY + sign[i]
262+
* (float) Math.sqrt(radiusPow2 - Math.pow(coordinateX[i], 2.0f)));
263+
//最后一个点, 指向圆环中心
264+
if (i == coordinateX.length - 1) {
265+
path.lineTo(originCoordinateX, originCoordinateY);
266+
}
267+
}
268+
return path;
269+
}
270+
```
271+
272+
## 杂谈
273+
如果你喜欢LoadingDrawable或者在使用它, 你可以
274+
275+
* star这个项目
276+
* 提一些建议, 谢谢。
277+
278+
## License
279+
Copyright 2015-2019 dinus
280+
281+
Licensed under the Apache License, Version 2.0 (the "License");
282+
you may not use this file except in compliance with the License.
283+
You may obtain a copy of the License at
284+
285+
http://www.apache.org/licenses/LICENSE-2.0
286+
287+
Unless required by applicable law or agreed to in writing, software
288+
distributed under the License is distributed on an "AS IS" BASIS,
289+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
290+
See the License for the specific language governing permissions and
291+
limitations under the License.

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11

22
## LoadingDrawable
3+
[中文版文档](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/README-ZH.md)
34
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-LoadingDrawable-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/3450)
45

56
some android loading drawable, can be combined with any View as the loading View and Progressbar,
67
and is especially suitable for the loading animation of the [RecyclerRefreshLayout](https://github.com/dinuscxj/RecyclerRefreshLayout).
78

89
![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleJumpDrawable.gif?width=300)
910
![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleRotateDrawable.gif?width=300)
11+
1012
## Features
1113
#### Circle Rotate
1214
* GearLoadingDrawable
@@ -21,13 +23,14 @@
2123
* CollisionLoadingDrawable
2224

2325
## TODO
24-
When I feel less bugs enough, I will add a gradle dependency. So I hope you will make more Suggestions.
26+
When I feel less bugs enough, I will add a gradle dependency. So I hope you will make more Suggestions or Issues.
2527

2628
## Usage
2729
#### Gradle
2830
```
2931
compile project(':library')
3032
```
33+
#### In java
3134

3235
Used with ImageView
3336
```java
@@ -45,6 +48,12 @@
4548
View.setBackground(new LoadingDrawable(new MaterialLoadingRenderer(Context)));
4649
```
4750

51+
## Misc
52+
If you like LoadingDrawable or use it, could you please:
53+
54+
* star this repo
55+
* send me some feedback. Thanks!
56+
4857
## License
4958
Copyright 2015-2019 dinus
5059

library/src/main/java/app/dinus/com/loadingdrawable/render/LoadingRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ private void setupDefaultParams(Context context) {
7474
}
7575

7676
private void setupAnimators() {
77-
mRenderAnimator = ValueAnimator.ofFloat(0, 1);
77+
mRenderAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
7878
mRenderAnimator.setRepeatCount(Animation.INFINITE);
7979
mRenderAnimator.setRepeatMode(Animation.RESTART);
8080
mRenderAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

library/src/main/java/app/dinus/com/loadingdrawable/render/circle/jump/GuardLoadingRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public void computeRender(float renderProgress) {
174174
return;
175175
}
176176

177-
if (renderProgress > BALL_SCALE_DURATION_OFFSET) {
177+
if (renderProgress >= BALL_SCALE_DURATION_OFFSET) {
178178
final float endTrimProgress =
179179
(renderProgress - BALL_SKIP_DURATION_OFFSET)
180180
/ (END_TRIM_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);

0 commit comments

Comments
 (0)