The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── Preview
    ├── AnimalDrawable.gif
    ├── CircleJumpDrawable.gif
    ├── CircleRotateDrawable.gif
    ├── GoodsDrawable.gif
    ├── SceneryDrawable.gif
    └── ShapeChangeDrawable.gif
├── README.md
├── app
    ├── .gitignore
    ├── build.gradle
    ├── proguard-rules.pro
    └── src
    │   ├── androidTest
    │       └── java
    │       │   └── app
    │       │       └── dinus
    │       │           └── com
    │       │               └── example
    │       │                   └── ApplicationTest.java
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── app
    │           │   └── dinus
    │           │       └── com
    │           │           └── example
    │           │               ├── AnimalActivity.java
    │           │               ├── CircleJumpActivity.java
    │           │               ├── CircleRotateActivity.java
    │           │               ├── GoodsActivity.java
    │           │               ├── MainActivity.java
    │           │               ├── SceneryActivity.java
    │           │               └── ShapeChangeActivity.java
    │       └── res
    │           ├── layout
    │               ├── activity_animal.xml
    │               ├── activity_circle_jump.xml
    │               ├── activity_circle_rotate.xml
    │               ├── activity_goods.xml
    │               ├── activity_main.xml
    │               ├── activity_scenery.xml
    │               └── activity_shape_change.xml
    │           ├── menu
    │               └── menu_main.xml
    │           ├── mipmap-hdpi
    │               └── ic_launcher.png
    │           ├── mipmap-mdpi
    │               └── ic_launcher.png
    │           ├── mipmap-xhdpi
    │               └── ic_launcher.png
    │           ├── mipmap-xxhdpi
    │               └── ic_launcher.png
    │           ├── values-w820dp
    │               └── dimens.xml
    │           └── values
    │               ├── dimens.xml
    │               ├── strings.xml
    │               └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
    └── wrapper
    │   ├── gradle-wrapper.jar
    │   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
    ├── .gitignore
    ├── build.gradle
    ├── proguard-rules.pro
    └── src
    │   ├── androidTest
    │       └── java
    │       │   └── app
    │       │       └── dinus
    │       │           └── com
    │       │               └── loadingdrawable
    │       │                   └── ApplicationTest.java
    │   └── main
    │       ├── AndroidManifest.xml
    │       ├── java
    │           └── app
    │           │   └── dinus
    │           │       └── com
    │           │           └── loadingdrawable
    │           │               ├── DensityUtil.java
    │           │               ├── LoadingView.java
    │           │               └── render
    │           │                   ├── LoadingDrawable.java
    │           │                   ├── LoadingRenderer.java
    │           │                   ├── LoadingRendererFactory.java
    │           │                   ├── animal
    │           │                       ├── FishLoadingRenderer.java
    │           │                       └── GhostsEyeLoadingRenderer.java
    │           │                   ├── circle
    │           │                       ├── jump
    │           │                       │   ├── CollisionLoadingRenderer.java
    │           │                       │   ├── DanceLoadingRenderer.java
    │           │                       │   ├── GuardLoadingRenderer.java
    │           │                       │   └── SwapLoadingRenderer.java
    │           │                       └── rotate
    │           │                       │   ├── GearLoadingRenderer.java
    │           │                       │   ├── LevelLoadingRenderer.java
    │           │                       │   ├── MaterialLoadingRenderer.java
    │           │                       │   └── WhorlLoadingRenderer.java
    │           │                   ├── goods
    │           │                       ├── BalloonLoadingRenderer.java
    │           │                       └── WaterBottleLoadingRenderer.java
    │           │                   ├── scenery
    │           │                       ├── DayNightLoadingRenderer.java
    │           │                       └── ElectricFanLoadingRenderer.java
    │           │                   └── shapechange
    │           │                       ├── CircleBroodLoadingRenderer.java
    │           │                       └── CoolWaitLoadingRenderer.java
    │       └── res
    │           ├── drawable-xhdpi
    │               ├── ic_eletric_fan.png
    │               ├── ic_leaf.png
    │               └── ic_loading.png
    │           └── values
    │               ├── attrs.xml
    │               └── strings.xml
└── settings.gradle


/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea
4 | .DS_Store
5 | /build
6 | /captures
7 | *.iml


--------------------------------------------------------------------------------
/Preview/AnimalDrawable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/Preview/AnimalDrawable.gif


--------------------------------------------------------------------------------
/Preview/CircleJumpDrawable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/Preview/CircleJumpDrawable.gif


--------------------------------------------------------------------------------
/Preview/CircleRotateDrawable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/Preview/CircleRotateDrawable.gif


--------------------------------------------------------------------------------
/Preview/GoodsDrawable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/Preview/GoodsDrawable.gif


--------------------------------------------------------------------------------
/Preview/SceneryDrawable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/Preview/SceneryDrawable.gif


--------------------------------------------------------------------------------
/Preview/ShapeChangeDrawable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/Preview/ShapeChangeDrawable.gif


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | 
  2 | ## LoadingDrawable: Android cool animation collection
  3 | [前言](http://www.jianshu.com/p/6e0ac5af4e8b)   
  4 | [CircleRotate源码解析](http://www.jianshu.com/p/1c3c6fc1b7ff)   
  5 | [Fish源码解析](http://blog.csdn.net/XSF50717/article/details/51494266)<br/>
  6 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-LoadingDrawable-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/3450)
  7 | 
  8 |  LoadingDrawable is some android animations implement of drawable: a library can be used in the pull-down to refresh, the placeholders of image loading and the time-consuming tasks. This project idea is from the [link](http://mp.weixin.qq.com/s?__biz=MjM5MDMxOTE5NA==&mid=402703079&idx=2&sn=2fcc6746a866dcc003c68ead9b68e595&scene=2&srcid=0302A7p723KK8E5gSzLKb2ZL&from=timeline&isappinstalled=0#wechat_redirect).<br/> 
  9 |  
 10 | The following content show a brief overview of LoadingDrawable
 11 | 
 12 | * It extends `Drawable` and implement the interface `Animatable`
 13 | * it uses strategy mode
 14 | * It can be used as the background of View or content of `ImageView`
 15 | * It's constructor must be passed a `LoadingRenderer`
 16 | * It interact with `LoadingRenderer` by the callback `Callback`
 17 | * `LoadingRenderer` is used for measuring and drawing the `LoadingDrawable`. note:
 18 | `LoadingRenderer` is the core
 19 | * `LoadingRenderer` only can be created by their `Builder`. 
 20 |  
 21 | Learn more about LoadingDrawable on the [Wiki Home](https://github.com/dinuscxj/LoadingDrawable/wiki).
 22 | 
 23 | ![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/ShapeChangeDrawable.gif)
 24 | ![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/GoodsDrawable.gif)
 25 | ![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/AnimalDrawable.gif)
 26 | ![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/SceneryDrawable.gif)
 27 | ![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleJumpDrawable.gif)
 28 | ![](https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/master/Preview/CircleRotateDrawable.gif)
 29 | 
 30 | ## LoadingRenderer Style
 31 | 
 32 | #### ShapeChange
 33 |  * CircleBroodLoadingRenderer
 34 |  * CoolWaitLoadingRenderer
 35 | 
 36 | #### Goods
 37 |  * BalloonLoadingRenderer
 38 |  * WaterBottleLoadingRenderer
 39 | 
 40 | #### Animal
 41 |  * FishLoadingRenderer
 42 |  * GhostsEyeLoadingEyeRenderer
 43 | 
 44 | #### Scenery
 45 |  * ElectricFanLoadingRenderer
 46 |  * DayNightLoadingRenderer
 47 | 
 48 | #### Circle Jump
 49 |  * CollisionLoadingRenderer
 50 |  * SwapLoadingRenderer
 51 |  * GuardLoadingRenderer
 52 |  * DanceLoadingRenderer
 53 | 
 54 | #### Circle Rotate
 55 |  * WhorlLoadingRenderer
 56 |  * MaterialLoadingRenderer
 57 |  * GearLoadingRenderer
 58 |  * LevelLoadingRenderer
 59 | 
 60 | ## Usage
 61 |  Define the `LoadingView` in XML and specify the `LoadingRenderer` style:
 62 |  ```xml
 63 |  <app.dinus.com.loadingdrawable.LoadingView
 64 |     android:id="@+id/level_view"
 65 |     android:layout_width="0dp"
 66 |     android:layout_height="match_parent"
 67 |     android:layout_weight="1"
 68 |     android:background="#fff1c02e"
 69 |     app:loading_renderer="LevelLoadingRenderer"/>
 70 |   ```
 71 |  Or specify the `LoadingRenderer` style in Java
 72 |  ```java
 73 |  ***LoadingRenderer.Builder builder = new ***LoadingRenderer.Builder(context);
 74 |  LoadingView.setLoadingRenderer(builder.build());
 75 |  ```
 76 |  
 77 | ## TODO
 78 |  When I feel less bugs enough, I will add a gradle dependency. So I hope you will make more Suggestions or Issues.
 79 | 
 80 | ## Misc
 81 |  If you like LoadingDrawable or use it, could you please:
 82 | 
 83 |  * star this repo
 84 |  * send me some feedback. Thanks!
 85 |  
 86 |  ***QQ Group:*** **342748245**
 87 | 
 88 | ## License
 89 |     Copyright 2015-2019 dinus
 90 | 
 91 |     Licensed under the Apache License, Version 2.0 (the "License");
 92 |     you may not use this file except in compliance with the License.
 93 |     You may obtain a copy of the License at
 94 | 
 95 |        http://www.apache.org/licenses/LICENSE-2.0
 96 | 
 97 |     Unless required by applicable law or agreed to in writing, software
 98 |     distributed under the License is distributed on an "AS IS" BASIS,
 99 |     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100 |     See the License for the specific language governing permissions and
101 |     limitations under the License.
102 | 


--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | 


--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
 1 | apply plugin: 'com.android.application'
 2 | 
 3 | android {
 4 |     compileSdkVersion 23
 5 |     buildToolsVersion "23.0.3"
 6 | 
 7 |     defaultConfig {
 8 |         applicationId "app.dinus.com.example"
 9 |         minSdkVersion 12
10 |         targetSdkVersion 23
11 |         versionCode 1
12 |         versionName "1.0"
13 |     }
14 |     buildTypes {
15 |         release {
16 |             minifyEnabled false
17 |             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 |         }
19 |     }
20 | }
21 | 
22 | dependencies {
23 |     compile fileTree(dir: 'libs', include: ['*.jar'])
24 |     compile 'com.android.support:appcompat-v7:23.2.1'
25 |     compile project(':library')
26 | }
27 | 


--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # By default, the flags in this file are appended to flags specified
 3 | # in /Users/dinus/workspace/android-sdk-macosx/tools/proguard/proguard-android.txt
 4 | # You can edit the include path and order by changing the proguardFiles
 5 | # directive in build.gradle.
 6 | #
 7 | # For more details, see
 8 | #   http://developer.android.com/guide/developing/tools/proguard.html
 9 | 
10 | # Add any project specific keep options here:
11 | 
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | #   public *;
17 | #}
18 | 


--------------------------------------------------------------------------------
/app/src/androidTest/java/app/dinus/com/example/ApplicationTest.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.app.Application;
 4 | import android.test.ApplicationTestCase;
 5 | 
 6 | /**
 7 |  * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 8 |  */
 9 | public class ApplicationTest extends ApplicationTestCase<Application> {
10 |     public ApplicationTest() {
11 |         super(Application.class);
12 |     }
13 | }


--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3 |     package="app.dinus.com.example" >
 4 | 
 5 |     <application
 6 |         android:allowBackup="true"
 7 |         android:icon="@mipmap/ic_launcher"
 8 |         android:label="@string/app_name"
 9 |         android:theme="@style/AppTheme" >
10 |         <activity
11 |             android:name=".MainActivity"
12 |             android:label="@string/app_name" >
13 |             <intent-filter>
14 |                 <action android:name="android.intent.action.MAIN" />
15 | 
16 |                 <category android:name="android.intent.category.LAUNCHER" />
17 |             </intent-filter>
18 |         </activity>
19 | 
20 |         <activity android:name=".CircleJumpActivity"
21 |             android:label="@string/label_circle_jump"/>
22 | 
23 |         <activity android:name=".CircleRotateActivity"
24 |             android:label="@string/label_circle_rotate"/>
25 | 
26 |         <activity android:name=".SceneryActivity"
27 |             android:label="@string/label_scenery"/>
28 | 
29 |         <activity android:name=".AnimalActivity"
30 |             android:hardwareAccelerated="false"
31 |             android:label="@string/label_animal"/>
32 | 
33 |         <activity android:name=".GoodsActivity"
34 |             android:hardwareAccelerated="false"
35 |             android:label="@string/label_goods"/>
36 | 
37 |         <activity android:name=".ShapeChangeActivity"
38 |             android:hardwareAccelerated="false"
39 |             android:label="@string/label_shape_change"/>
40 |     </application>
41 | 
42 | </manifest>
43 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/AnimalActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.Intent;
 5 | import android.os.Bundle;
 6 | import android.support.v7.app.AppCompatActivity;
 7 | 
 8 | public class AnimalActivity extends AppCompatActivity {
 9 |     public static void startActivity(Context context) {
10 |         Intent intent = new Intent(context, AnimalActivity.class);
11 |         context.startActivity(intent);
12 |     }
13 | 
14 |     @Override
15 |     protected void onCreate(Bundle savedInstanceState) {
16 |         super.onCreate(savedInstanceState);
17 |         setContentView(R.layout.activity_animal);
18 |     }
19 | }
20 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/CircleJumpActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.Intent;
 5 | import android.os.Bundle;
 6 | import android.support.v7.app.AppCompatActivity;
 7 | 
 8 | public class CircleJumpActivity extends AppCompatActivity {
 9 |     public static void startActivity(Context context) {
10 |         Intent intent = new Intent(context, CircleJumpActivity.class);
11 |         context.startActivity(intent);
12 |     }
13 | 
14 |     @Override
15 |     protected void onCreate(Bundle savedInstanceState) {
16 |         super.onCreate(savedInstanceState);
17 |         setContentView(R.layout.activity_circle_jump);
18 |     }
19 | }
20 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/CircleRotateActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.Intent;
 5 | import android.graphics.Color;
 6 | import android.os.Bundle;
 7 | import android.support.v7.app.AppCompatActivity;
 8 | 
 9 | import app.dinus.com.loadingdrawable.DensityUtil;
10 | import app.dinus.com.loadingdrawable.LoadingView;
11 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
12 | import app.dinus.com.loadingdrawable.render.circle.rotate.GearLoadingRenderer;
13 | import app.dinus.com.loadingdrawable.render.circle.rotate.LevelLoadingRenderer;
14 | import app.dinus.com.loadingdrawable.render.circle.rotate.MaterialLoadingRenderer;
15 | import app.dinus.com.loadingdrawable.render.circle.rotate.WhorlLoadingRenderer;
16 | 
17 | public class CircleRotateActivity extends AppCompatActivity {
18 |     public static void startActivity(Context context) {
19 |         Intent intent = new Intent(context, CircleRotateActivity.class);
20 |         context.startActivity(intent);
21 |     }
22 | 
23 |     @Override
24 |     protected void onCreate(Bundle savedInstanceState) {
25 |         super.onCreate(savedInstanceState);
26 |         setContentView(R.layout.activity_circle_rotate);
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/GoodsActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.Intent;
 5 | import android.os.Bundle;
 6 | import android.support.v7.app.AppCompatActivity;
 7 | 
 8 | public class GoodsActivity extends AppCompatActivity {
 9 | 
10 |     public static void startActivity(Context context) {
11 |         Intent intent = new Intent(context, GoodsActivity.class);
12 |         context.startActivity(intent);
13 |     }
14 | 
15 |     @Override
16 |     protected void onCreate(Bundle savedInstanceState) {
17 |         super.onCreate(savedInstanceState);
18 |         setContentView(R.layout.activity_goods);
19 | 
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/MainActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.os.Bundle;
 4 | import android.support.v7.app.AppCompatActivity;
 5 | import android.view.View;
 6 | import android.widget.Button;
 7 | 
 8 | 
 9 | public class MainActivity extends AppCompatActivity implements View.OnClickListener{
10 |     private Button mBtnGoods;
11 |     private Button mBtnAnimal;
12 |     private Button mBtnScenery;
13 |     private Button mBtnCircleJump;
14 |     private Button mBtnShapeChange;
15 |     private Button mBtnCircleRotate;
16 | 
17 |     @Override
18 |     protected void onCreate(Bundle savedInstanceState) {
19 |         super.onCreate(savedInstanceState);
20 |         setContentView(R.layout.activity_main);
21 | 
22 |         mBtnGoods = (Button) findViewById(R.id.goods);
23 |         mBtnAnimal = (Button) findViewById(R.id.animal);
24 |         mBtnScenery = (Button) findViewById(R.id.scenery);
25 |         mBtnCircleJump = (Button) findViewById(R.id.circle_jump);
26 |         mBtnShapeChange = (Button) findViewById(R.id.shape_change);
27 |         mBtnCircleRotate = (Button) findViewById(R.id.circle_rotate);
28 | 
29 |         mBtnGoods.setOnClickListener(this);
30 |         mBtnAnimal.setOnClickListener(this);
31 |         mBtnScenery.setOnClickListener(this);
32 |         mBtnCircleJump.setOnClickListener(this);
33 |         mBtnShapeChange.setOnClickListener(this);
34 |         mBtnCircleRotate.setOnClickListener(this);
35 |     }
36 | 
37 |     @Override
38 |     public void onClick(View v) {
39 |         switch (v.getId()){
40 |             case R.id.shape_change:
41 |                 ShapeChangeActivity.startActivity(this);
42 |                 break;
43 |             case R.id.goods:
44 |                 GoodsActivity.startActivity(this);
45 |                 break;
46 |             case R.id.animal:
47 |                 AnimalActivity.startActivity(this);
48 |                 break;
49 |             case R.id.scenery:
50 |                 SceneryActivity.startActivity(this);
51 |                 break;
52 |             case R.id.circle_jump:
53 |                 CircleJumpActivity.startActivity(this);
54 |                 break;
55 |             case R.id.circle_rotate:
56 |                 CircleRotateActivity.startActivity(this);
57 |                 break;
58 |             default:
59 |                 break;
60 |         }
61 |     }
62 | }
63 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/SceneryActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.Intent;
 5 | import android.os.Bundle;
 6 | import android.support.v7.app.AppCompatActivity;
 7 | 
 8 | public class SceneryActivity extends AppCompatActivity {
 9 | 
10 |     public static void startActivity(Context context) {
11 |         Intent intent = new Intent(context, SceneryActivity.class);
12 |         context.startActivity(intent);
13 |     }
14 | 
15 |     @Override
16 |     protected void onCreate(Bundle savedInstanceState) {
17 |         super.onCreate(savedInstanceState);
18 |         setContentView(R.layout.activity_scenery);
19 |     }
20 | }
21 | 


--------------------------------------------------------------------------------
/app/src/main/java/app/dinus/com/example/ShapeChangeActivity.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.example;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.Intent;
 5 | import android.os.Bundle;
 6 | import android.support.v7.app.AppCompatActivity;
 7 | 
 8 | public class ShapeChangeActivity extends AppCompatActivity {
 9 | 
10 |     public static void startActivity(Context context) {
11 |         Intent intent = new Intent(context, ShapeChangeActivity.class);
12 |         context.startActivity(intent);
13 |     }
14 | 
15 |     @Override
16 |     protected void onCreate(Bundle savedInstanceState) {
17 |         super.onCreate(savedInstanceState);
18 |         setContentView(R.layout.activity_shape_change);
19 |     }
20 | }
21 | 


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_animal.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 3 |     android:layout_width="match_parent"
 4 |     android:layout_height="match_parent"
 5 |     android:orientation="vertical">
 6 | 
 7 |     <app.dinus.com.loadingdrawable.LoadingView
 8 |         android:id="@+id/fish_view"
 9 |         android:layout_width="match_parent"
10 |         android:layout_height="0dp"
11 |         android:layout_weight="1"
12 |         android:background="#ff071c28"
13 |         app:loading_renderer="FishLoadingRenderer" />
14 | 
15 |     <app.dinus.com.loadingdrawable.LoadingView
16 |         android:id="@+id/ghosts_eye_view"
17 |         android:layout_width="match_parent"
18 |         android:layout_height="0dp"
19 |         android:layout_weight="1"
20 |         android:background="#ffcdcdcd"
21 |         app:loading_renderer="GhostsEyeLoadingRenderer" />
22 | 
23 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_circle_jump.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 3 |     android:layout_width="match_parent"
 4 |     android:layout_height="match_parent"
 5 |     android:orientation="vertical">
 6 | 
 7 |     <LinearLayout
 8 |         android:layout_width="match_parent"
 9 |         android:layout_height="0dp"
10 |         android:layout_weight="1"
11 |         android:orientation="horizontal">
12 | 
13 |         <app.dinus.com.loadingdrawable.LoadingView
14 |             android:id="@+id/collision_view"
15 |             android:layout_width="0dp"
16 |             android:layout_height="match_parent"
17 |             android:layout_weight="1"
18 |             android:background="#ffe9e4d1"
19 |             app:loading_renderer="CollisionLoadingRenderer" />
20 | 
21 |         <app.dinus.com.loadingdrawable.LoadingView
22 |             android:id="@+id/swap_view"
23 |             android:layout_width="0dp"
24 |             android:layout_height="match_parent"
25 |             android:layout_weight="1"
26 |             android:background="#ff9c0000"
27 |             app:loading_renderer="SwapLoadingRenderer" />
28 |     </LinearLayout>
29 | 
30 |     <LinearLayout
31 |         android:layout_width="match_parent"
32 |         android:layout_height="0dp"
33 |         android:layout_weight="1"
34 |         android:orientation="horizontal">
35 | 
36 |         <app.dinus.com.loadingdrawable.LoadingView
37 |             android:id="@+id/guard_view"
38 |             android:layout_width="0dp"
39 |             android:layout_height="match_parent"
40 |             android:layout_weight="1"
41 |             android:background="#ff000000"
42 |             app:loading_renderer="GuardLoadingRenderer" />
43 | 
44 |         <app.dinus.com.loadingdrawable.LoadingView
45 |             android:id="@+id/dance_view"
46 |             android:layout_width="0dp"
47 |             android:layout_height="match_parent"
48 |             android:layout_weight="1"
49 |             android:background="#ff94d0c1"
50 |             app:loading_renderer="DanceLoadingRenderer" />
51 |     </LinearLayout>
52 | 
53 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_circle_rotate.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:layout_width="match_parent"
 3 |     android:layout_height="match_parent"
 4 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 5 |     android:orientation="vertical">
 6 | 
 7 |     <LinearLayout
 8 |         android:layout_width="match_parent"
 9 |         android:layout_height="0dp"
10 |         android:layout_weight="1"
11 |         android:orientation="horizontal">
12 | 
13 |         <app.dinus.com.loadingdrawable.LoadingView
14 |             android:id="@+id/whorl_view"
15 |             android:layout_width="0dp"
16 |             android:layout_height="match_parent"
17 |             android:layout_weight="1"
18 |             android:background="#ffe6ebed"
19 |             app:loading_renderer="WhorlLoadingRenderer"/>
20 | 
21 |         <app.dinus.com.loadingdrawable.LoadingView
22 |             android:id="@+id/material_view"
23 |             android:layout_width="0dp"
24 |             android:layout_height="match_parent"
25 |             android:layout_weight="1"
26 |             android:background="#ff2a8cc8"
27 |             app:loading_renderer="MaterialLoadingRenderer"/>
28 |     </LinearLayout>
29 | 
30 |     <LinearLayout
31 |         android:layout_width="match_parent"
32 |         android:layout_height="0dp"
33 |         android:layout_weight="1"
34 |         android:orientation="horizontal">
35 | 
36 |         <app.dinus.com.loadingdrawable.LoadingView
37 |             android:id="@+id/gear_view"
38 |             android:layout_width="0dp"
39 |             android:layout_height="match_parent"
40 |             android:layout_weight="1"
41 |             android:background="#fffa644e"
42 |             app:loading_renderer="GearLoadingRenderer"/>
43 | 
44 |         <app.dinus.com.loadingdrawable.LoadingView
45 |             android:id="@+id/level_view"
46 |             android:layout_width="0dp"
47 |             android:layout_height="match_parent"
48 |             android:layout_weight="1"
49 |             android:background="#fff1c02e"
50 |             app:loading_renderer="LevelLoadingRenderer"/>
51 |     </LinearLayout>
52 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_goods.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:layout_width="match_parent"
 3 |     android:layout_height="match_parent"
 4 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 5 |     android:orientation="vertical">
 6 | 
 7 | 
 8 |     <app.dinus.com.loadingdrawable.LoadingView
 9 |         android:id="@+id/balloon_view"
10 |         android:layout_width="match_parent"
11 |         android:layout_height="0dp"
12 |         android:layout_weight="1"
13 |         android:background="#ffd4d9da"
14 |         app:loading_renderer="BalloonLoadingRenderer"/>
15 | 
16 |     <app.dinus.com.loadingdrawable.LoadingView
17 |         android:id="@+id/water_bottle_view"
18 |         android:layout_width="match_parent"
19 |         android:layout_height="0dp"
20 |         android:layout_weight="1"
21 |         android:background="#ff15181d"
22 |         app:loading_renderer="WaterBottleLoadingRenderer"/>
23 | 
24 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     android:layout_width="match_parent"
 3 |     android:layout_height="match_parent"
 4 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 5 |     android:layout_marginTop="50dp"
 6 |     android:gravity="center"
 7 |     android:orientation="vertical">
 8 | 
 9 |     <Button
10 |         android:id="@+id/shape_change"
11 |         android:layout_width="wrap_content"
12 |         android:layout_height="wrap_content"
13 |         android:text="ShapeChangeActivity"
14 |         android:textAllCaps="false" />
15 | 
16 |     <Button
17 |         android:id="@+id/goods"
18 |         android:layout_width="wrap_content"
19 |         android:layout_height="wrap_content"
20 |         android:layout_marginTop="30dp"
21 |         android:text="GoodsActivity"
22 |         android:textAllCaps="false" />
23 | 
24 |     <Button
25 |         android:id="@+id/animal"
26 |         android:layout_width="wrap_content"
27 |         android:layout_height="wrap_content"
28 |         android:layout_marginTop="30dp"
29 |         android:text="AnimalActivity"
30 |         android:textAllCaps="false" />
31 | 
32 |     <Button
33 |         android:id="@+id/scenery"
34 |         android:layout_width="wrap_content"
35 |         android:layout_height="wrap_content"
36 |         android:layout_marginTop="30dp"
37 |         android:text="SceneryActivity"
38 |         android:textAllCaps="false" />
39 | 
40 |     <Button
41 |         android:id="@+id/circle_rotate"
42 |         android:layout_width="wrap_content"
43 |         android:layout_height="wrap_content"
44 |         android:layout_marginTop="30dp"
45 |         android:text="CircleRotateActivity"
46 |         android:textAllCaps="false" />
47 | 
48 |     <Button
49 |         android:id="@+id/circle_jump"
50 |         android:layout_width="wrap_content"
51 |         android:layout_height="wrap_content"
52 |         android:layout_marginTop="30dp"
53 |         android:text="CircleJumpActivity"
54 |         android:textAllCaps="false" />
55 | 
56 | </LinearLayout>
57 | 


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_scenery.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 |     xmlns:app="http://schemas.android.com/apk/res-auto"
 3 |     android:layout_width="match_parent"
 4 |     android:layout_height="match_parent"
 5 |     android:orientation="vertical">
 6 | 
 7 |     <app.dinus.com.loadingdrawable.LoadingView
 8 |         android:id="@+id/electric_fan_view"
 9 |         android:layout_width="match_parent"
10 |         android:layout_height="0dp"
11 |         android:layout_weight="1"
12 |         android:background="#fffcd03c"
13 |         app:loading_renderer="ElectricFanLoadingRenderer" />
14 | 
15 |     <app.dinus.com.loadingdrawable.LoadingView
16 |         android:id="@+id/day_night_view"
17 |         android:layout_width="match_parent"
18 |         android:layout_height="0dp"
19 |         android:layout_weight="1"
20 |         android:background="#ff071c28"
21 |         app:loading_renderer="DayNightLoadingRenderer" />
22 | 
23 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_shape_change.xml:
--------------------------------------------------------------------------------
 1 | <LinearLayout android:orientation="vertical"
 2 |     android:layout_height="match_parent"
 3 |     android:layout_width="match_parent"
 4 |     xmlns:android="http://schemas.android.com/apk/res/android"
 5 |     xmlns:app="http://schemas.android.com/apk/res-auto">
 6 | 
 7 |     <app.dinus.com.loadingdrawable.LoadingView
 8 |         android:id="@+id/circle_brood_view"
 9 |         android:layout_weight="1"
10 |         android:layout_width="match_parent"
11 |         android:background="#ffd4d9da"
12 |         android:layout_height="0dp"
13 |         app:loading_renderer="CircleBroodLoadingRenderer"/>
14 | 
15 |     <app.dinus.com.loadingdrawable.LoadingView
16 |         android:id="@+id/cool_wait_view"
17 |         android:layout_weight="1"
18 |         android:layout_height="0dp"
19 |         android:background="#ff1B78DD"
20 |         android:layout_width="match_parent"
21 |         app:loading_renderer="CoolWaitLoadingRenderer"/>
22 | 
23 | </LinearLayout>


--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 | <menu xmlns:android="http://schemas.android.com/apk/res/android"
2 |     xmlns:app="http://schemas.android.com/apk/res-auto"
3 |     xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
4 |     <item android:id="@+id/action_settings" android:title="@string/action_settings"
5 |         android:orderInCategory="100" app:showAsAction="never" />
6 | </menu>
7 | 


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/app/src/main/res/mipmap-hdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/app/src/main/res/mipmap-mdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/app/src/main/res/mipmap-xhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <!-- Example customization of dimensions originally defined in res/values/dimens.xml
3 |          (such as screen margins) for screens with more than 820dp of available width. This
4 |          would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
5 |     <dimen name="activity_horizontal_margin">64dp</dimen>
6 | </resources>
7 | 


--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <!-- Default screen margins, per the Android Design guidelines. -->
3 |     <dimen name="activity_horizontal_margin">16dp</dimen>
4 |     <dimen name="activity_vertical_margin">16dp</dimen>
5 | </resources>
6 | 


--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
 1 | <resources>
 2 |     <string name="app_name">LoadingDrawable</string>
 3 |     <string name="label_circle_rotate">CircleRotateActivity</string>
 4 |     <string name="label_circle_jump">CircleJumpActivity</string>
 5 |     <string name="label_scenery">SceneryActivity</string>
 6 |     <string name="label_animal">AnimalActivity</string>
 7 |     <string name="label_goods">GoodsActivity</string>
 8 |     <string name="label_shape_change">ShapeChangeActivity</string>
 9 | 
10 |     <string name="hello_world">Hello world!</string>
11 |     <string name="action_settings">Settings</string>
12 | </resources>
13 | 


--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 | 
3 |     <!-- Base application theme. -->
4 |     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
5 |         <!-- Customize your theme here. -->
6 |     </style>
7 | 
8 | </resources>
9 | 


--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
 2 | 
 3 | buildscript {
 4 |     repositories {
 5 |         jcenter()
 6 |     }
 7 |     dependencies {
 8 |         classpath 'com.android.tools.build:gradle:2.1.2'
 9 | 
10 |         // NOTE: Do not place your application dependencies here; they belong
11 |         // in the individual module build.gradle files
12 |     }
13 | }
14 | 
15 | allprojects {
16 |     repositories {
17 |         jcenter()
18 |     }
19 | }
20 | 


--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
 1 | # Project-wide Gradle settings.
 2 | 
 3 | # IDE (e.g. Android Studio) users:
 4 | # Gradle settings configured through the IDE *will override*
 5 | # any settings specified in this file.
 6 | 
 7 | # For more details on how to configure your build environment visit
 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
 9 | 
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 | 
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true


--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/gradle/wrapper/gradle-wrapper.jar


--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 | 


--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env bash
  2 | 
  3 | ##############################################################################
  4 | ##
  5 | ##  Gradle start up script for UN*X
  6 | ##
  7 | ##############################################################################
  8 | 
  9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 10 | DEFAULT_JVM_OPTS=""
 11 | 
 12 | APP_NAME="Gradle"
 13 | APP_BASE_NAME=`basename "$0"`
 14 | 
 15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
 16 | MAX_FD="maximum"
 17 | 
 18 | warn ( ) {
 19 |     echo "$*"
 20 | }
 21 | 
 22 | die ( ) {
 23 |     echo
 24 |     echo "$*"
 25 |     echo
 26 |     exit 1
 27 | }
 28 | 
 29 | # OS specific support (must be 'true' or 'false').
 30 | cygwin=false
 31 | msys=false
 32 | darwin=false
 33 | case "`uname`" in
 34 |   CYGWIN* )
 35 |     cygwin=true
 36 |     ;;
 37 |   Darwin* )
 38 |     darwin=true
 39 |     ;;
 40 |   MINGW* )
 41 |     msys=true
 42 |     ;;
 43 | esac
 44 | 
 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
 46 | if $cygwin ; then
 47 |     [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
 48 | fi
 49 | 
 50 | # Attempt to set APP_HOME
 51 | # Resolve links: $0 may be a link
 52 | PRG="$0"
 53 | # Need this for relative symlinks.
 54 | while [ -h "$PRG" ] ; do
 55 |     ls=`ls -ld "$PRG"`
 56 |     link=`expr "$ls" : '.*-> \(.*\)
#39;`
 57 |     if expr "$link" : '/.*' > /dev/null; then
 58 |         PRG="$link"
 59 |     else
 60 |         PRG=`dirname "$PRG"`"/$link"
 61 |     fi
 62 | done
 63 | SAVED="`pwd`"
 64 | cd "`dirname \"$PRG\"`/" >&-
 65 | APP_HOME="`pwd -P`"
 66 | cd "$SAVED" >&-
 67 | 
 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 69 | 
 70 | # Determine the Java command to use to start the JVM.
 71 | if [ -n "$JAVA_HOME" ] ; then
 72 |     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
 73 |         # IBM's JDK on AIX uses strange locations for the executables
 74 |         JAVACMD="$JAVA_HOME/jre/sh/java"
 75 |     else
 76 |         JAVACMD="$JAVA_HOME/bin/java"
 77 |     fi
 78 |     if [ ! -x "$JAVACMD" ] ; then
 79 |         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
 80 | 
 81 | Please set the JAVA_HOME variable in your environment to match the
 82 | location of your Java installation."
 83 |     fi
 84 | else
 85 |     JAVACMD="java"
 86 |     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 87 | 
 88 | Please set the JAVA_HOME variable in your environment to match the
 89 | location of your Java installation."
 90 | fi
 91 | 
 92 | # Increase the maximum file descriptors if we can.
 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
 94 |     MAX_FD_LIMIT=`ulimit -H -n`
 95 |     if [ $? -eq 0 ] ; then
 96 |         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
 97 |             MAX_FD="$MAX_FD_LIMIT"
 98 |         fi
 99 |         ulimit -n $MAX_FD
100 |         if [ $? -ne 0 ] ; then
101 |             warn "Could not set maximum file descriptor limit: $MAX_FD"
102 |         fi
103 |     else
104 |         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 |     fi
106 | fi
107 | 
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 |     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 | 
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 |     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 |     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 | 
118 |     # We build the pattern for arguments to be converted via cygpath
119 |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 |     SEP=""
121 |     for dir in $ROOTDIRSRAW ; do
122 |         ROOTDIRS="$ROOTDIRS$SEP$dir"
123 |         SEP="|"
124 |     done
125 |     OURCYGPATTERN="(^($ROOTDIRS))"
126 |     # Add a user-defined pattern to the cygpath arguments
127 |     if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 |         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 |     fi
130 |     # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 |     i=0
132 |     for arg in "$@" ; do
133 |         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 |         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
135 | 
136 |         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
137 |             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 |         else
139 |             eval `echo args$i`="\"$arg\""
140 |         fi
141 |         i=$((i+1))
142 |     done
143 |     case $i in
144 |         (0) set -- ;;
145 |         (1) set -- "$args0" ;;
146 |         (2) set -- "$args0" "$args1" ;;
147 |         (3) set -- "$args0" "$args1" "$args2" ;;
148 |         (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 |         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 |         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 |         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 |         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 |         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 |     esac
155 | fi
156 | 
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 |     JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 | 
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 | 


--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
 1 | @if "%DEBUG%" == "" @echo off
 2 | @rem ##########################################################################
 3 | @rem
 4 | @rem  Gradle startup script for Windows
 5 | @rem
 6 | @rem ##########################################################################
 7 | 
 8 | @rem Set local scope for the variables with windows NT shell
 9 | if "%OS%"=="Windows_NT" setlocal
10 | 
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 | 
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 | 
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 | 
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 | 
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 | 
32 | goto fail
33 | 
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 | 
38 | if exist "%JAVA_EXE%" goto init
39 | 
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 | 
46 | goto fail
47 | 
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 | 
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 | 
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 | 
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 | 
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 | 
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 | 
69 | :execute
70 | @rem Setup the command line
71 | 
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 | 
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 | 
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 | 
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 | 
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 | 
90 | :omega
91 | 


--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | 


--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
 1 | apply plugin: 'com.android.library'
 2 | 
 3 | android {
 4 |     compileSdkVersion 23
 5 |     buildToolsVersion "23.0.3"
 6 | 
 7 |     defaultConfig {
 8 |         minSdkVersion 12
 9 |         targetSdkVersion 23
10 |         versionCode 1
11 |         versionName "1.0"
12 |     }
13 |     buildTypes {
14 |         release {
15 |             minifyEnabled false
16 |             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 |         }
18 |     }
19 | }
20 | 
21 | dependencies {
22 |     compile fileTree(dir: 'libs', include: ['*.jar'])
23 |     compile 'com.android.support:appcompat-v7:23.2.1'
24 | }
25 | 


--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
 1 | # Add project specific ProGuard rules here.
 2 | # By default, the flags in this file are appended to flags specified
 3 | # in /Users/dinus/workspace/android-sdk-macosx/tools/proguard/proguard-android.txt
 4 | # You can edit the include path and order by changing the proguardFiles
 5 | # directive in build.gradle.
 6 | #
 7 | # For more details, see
 8 | #   http://developer.android.com/guide/developing/tools/proguard.html
 9 | 
10 | # Add any project specific keep options here:
11 | 
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | #   public *;
17 | #}
18 | 


--------------------------------------------------------------------------------
/library/src/androidTest/java/app/dinus/com/loadingdrawable/ApplicationTest.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.loadingdrawable;
 2 | 
 3 | import android.app.Application;
 4 | import android.test.ApplicationTestCase;
 5 | 
 6 | /**
 7 |  * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 8 |  */
 9 | public class ApplicationTest extends ApplicationTestCase<Application> {
10 |     public ApplicationTest() {
11 |         super(Application.class);
12 |     }
13 | }


--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 |     package="app.dinus.com.loadingdrawable">
3 | 
4 |     <application android:allowBackup="true" android:label="@string/app_name">
5 | 
6 |     </application>
7 | 
8 | </manifest>
9 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/DensityUtil.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.loadingdrawable;
 2 | 
 3 | import android.content.Context;
 4 | 
 5 | public class DensityUtil {
 6 | 
 7 |     public static float dip2px(Context context, float dpValue) {
 8 |         float scale = context.getResources().getDisplayMetrics().density;
 9 |         return dpValue * scale;
10 |     }  
11 | }


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/LoadingView.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.loadingdrawable;
 2 | 
 3 | import android.content.Context;
 4 | import android.content.res.TypedArray;
 5 | import android.util.AttributeSet;
 6 | import android.view.View;
 7 | import android.widget.ImageView;
 8 | 
 9 | import app.dinus.com.loadingdrawable.render.LoadingDrawable;
10 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
11 | import app.dinus.com.loadingdrawable.render.LoadingRendererFactory;
12 | 
13 | public class LoadingView extends ImageView {
14 |     private LoadingDrawable mLoadingDrawable;
15 | 
16 |     public LoadingView(Context context) {
17 |         super(context);
18 |     }
19 | 
20 |     public LoadingView(Context context, AttributeSet attrs) {
21 |         super(context, attrs);
22 |         initAttrs(context, attrs);
23 |     }
24 | 
25 |     private void initAttrs(Context context, AttributeSet attrs) {
26 |         try {
27 |             TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
28 |             int loadingRendererId = ta.getInt(R.styleable.LoadingView_loading_renderer, 0);
29 |             LoadingRenderer loadingRenderer = LoadingRendererFactory.createLoadingRenderer(context, loadingRendererId);
30 |             setLoadingRenderer(loadingRenderer);
31 |             ta.recycle();
32 |         } catch (Exception e) {
33 |             e.printStackTrace();
34 |         }
35 |     }
36 | 
37 |     public void setLoadingRenderer(LoadingRenderer loadingRenderer) {
38 |         mLoadingDrawable = new LoadingDrawable(loadingRenderer);
39 |         setImageDrawable(mLoadingDrawable);
40 |     }
41 | 
42 |     @Override
43 |     protected void onAttachedToWindow() {
44 |         super.onAttachedToWindow();
45 |         startAnimation();
46 |     }
47 | 
48 |     @Override
49 |     protected void onDetachedFromWindow() {
50 |         super.onDetachedFromWindow();
51 |         stopAnimation();
52 |     }
53 | 
54 |     @Override
55 |     protected void onVisibilityChanged(View changedView, int visibility) {
56 |         super.onVisibilityChanged(changedView, visibility);
57 | 
58 |         final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
59 |         if (visible) {
60 |             startAnimation();
61 |         } else {
62 |             stopAnimation();
63 |         }
64 |     }
65 | 
66 |     private void startAnimation() {
67 |         if (mLoadingDrawable != null) {
68 |             mLoadingDrawable.start();
69 |         }
70 |     }
71 | 
72 |     private void stopAnimation() {
73 |         if (mLoadingDrawable != null) {
74 |             mLoadingDrawable.stop();
75 |         }
76 |     }
77 | }
78 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/LoadingDrawable.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.loadingdrawable.render;
 2 | 
 3 | import android.graphics.Canvas;
 4 | import android.graphics.ColorFilter;
 5 | import android.graphics.PixelFormat;
 6 | import android.graphics.Rect;
 7 | import android.graphics.drawable.Animatable;
 8 | import android.graphics.drawable.Drawable;
 9 | 
10 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
11 | 
12 | public class LoadingDrawable extends Drawable implements Animatable {
13 |     private final LoadingRenderer mLoadingRender;
14 | 
15 |     private final Callback mCallback = new Callback() {
16 |         @Override
17 |         public void invalidateDrawable(Drawable d) {
18 |             invalidateSelf();
19 |         }
20 | 
21 |         @Override
22 |         public void scheduleDrawable(Drawable d, Runnable what, long when) {
23 |             scheduleSelf(what, when);
24 |         }
25 | 
26 |         @Override
27 |         public void unscheduleDrawable(Drawable d, Runnable what) {
28 |             unscheduleSelf(what);
29 |         }
30 |     };
31 | 
32 |     public LoadingDrawable(LoadingRenderer loadingRender) {
33 |         this.mLoadingRender = loadingRender;
34 |         this.mLoadingRender.setCallback(mCallback);
35 |     }
36 | 
37 |     @Override
38 |     protected void onBoundsChange(Rect bounds) {
39 |         super.onBoundsChange(bounds);
40 |         this.mLoadingRender.setBounds(bounds);
41 |     }
42 | 
43 |     @Override
44 |     public void draw(Canvas canvas) {
45 |         if (!getBounds().isEmpty()) {
46 |             this.mLoadingRender.draw(canvas);
47 |         }
48 |     }
49 | 
50 |     @Override
51 |     public void setAlpha(int alpha) {
52 |         this.mLoadingRender.setAlpha(alpha);
53 |     }
54 | 
55 |     @Override
56 |     public void setColorFilter(ColorFilter cf) {
57 |         this.mLoadingRender.setColorFilter(cf);
58 |     }
59 | 
60 |     @Override
61 |     public int getOpacity() {
62 |         return PixelFormat.TRANSLUCENT;
63 |     }
64 | 
65 |     @Override
66 |     public void start() {
67 |         this.mLoadingRender.start();
68 |     }
69 | 
70 |     @Override
71 |     public void stop() {
72 |         this.mLoadingRender.stop();
73 |     }
74 | 
75 |     @Override
76 |     public boolean isRunning() {
77 |         return this.mLoadingRender.isRunning();
78 |     }
79 | 
80 |     @Override
81 |     public int getIntrinsicHeight() {
82 |         return (int) this.mLoadingRender.mHeight;
83 |     }
84 | 
85 |     @Override
86 |     public int getIntrinsicWidth() {
87 |         return (int) this.mLoadingRender.mWidth;
88 |     }
89 | }
90 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/LoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.ValueAnimator;
  5 | import android.content.Context;
  6 | import android.graphics.Canvas;
  7 | import android.graphics.ColorFilter;
  8 | import android.graphics.Rect;
  9 | import android.graphics.RectF;
 10 | import android.graphics.drawable.Drawable;
 11 | import android.os.Handler;
 12 | import android.os.Looper;
 13 | import android.util.Log;
 14 | import android.view.animation.Animation;
 15 | import android.view.animation.LinearInterpolator;
 16 | 
 17 | import app.dinus.com.loadingdrawable.DensityUtil;
 18 | 
 19 | public abstract class LoadingRenderer {
 20 |     private static final long ANIMATION_DURATION = 1333;
 21 |     private static final float DEFAULT_SIZE = 56.0f;
 22 | 
 23 |     private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener
 24 |             = new ValueAnimator.AnimatorUpdateListener() {
 25 |         @Override
 26 |         public void onAnimationUpdate(ValueAnimator animation) {
 27 |             computeRender((float) animation.getAnimatedValue());
 28 |             invalidateSelf();
 29 |         }
 30 |     };
 31 | 
 32 |     /**
 33 |      * Whenever {@link LoadingDrawable} boundary changes mBounds will be updated.
 34 |      * More details you can see {@link LoadingDrawable#onBoundsChange(Rect)}
 35 |      */
 36 |     protected final Rect mBounds = new Rect();
 37 | 
 38 |     private Drawable.Callback mCallback;
 39 |     private ValueAnimator mRenderAnimator;
 40 | 
 41 |     protected long mDuration;
 42 | 
 43 |     protected float mWidth;
 44 |     protected float mHeight;
 45 | 
 46 |     public LoadingRenderer(Context context) {
 47 |         initParams(context);
 48 |         setupAnimators();
 49 |     }
 50 | 
 51 |     @Deprecated
 52 |     protected void draw(Canvas canvas, Rect bounds) {
 53 |     }
 54 | 
 55 |     protected void draw(Canvas canvas) {
 56 |         draw(canvas, mBounds);
 57 |     }
 58 | 
 59 |     protected abstract void computeRender(float renderProgress);
 60 | 
 61 |     protected abstract void setAlpha(int alpha);
 62 | 
 63 |     protected abstract void setColorFilter(ColorFilter cf);
 64 | 
 65 |     protected abstract void reset();
 66 | 
 67 |     protected void addRenderListener(Animator.AnimatorListener animatorListener) {
 68 |         mRenderAnimator.addListener(animatorListener);
 69 |     }
 70 | 
 71 |     void start() {
 72 |         reset();
 73 |         mRenderAnimator.addUpdateListener(mAnimatorUpdateListener);
 74 | 
 75 |         mRenderAnimator.setRepeatCount(ValueAnimator.INFINITE);
 76 |         mRenderAnimator.setDuration(mDuration);
 77 |         mRenderAnimator.start();
 78 |     }
 79 | 
 80 |     void stop() {
 81 |         // if I just call mRenderAnimator.end(),
 82 |         // it will always call the method onAnimationUpdate(ValueAnimator animation)
 83 |         // why ? if you know why please send email to me (dinus_developer@163.com)
 84 |         mRenderAnimator.removeUpdateListener(mAnimatorUpdateListener);
 85 | 
 86 |         mRenderAnimator.setRepeatCount(0);
 87 |         mRenderAnimator.setDuration(0);
 88 |         mRenderAnimator.end();
 89 |     }
 90 | 
 91 |     boolean isRunning() {
 92 |         return mRenderAnimator.isRunning();
 93 |     }
 94 | 
 95 |     void setCallback(Drawable.Callback callback) {
 96 |         this.mCallback = callback;
 97 |     }
 98 | 
 99 |     void setBounds(Rect bounds) {
100 |         mBounds.set(bounds);
101 |     }
102 | 
103 |     private void initParams(Context context) {
104 |         mWidth = DensityUtil.dip2px(context, DEFAULT_SIZE);
105 |         mHeight = DensityUtil.dip2px(context, DEFAULT_SIZE);
106 | 
107 |         mDuration = ANIMATION_DURATION;
108 |     }
109 | 
110 |     private void setupAnimators() {
111 |         mRenderAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
112 |         mRenderAnimator.setRepeatCount(Animation.INFINITE);
113 |         mRenderAnimator.setRepeatMode(Animation.RESTART);
114 |         mRenderAnimator.setDuration(mDuration);
115 |         //fuck you! the default interpolator is AccelerateDecelerateInterpolator
116 |         mRenderAnimator.setInterpolator(new LinearInterpolator());
117 |         mRenderAnimator.addUpdateListener(mAnimatorUpdateListener);
118 |     }
119 | 
120 |     private void invalidateSelf() {
121 |         mCallback.invalidateDrawable(null);
122 |     }
123 | 
124 | }
125 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/LoadingRendererFactory.java:
--------------------------------------------------------------------------------
 1 | package app.dinus.com.loadingdrawable.render;
 2 | 
 3 | import android.content.Context;
 4 | import android.util.SparseArray;
 5 | 
 6 | import java.lang.reflect.Constructor;
 7 | 
 8 | import app.dinus.com.loadingdrawable.render.animal.FishLoadingRenderer;
 9 | import app.dinus.com.loadingdrawable.render.animal.GhostsEyeLoadingRenderer;
10 | import app.dinus.com.loadingdrawable.render.circle.jump.CollisionLoadingRenderer;
11 | import app.dinus.com.loadingdrawable.render.circle.jump.DanceLoadingRenderer;
12 | import app.dinus.com.loadingdrawable.render.circle.jump.GuardLoadingRenderer;
13 | import app.dinus.com.loadingdrawable.render.circle.jump.SwapLoadingRenderer;
14 | import app.dinus.com.loadingdrawable.render.circle.rotate.GearLoadingRenderer;
15 | import app.dinus.com.loadingdrawable.render.circle.rotate.LevelLoadingRenderer;
16 | import app.dinus.com.loadingdrawable.render.circle.rotate.MaterialLoadingRenderer;
17 | import app.dinus.com.loadingdrawable.render.circle.rotate.WhorlLoadingRenderer;
18 | import app.dinus.com.loadingdrawable.render.goods.BalloonLoadingRenderer;
19 | import app.dinus.com.loadingdrawable.render.goods.WaterBottleLoadingRenderer;
20 | import app.dinus.com.loadingdrawable.render.scenery.DayNightLoadingRenderer;
21 | import app.dinus.com.loadingdrawable.render.scenery.ElectricFanLoadingRenderer;
22 | import app.dinus.com.loadingdrawable.render.shapechange.CircleBroodLoadingRenderer;
23 | import app.dinus.com.loadingdrawable.render.shapechange.CoolWaitLoadingRenderer;
24 | 
25 | public final class LoadingRendererFactory {
26 |     private static final SparseArray<Class<? extends LoadingRenderer>> LOADING_RENDERERS = new SparseArray<>();
27 | 
28 |     static {
29 |         //circle rotate
30 |         LOADING_RENDERERS.put(0, MaterialLoadingRenderer.class);
31 |         LOADING_RENDERERS.put(1, LevelLoadingRenderer.class);
32 |         LOADING_RENDERERS.put(2, WhorlLoadingRenderer.class);
33 |         LOADING_RENDERERS.put(3, GearLoadingRenderer.class);
34 |         //circle jump
35 |         LOADING_RENDERERS.put(4, SwapLoadingRenderer.class);
36 |         LOADING_RENDERERS.put(5, GuardLoadingRenderer.class);
37 |         LOADING_RENDERERS.put(6, DanceLoadingRenderer.class);
38 |         LOADING_RENDERERS.put(7, CollisionLoadingRenderer.class);
39 |         //scenery
40 |         LOADING_RENDERERS.put(8, DayNightLoadingRenderer.class);
41 |         LOADING_RENDERERS.put(9, ElectricFanLoadingRenderer.class);
42 |         //animal
43 |         LOADING_RENDERERS.put(10, FishLoadingRenderer.class);
44 |         LOADING_RENDERERS.put(11, GhostsEyeLoadingRenderer.class);
45 |         //goods
46 |         LOADING_RENDERERS.put(12, BalloonLoadingRenderer.class);
47 |         LOADING_RENDERERS.put(13, WaterBottleLoadingRenderer.class);
48 |         //shape change
49 |         LOADING_RENDERERS.put(14, CircleBroodLoadingRenderer.class);
50 |         LOADING_RENDERERS.put(15, CoolWaitLoadingRenderer.class);
51 |     }
52 | 
53 |     private LoadingRendererFactory() {
54 |     }
55 | 
56 |     public static LoadingRenderer createLoadingRenderer(Context context, int loadingRendererId) throws Exception {
57 |         Class<?> loadingRendererClazz = LOADING_RENDERERS.get(loadingRendererId);
58 |         Constructor<?>[] constructors = loadingRendererClazz.getDeclaredConstructors();
59 |         for (Constructor<?> constructor : constructors) {
60 |             Class<?>[] parameterTypes = constructor.getParameterTypes();
61 |             if (parameterTypes != null
62 |                     && parameterTypes.length == 1
63 |                     && parameterTypes[0].equals(Context.class)) {
64 |                 constructor.setAccessible(true);
65 |                 return (LoadingRenderer) constructor.newInstance(context);
66 |             }
67 |         }
68 | 
69 |         throw new InstantiationException();
70 |     }
71 | }
72 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/animal/FishLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.animal;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.DashPathEffect;
  8 | import android.graphics.Matrix;
  9 | import android.graphics.Paint;
 10 | import android.graphics.Path;
 11 | import android.graphics.PathMeasure;
 12 | import android.graphics.Rect;
 13 | import android.graphics.RectF;
 14 | import android.graphics.Region;
 15 | import android.util.DisplayMetrics;
 16 | import android.view.animation.Interpolator;
 17 | 
 18 | import app.dinus.com.loadingdrawable.DensityUtil;
 19 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 20 | 
 21 | public class FishLoadingRenderer extends LoadingRenderer {
 22 |     private Interpolator FISH_INTERPOLATOR = new FishInterpolator();
 23 | 
 24 |     private static final float DEFAULT_PATH_FULL_LINE_SIZE = 7.0f;
 25 |     private static final float DEFAULT_PATH_DOTTED_LINE_SIZE = DEFAULT_PATH_FULL_LINE_SIZE / 2.0f;
 26 |     private static final float DEFAULT_RIVER_HEIGHT = DEFAULT_PATH_FULL_LINE_SIZE * 8.5f;
 27 |     private static final float DEFAULT_RIVER_WIDTH = DEFAULT_PATH_FULL_LINE_SIZE * 5.5f;
 28 | 
 29 |     private static final float DEFAULT_FISH_EYE_SIZE = DEFAULT_PATH_FULL_LINE_SIZE * 0.5f;
 30 |     private static final float DEFAULT_FISH_WIDTH = DEFAULT_PATH_FULL_LINE_SIZE * 3.0f;
 31 |     private static final float DEFAULT_FISH_HEIGHT = DEFAULT_PATH_FULL_LINE_SIZE * 4.5f;
 32 | 
 33 |     private static final float DEFAULT_WIDTH = 200.0f;
 34 |     private static final float DEFAULT_HEIGHT = 150.0f;
 35 |     private static final float DEFAULT_RIVER_BANK_WIDTH = DEFAULT_PATH_FULL_LINE_SIZE;
 36 | 
 37 |     private static final long ANIMATION_DURATION = 800;
 38 |     private static final float DOTTED_LINE_WIDTH_COUNT = (8.5f + 5.5f - 2.0f) * 2.0f * 2.0f;
 39 |     private static final float DOTTED_LINE_WIDTH_RATE = 1.0f / DOTTED_LINE_WIDTH_COUNT;
 40 | 
 41 |     private final float[] FISH_MOVE_POINTS = new float[]{
 42 |             DOTTED_LINE_WIDTH_RATE * 3.0f, DOTTED_LINE_WIDTH_RATE * 6.0f,
 43 |             DOTTED_LINE_WIDTH_RATE * 15f, DOTTED_LINE_WIDTH_RATE * 18f,
 44 |             DOTTED_LINE_WIDTH_RATE * 27.0f, DOTTED_LINE_WIDTH_RATE * 30.0f,
 45 |             DOTTED_LINE_WIDTH_RATE * 39f, DOTTED_LINE_WIDTH_RATE * 42f,
 46 |     };
 47 | 
 48 |     private final float FISH_MOVE_POINTS_RATE = 1.0f / FISH_MOVE_POINTS.length;
 49 | 
 50 |     private static final int DEFAULT_COLOR = Color.parseColor("#fffefed6");
 51 | 
 52 |     private final Paint mPaint = new Paint();
 53 |     private final RectF mTempBounds = new RectF();
 54 | 
 55 |     private final float[] mFishHeadPos = new float[2];
 56 | 
 57 |     private Path mRiverPath;
 58 |     private PathMeasure mRiverMeasure;
 59 | 
 60 |     private float mFishRotateDegrees;
 61 | 
 62 |     private float mRiverBankWidth;
 63 |     private float mRiverWidth;
 64 |     private float mRiverHeight;
 65 |     private float mFishWidth;
 66 |     private float mFishHeight;
 67 |     private float mFishEyeSize;
 68 |     private float mPathFullLineSize;
 69 |     private float mPathDottedLineSize;
 70 | 
 71 |     private int mColor;
 72 | 
 73 |     private FishLoadingRenderer(Context context) {
 74 |         super(context);
 75 |         init(context);
 76 |         setupPaint();
 77 |     }
 78 | 
 79 |     private void init(Context context) {
 80 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 81 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 82 |         mRiverBankWidth = DensityUtil.dip2px(context, DEFAULT_RIVER_BANK_WIDTH);
 83 | 
 84 |         mPathFullLineSize = DensityUtil.dip2px(context, DEFAULT_PATH_FULL_LINE_SIZE);
 85 |         mPathDottedLineSize = DensityUtil.dip2px(context, DEFAULT_PATH_DOTTED_LINE_SIZE);
 86 |         mFishWidth = DensityUtil.dip2px(context, DEFAULT_FISH_WIDTH);
 87 |         mFishHeight = DensityUtil.dip2px(context, DEFAULT_FISH_HEIGHT);
 88 |         mFishEyeSize = DensityUtil.dip2px(context, DEFAULT_FISH_EYE_SIZE);
 89 |         mRiverWidth = DensityUtil.dip2px(context, DEFAULT_RIVER_WIDTH);
 90 |         mRiverHeight = DensityUtil.dip2px(context, DEFAULT_RIVER_HEIGHT);
 91 | 
 92 |         mColor = DEFAULT_COLOR;
 93 | 
 94 |         mDuration = ANIMATION_DURATION;
 95 |     }
 96 | 
 97 |     private void setupPaint() {
 98 |         mPaint.setAntiAlias(true);
 99 |         mPaint.setStrokeWidth(mRiverBankWidth);
100 |         mPaint.setStyle(Paint.Style.STROKE);
101 |         mPaint.setStrokeJoin(Paint.Join.MITER);
102 |         mPaint.setPathEffect(new DashPathEffect(new float[]{mPathFullLineSize, mPathDottedLineSize}, mPathDottedLineSize));
103 |     }
104 | 
105 |     @Override
106 |     protected void draw(Canvas canvas, Rect bounds) {
107 |         int saveCount = canvas.save();
108 |         RectF arcBounds = mTempBounds;
109 |         arcBounds.set(bounds);
110 | 
111 |         mPaint.setColor(mColor);
112 | 
113 |         //calculate fish clip bounds
114 |         //clip the width of the fish need to increase mPathDottedLineSize * 1.2f
115 |         RectF fishRectF = new RectF(mFishHeadPos[0] - mFishWidth / 2.0f - mPathDottedLineSize * 1.2f, mFishHeadPos[1] - mFishHeight / 2.0f,
116 |                 mFishHeadPos[0] + mFishWidth / 2.0f + mPathDottedLineSize * 1.2f, mFishHeadPos[1] + mFishHeight / 2.0f);
117 |         Matrix matrix = new Matrix();
118 |         matrix.postRotate(mFishRotateDegrees, fishRectF.centerX(), fishRectF.centerY());
119 |         matrix.mapRect(fishRectF);
120 | 
121 |         //draw river
122 |         int riverSaveCount = canvas.save();
123 |         mPaint.setStyle(Paint.Style.STROKE);
124 |         canvas.clipRect(fishRectF, Region.Op.DIFFERENCE);
125 |         canvas.drawPath(createRiverPath(arcBounds), mPaint);
126 |         canvas.restoreToCount(riverSaveCount);
127 | 
128 |         //draw fish
129 |         int fishSaveCount = canvas.save();
130 |         mPaint.setStyle(Paint.Style.FILL);
131 |         canvas.rotate(mFishRotateDegrees, mFishHeadPos[0], mFishHeadPos[1]);
132 |         canvas.clipPath(createFishEyePath(mFishHeadPos[0], mFishHeadPos[1] - mFishHeight * 0.06f), Region.Op.DIFFERENCE);
133 |         canvas.drawPath(createFishPath(mFishHeadPos[0], mFishHeadPos[1]), mPaint);
134 |         canvas.restoreToCount(fishSaveCount);
135 | 
136 |         canvas.restoreToCount(saveCount);
137 |     }
138 | 
139 |     private float calculateRotateDegrees(float fishProgress) {
140 |         if (fishProgress < FISH_MOVE_POINTS_RATE * 2) {
141 |             return 90;
142 |         }
143 | 
144 |         if (fishProgress < FISH_MOVE_POINTS_RATE * 4) {
145 |             return 180;
146 |         }
147 | 
148 |         if (fishProgress < FISH_MOVE_POINTS_RATE * 6) {
149 |             return 270;
150 |         }
151 | 
152 |         return 0.0f;
153 |     }
154 | 
155 |     @Override
156 |     protected void computeRender(float renderProgress) {
157 |         if (mRiverPath == null) {
158 |             return;
159 |         }
160 | 
161 |         if (mRiverMeasure == null) {
162 |             mRiverMeasure = new PathMeasure(mRiverPath, false);
163 |         }
164 | 
165 |         float fishProgress = FISH_INTERPOLATOR.getInterpolation(renderProgress);
166 | 
167 |         mRiverMeasure.getPosTan(mRiverMeasure.getLength() * fishProgress, mFishHeadPos, null);
168 |         mFishRotateDegrees = calculateRotateDegrees(fishProgress);
169 |     }
170 | 
171 |     @Override
172 |     protected void setAlpha(int alpha) {
173 | 
174 |     }
175 | 
176 |     @Override
177 |     protected void setColorFilter(ColorFilter cf) {
178 | 
179 |     }
180 | 
181 |     @Override
182 |     protected void reset() {
183 |     }
184 | 
185 |     private Path createFishEyePath(float fishEyeCenterX, float fishEyeCenterY) {
186 |         Path path = new Path();
187 |         path.addCircle(fishEyeCenterX, fishEyeCenterY, mFishEyeSize, Path.Direction.CW);
188 | 
189 |         return path;
190 |     }
191 | 
192 |     private Path createFishPath(float fishCenterX, float fishCenterY) {
193 |         Path path = new Path();
194 | 
195 |         float fishHeadX = fishCenterX;
196 |         float fishHeadY = fishCenterY - mFishHeight / 2.0f;
197 | 
198 |         //the head of the fish
199 |         path.moveTo(fishHeadX, fishHeadY);
200 |         //the left body of the fish
201 |         path.quadTo(fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.222f, fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.444f);
202 |         path.lineTo(fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.666f);
203 |         path.lineTo(fishHeadX - mFishWidth * 0.5f, fishHeadY + mFishHeight * 0.8f);
204 |         path.lineTo(fishHeadX - mFishWidth * 0.5f, fishHeadY + mFishHeight);
205 | 
206 |         //the tail of the fish
207 |         path.lineTo(fishHeadX, fishHeadY + mFishHeight * 0.9f);
208 | 
209 |         //the right body of the fish
210 |         path.lineTo(fishHeadX + mFishWidth * 0.5f, fishHeadY + mFishHeight);
211 |         path.lineTo(fishHeadX + mFishWidth * 0.5f, fishHeadY + mFishHeight * 0.8f);
212 |         path.lineTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.666f);
213 |         path.lineTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.444f);
214 |         path.quadTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.222f, fishHeadX, fishHeadY);
215 | 
216 |         path.close();
217 | 
218 |         return path;
219 |     }
220 | 
221 |     private Path createRiverPath(RectF arcBounds) {
222 |         if (mRiverPath != null) {
223 |             return mRiverPath;
224 |         }
225 | 
226 |         mRiverPath = new Path();
227 | 
228 |         RectF rectF = new RectF(arcBounds.centerX() - mRiverWidth / 2.0f, arcBounds.centerY() - mRiverHeight / 2.0f,
229 |                 arcBounds.centerX() + mRiverWidth / 2.0f, arcBounds.centerY() + mRiverHeight / 2.0f);
230 | 
231 |         rectF.inset(mRiverBankWidth / 2.0f, mRiverBankWidth / 2.0f);
232 | 
233 |         mRiverPath.addRect(rectF, Path.Direction.CW);
234 | 
235 |         return mRiverPath;
236 |     }
237 | 
238 |     private class FishInterpolator implements Interpolator {
239 |         @Override
240 |         public float getInterpolation(float input) {
241 |             int index = ((int) (input / FISH_MOVE_POINTS_RATE));
242 |             if (index >= FISH_MOVE_POINTS.length) {
243 |                 index = FISH_MOVE_POINTS.length - 1;
244 |             }
245 | 
246 |             return FISH_MOVE_POINTS[index];
247 |         }
248 |     }
249 | 
250 |     public static class Builder {
251 |         private Context mContext;
252 | 
253 |         public Builder(Context mContext) {
254 |             this.mContext = mContext;
255 |         }
256 | 
257 |         public FishLoadingRenderer build() {
258 |             FishLoadingRenderer loadingRenderer = new FishLoadingRenderer(mContext);
259 |             return loadingRenderer;
260 |         }
261 |     }
262 | }
263 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/animal/GhostsEyeLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.animal;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Path;
  9 | import android.graphics.Rect;
 10 | import android.graphics.RectF;
 11 | import android.util.DisplayMetrics;
 12 | import android.view.animation.Interpolator;
 13 | 
 14 | import app.dinus.com.loadingdrawable.DensityUtil;
 15 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 16 | 
 17 | public class GhostsEyeLoadingRenderer extends LoadingRenderer {
 18 |     private Interpolator EYE_BALL_INTERPOLATOR = new EyeBallInterpolator();
 19 |     private Interpolator EYE_CIRCLE_INTERPOLATOR = new EyeCircleInterpolator();
 20 | 
 21 |     private static final float DEFAULT_WIDTH = 200.0f;
 22 |     private static final float DEFAULT_HEIGHT = 176.0f;
 23 |     private static final float DEFAULT_EYE_EDGE_WIDTH = 5.0f;
 24 | 
 25 |     private static final float DEFAULT_EYE_BALL_HEIGHT = 9.0f;
 26 |     private static final float DEFAULT_EYE_BALL_WIDTH = 11.0f;
 27 | 
 28 |     private static final float DEFAULT_EYE_CIRCLE_INTERVAL = 8.0f;
 29 |     private static final float DEFAULT_EYE_BALL_OFFSET_Y = 2.0f;
 30 |     private static final float DEFAULT_ABOVE_RADIAN_EYE_CIRCLE_OFFSET = 6.0f;
 31 |     private static final float DEFAULT_EYE_CIRCLE_RADIUS = 21.0f;
 32 |     private static final float DEFAULT_MAX_EYE_JUMP_DISTANCE = 11.0f;
 33 | 
 34 |     private static final float LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET = 0.0f;
 35 |     private static final float RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET = 0.067f;
 36 | 
 37 |     private static final float LEFT_EYE_BALL_END_JUMP_OFFSET = 0.4f;
 38 |     private static final float LEFT_EYE_CIRCLE_END_JUMP_OFFSET = 0.533f;
 39 |     private static final float RIGHT_EYE_BALL_END_JUMP_OFFSET = 0.467f;
 40 |     private static final float RIGHT_EYE_CIRCLE_END_JUMP_OFFSET = 0.60f;
 41 | 
 42 |     private static final int DEGREE_180 = 180;
 43 | 
 44 |     private static final long ANIMATION_DURATION = 2333;
 45 | 
 46 |     private static final int DEFAULT_COLOR = Color.parseColor("#ff484852");
 47 | 
 48 |     private final Paint mPaint = new Paint();
 49 |     private final RectF mTempBounds = new RectF();
 50 | 
 51 |     private float mEyeInterval;
 52 |     private float mEyeCircleRadius;
 53 |     private float mMaxEyeJumptDistance;
 54 |     private float mAboveRadianEyeOffsetX;
 55 |     private float mEyeBallOffsetY;
 56 | 
 57 |     private float mEyeEdgeWidth;
 58 |     private float mEyeBallWidth;
 59 |     private float mEyeBallHeight;
 60 | 
 61 |     private float mLeftEyeCircleOffsetY;
 62 |     private float mRightEyeCircleOffsetY;
 63 |     private float mLeftEyeBallOffsetY;
 64 |     private float mRightEyeBallOffsetY;
 65 | 
 66 |     private int mColor;
 67 | 
 68 |     private GhostsEyeLoadingRenderer(Context context) {
 69 |         super(context);
 70 |         init(context);
 71 |         setupPaint();
 72 |     }
 73 | 
 74 |     private void init(Context context) {
 75 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 76 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 77 |         mEyeEdgeWidth = DensityUtil.dip2px(context, DEFAULT_EYE_EDGE_WIDTH);
 78 | 
 79 |         mEyeInterval = DensityUtil.dip2px(context, DEFAULT_EYE_CIRCLE_INTERVAL);
 80 |         mEyeBallOffsetY = DensityUtil.dip2px(context, DEFAULT_EYE_BALL_OFFSET_Y);
 81 |         mEyeCircleRadius = DensityUtil.dip2px(context, DEFAULT_EYE_CIRCLE_RADIUS);
 82 |         mMaxEyeJumptDistance = DensityUtil.dip2px(context, DEFAULT_MAX_EYE_JUMP_DISTANCE);
 83 |         mAboveRadianEyeOffsetX = DensityUtil.dip2px(context, DEFAULT_ABOVE_RADIAN_EYE_CIRCLE_OFFSET);
 84 | 
 85 |         mEyeBallWidth = DensityUtil.dip2px(context, DEFAULT_EYE_BALL_WIDTH);
 86 |         mEyeBallHeight = DensityUtil.dip2px(context, DEFAULT_EYE_BALL_HEIGHT);
 87 | 
 88 |         mColor = DEFAULT_COLOR;
 89 | 
 90 |         mDuration = ANIMATION_DURATION;
 91 |     }
 92 | 
 93 |     private void setupPaint() {
 94 |         mPaint.setAntiAlias(true);
 95 |         mPaint.setStrokeWidth(mEyeEdgeWidth);
 96 |         mPaint.setStrokeJoin(Paint.Join.ROUND);
 97 |         mPaint.setStyle(Paint.Style.STROKE);
 98 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
 99 |     }
100 | 
101 |     @Override
102 |     protected void draw(Canvas canvas, Rect bounds) {
103 |         int saveCount = canvas.save();
104 |         RectF arcBounds = mTempBounds;
105 |         arcBounds.set(bounds);
106 | 
107 |         mPaint.setColor(mColor);
108 | 
109 |         mPaint.setStyle(Paint.Style.STROKE);
110 |         canvas.drawPath(createLeftEyeCircle(arcBounds, mLeftEyeCircleOffsetY), mPaint);
111 |         canvas.drawPath(createRightEyeCircle(arcBounds, mRightEyeCircleOffsetY), mPaint);
112 | 
113 |         mPaint.setStyle(Paint.Style.FILL);
114 |         //create left eye ball
115 |         canvas.drawOval(createLeftEyeBall(arcBounds, mLeftEyeBallOffsetY), mPaint);
116 |         //create right eye ball
117 |         canvas.drawOval(createRightEyeBall(arcBounds, mRightEyeBallOffsetY), mPaint);
118 | 
119 |         canvas.restoreToCount(saveCount);
120 |     }
121 | 
122 |     @Override
123 |     protected void computeRender(float renderProgress) {
124 |         if (renderProgress <= LEFT_EYE_BALL_END_JUMP_OFFSET && renderProgress >= LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
125 |             float eyeCircle$BallJumpUpProgress = (renderProgress - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (LEFT_EYE_BALL_END_JUMP_OFFSET - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
126 |             mLeftEyeBallOffsetY = -mMaxEyeJumptDistance * EYE_BALL_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
127 |         }
128 | 
129 |         if (renderProgress <= LEFT_EYE_CIRCLE_END_JUMP_OFFSET && renderProgress >= LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
130 |             float eyeCircle$BallJumpUpProgress = (renderProgress - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (LEFT_EYE_CIRCLE_END_JUMP_OFFSET - LEFT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
131 |             mLeftEyeCircleOffsetY = -mMaxEyeJumptDistance * EYE_CIRCLE_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
132 |         }
133 | 
134 |         if (renderProgress <= RIGHT_EYE_BALL_END_JUMP_OFFSET && renderProgress >= RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
135 |             float eyeCircle$BallJumpUpProgress = (renderProgress - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (RIGHT_EYE_BALL_END_JUMP_OFFSET - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
136 |             mRightEyeBallOffsetY = -mMaxEyeJumptDistance * EYE_BALL_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
137 |         }
138 | 
139 |         if (renderProgress <= RIGHT_EYE_CIRCLE_END_JUMP_OFFSET && renderProgress >= RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) {
140 |             float eyeCircle$BallJumpUpProgress = (renderProgress - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET) / (RIGHT_EYE_CIRCLE_END_JUMP_OFFSET - RIGHT_EYE_CIRCLE$BALL_START_JUMP_UP_OFFSET);
141 |             mRightEyeCircleOffsetY = -mMaxEyeJumptDistance * EYE_CIRCLE_INTERPOLATOR.getInterpolation(eyeCircle$BallJumpUpProgress);
142 |         }
143 |     }
144 | 
145 |     @Override
146 |     protected void setAlpha(int alpha) {
147 | 
148 |     }
149 | 
150 |     @Override
151 |     protected void setColorFilter(ColorFilter cf) {
152 | 
153 |     }
154 | 
155 |     @Override
156 |     protected void reset() {
157 |         mLeftEyeBallOffsetY = 0.0f;
158 |         mRightEyeBallOffsetY = 0.0f;
159 |         mLeftEyeCircleOffsetY = 0.0f;
160 |         mRightEyeCircleOffsetY = 0.0f;
161 |     }
162 | 
163 |     private RectF createLeftEyeBall(RectF arcBounds, float offsetY) {
164 |         //the center of the left eye
165 |         float leftEyeCenterX = arcBounds.centerX() - mEyeInterval / 2.0f - mEyeCircleRadius;
166 |         float leftEyeCenterY = arcBounds.centerY() - mEyeBallOffsetY + offsetY;
167 | 
168 |         RectF rectF = new RectF(leftEyeCenterX - mEyeBallWidth / 2.0f, leftEyeCenterY - mEyeBallHeight / 2.0f,
169 |                 leftEyeCenterX + mEyeBallWidth / 2.0f, leftEyeCenterY + mEyeBallHeight / 2.0f);
170 | 
171 |         return rectF;
172 |     }
173 | 
174 |     private RectF createRightEyeBall(RectF arcBounds, float offsetY) {
175 |         //the center of the right eye
176 |         float rightEyeCenterX = arcBounds.centerX() + mEyeInterval / 2.0f + mEyeCircleRadius;
177 |         float rightEyeCenterY = arcBounds.centerY() - mEyeBallOffsetY + offsetY;
178 | 
179 |         RectF rectF = new RectF(rightEyeCenterX - mEyeBallWidth / 2.0f, rightEyeCenterY - mEyeBallHeight / 2.0f,
180 |                 rightEyeCenterX + mEyeBallWidth / 2.0f, rightEyeCenterY + mEyeBallHeight / 2.0f);
181 | 
182 |         return rectF;
183 |     }
184 | 
185 | 
186 |     private Path createLeftEyeCircle(RectF arcBounds, float offsetY) {
187 |         Path path = new Path();
188 | 
189 |         //the center of the left eye
190 |         float leftEyeCenterX = arcBounds.centerX() - mEyeInterval / 2.0f - mEyeCircleRadius;
191 |         float leftEyeCenterY = arcBounds.centerY() + offsetY;
192 |         //the bounds of left eye
193 |         RectF leftEyeBounds = new RectF(leftEyeCenterX - mEyeCircleRadius, leftEyeCenterY - mEyeCircleRadius,
194 |                 leftEyeCenterX + mEyeCircleRadius, leftEyeCenterY + mEyeCircleRadius);
195 |         path.addArc(leftEyeBounds, 0, DEGREE_180 + 15);
196 |         //the above radian of of the eye
197 |         path.quadTo(leftEyeBounds.left + mAboveRadianEyeOffsetX, leftEyeBounds.top + mEyeCircleRadius * 0.2f,
198 |                 leftEyeBounds.left + mAboveRadianEyeOffsetX / 4.0f, leftEyeBounds.top - mEyeCircleRadius * 0.15f);
199 | 
200 |         return path;
201 |     }
202 | 
203 |     private Path createRightEyeCircle(RectF arcBounds, float offsetY) {
204 |         Path path = new Path();
205 | 
206 |         //the center of the right eye
207 |         float rightEyeCenterX = arcBounds.centerX() + mEyeInterval / 2.0f + mEyeCircleRadius;
208 |         float rightEyeCenterY = arcBounds.centerY() + offsetY;
209 |         //the bounds of left eye
210 |         RectF leftEyeBounds = new RectF(rightEyeCenterX - mEyeCircleRadius, rightEyeCenterY - mEyeCircleRadius,
211 |                 rightEyeCenterX + mEyeCircleRadius, rightEyeCenterY + mEyeCircleRadius);
212 |         path.addArc(leftEyeBounds, 180, -(DEGREE_180 + 15));
213 |         //the above radian of of the eye
214 |         path.quadTo(leftEyeBounds.right - mAboveRadianEyeOffsetX, leftEyeBounds.top + mEyeCircleRadius * 0.2f,
215 |                 leftEyeBounds.right - mAboveRadianEyeOffsetX / 4.0f, leftEyeBounds.top - mEyeCircleRadius * 0.15f);
216 | 
217 |         return path;
218 |     }
219 | 
220 |     private class EyeCircleInterpolator implements Interpolator {
221 | 
222 |         @Override
223 |         public float getInterpolation(float input) {
224 |             if (input < 0.25f) {
225 |                 return input * 4.0f;
226 |             } else if (input < 0.5f) {
227 |                 return 1.0f - (input - 0.25f) * 4.0f;
228 |             } else if (input < 0.75f) {
229 |                 return (input - 0.5f) * 2.0f;
230 |             } else {
231 |                 return 0.5f - (input - 0.75f) * 2.0f;
232 |             }
233 | 
234 |         }
235 |     }
236 | 
237 |     private class EyeBallInterpolator implements Interpolator {
238 | 
239 |         @Override
240 |         public float getInterpolation(float input) {
241 |             if (input < 0.333333f) {
242 |                 return input * 3.0f;
243 |             } else {
244 |                 return 1.0f - (input - 0.333333f) * 1.5f;
245 |             }
246 |         }
247 |     }
248 | 
249 |     public static class Builder {
250 |         private Context mContext;
251 | 
252 |         public Builder(Context mContext) {
253 |             this.mContext = mContext;
254 |         }
255 | 
256 |         public GhostsEyeLoadingRenderer build() {
257 |             GhostsEyeLoadingRenderer loadingRenderer = new GhostsEyeLoadingRenderer(mContext);
258 |             return loadingRenderer;
259 |         }
260 |     }
261 | }
262 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/jump/CollisionLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.jump;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.LinearGradient;
  8 | import android.graphics.Paint;
  9 | import android.graphics.RectF;
 10 | import android.graphics.Shader;
 11 | import android.support.annotation.Size;
 12 | import android.view.animation.AccelerateInterpolator;
 13 | import android.view.animation.DecelerateInterpolator;
 14 | import android.view.animation.Interpolator;
 15 | 
 16 | import app.dinus.com.loadingdrawable.DensityUtil;
 17 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 18 | 
 19 | public class CollisionLoadingRenderer extends LoadingRenderer {
 20 |     private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
 21 |     private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
 22 | 
 23 |     private static final int MAX_ALPHA = 255;
 24 |     private static final int OVAL_ALPHA = 64;
 25 | 
 26 |     private static final int DEFAULT_BALL_COUNT = 7;
 27 | 
 28 |     private static final float DEFAULT_OVAL_HEIGHT = 1.5f;
 29 |     private static final float DEFAULT_BALL_RADIUS = 7.5f;
 30 |     private static final float DEFAULT_WIDTH = 15.0f * 11;
 31 |     private static final float DEFAULT_HEIGHT = 15.0f * 4;
 32 | 
 33 |     private static final float START_LEFT_DURATION_OFFSET = 0.25f;
 34 |     private static final float START_RIGHT_DURATION_OFFSET = 0.5f;
 35 |     private static final float END_RIGHT_DURATION_OFFSET = 0.75f;
 36 |     private static final float END_LEFT_DURATION_OFFSET = 1.0f;
 37 | 
 38 |     private static final int[] DEFAULT_COLORS = new int[]{
 39 |             Color.parseColor("#FF28435D"), Color.parseColor("#FFC32720")
 40 |     };
 41 | 
 42 |     private static final float[] DEFAULT_POSITIONS = new float[]{
 43 |             0.0f, 1.0f
 44 |     };
 45 | 
 46 |     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 47 |     private final RectF mOvalRect = new RectF();
 48 | 
 49 |     @Size(2)
 50 |     private int[] mColors;
 51 |     private float[] mPositions;
 52 | 
 53 |     private float mOvalVerticalRadius;
 54 | 
 55 |     private float mBallRadius;
 56 |     private float mBallCenterY;
 57 |     private float mBallSideOffsets;
 58 |     private float mBallMoveXOffsets;
 59 |     private float mBallQuadCoefficient;
 60 | 
 61 |     private float mLeftBallMoveXOffsets;
 62 |     private float mLeftBallMoveYOffsets;
 63 |     private float mRightBallMoveXOffsets;
 64 |     private float mRightBallMoveYOffsets;
 65 | 
 66 |     private float mLeftOvalShapeRate;
 67 |     private float mRightOvalShapeRate;
 68 | 
 69 |     private int mBallCount;
 70 | 
 71 |     private CollisionLoadingRenderer(Context context) {
 72 |         super(context);
 73 |         init(context);
 74 |         adjustParams();
 75 |         setupPaint();
 76 |     }
 77 | 
 78 |     private void init(Context context) {
 79 |         mBallRadius = DensityUtil.dip2px(context, DEFAULT_BALL_RADIUS);
 80 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 81 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 82 |         mOvalVerticalRadius = DensityUtil.dip2px(context, DEFAULT_OVAL_HEIGHT);
 83 | 
 84 |         mColors = DEFAULT_COLORS;
 85 |         mPositions = DEFAULT_POSITIONS;
 86 | 
 87 |         mBallCount = DEFAULT_BALL_COUNT;
 88 | 
 89 |         //mBallMoveYOffsets = mBallQuadCoefficient * mBallMoveXOffsets ^ 2
 90 |         // ==> if mBallMoveYOffsets == mBallMoveXOffsets
 91 |         // ==> mBallQuadCoefficient = 1.0f / mBallMoveXOffsets;
 92 |         mBallMoveXOffsets = 1.5f * (2 * mBallRadius);
 93 |         mBallQuadCoefficient = 1.0f / mBallMoveXOffsets;
 94 |     }
 95 | 
 96 |     private void adjustParams() {
 97 |         mBallCenterY = mHeight / 2.0f;
 98 |         mBallSideOffsets = (mWidth - mBallRadius * 2.0f * (mBallCount - 2)) / 2;
 99 |     }
100 | 
101 |     private void setupPaint() {
102 |         mPaint.setStyle(Paint.Style.FILL);
103 |         mPaint.setShader(new LinearGradient(mBallSideOffsets, 0, mWidth - mBallSideOffsets, 0,
104 |                 mColors, mPositions, Shader.TileMode.CLAMP));
105 |     }
106 | 
107 |     @Override
108 |     protected void draw(Canvas canvas) {
109 |         int saveCount = canvas.save();
110 | 
111 |         for (int i = 1; i < mBallCount - 1; i++) {
112 |             mPaint.setAlpha(MAX_ALPHA);
113 |             canvas.drawCircle(mBallRadius * (i * 2 - 1) + mBallSideOffsets, mBallCenterY, mBallRadius, mPaint);
114 | 
115 |             mOvalRect.set(mBallRadius * (i * 2 - 2) + mBallSideOffsets, mHeight - mOvalVerticalRadius * 2,
116 |                     mBallRadius * (i * 2) + mBallSideOffsets, mHeight);
117 |             mPaint.setAlpha(OVAL_ALPHA);
118 |             canvas.drawOval(mOvalRect, mPaint);
119 |         }
120 | 
121 |         //draw the first ball
122 |         mPaint.setAlpha(MAX_ALPHA);
123 |         canvas.drawCircle(mBallSideOffsets - mBallRadius - mLeftBallMoveXOffsets,
124 |                 mBallCenterY - mLeftBallMoveYOffsets, mBallRadius, mPaint);
125 | 
126 |         mOvalRect.set(mBallSideOffsets - mBallRadius - mBallRadius * mLeftOvalShapeRate - mLeftBallMoveXOffsets,
127 |                 mHeight - mOvalVerticalRadius - mOvalVerticalRadius * mLeftOvalShapeRate,
128 |                 mBallSideOffsets - mBallRadius + mBallRadius * mLeftOvalShapeRate - mLeftBallMoveXOffsets,
129 |                 mHeight - mOvalVerticalRadius + mOvalVerticalRadius * mLeftOvalShapeRate);
130 |         mPaint.setAlpha(OVAL_ALPHA);
131 |         canvas.drawOval(mOvalRect, mPaint);
132 | 
133 |         //draw the last ball
134 |         mPaint.setAlpha(MAX_ALPHA);
135 |         canvas.drawCircle(mBallRadius * (mBallCount * 2 - 3) + mBallSideOffsets + mRightBallMoveXOffsets,
136 |                 mBallCenterY - mRightBallMoveYOffsets, mBallRadius, mPaint);
137 | 
138 |         mOvalRect.set(mBallRadius * (mBallCount * 2 - 3) - mBallRadius * mRightOvalShapeRate + mBallSideOffsets + mRightBallMoveXOffsets,
139 |                 mHeight - mOvalVerticalRadius - mOvalVerticalRadius * mRightOvalShapeRate,
140 |                 mBallRadius * (mBallCount * 2 - 3) + mBallRadius * mRightOvalShapeRate + mBallSideOffsets + mRightBallMoveXOffsets,
141 |                 mHeight - mOvalVerticalRadius + mOvalVerticalRadius * mRightOvalShapeRate);
142 |         mPaint.setAlpha(OVAL_ALPHA);
143 |         canvas.drawOval(mOvalRect, mPaint);
144 | 
145 |         canvas.restoreToCount(saveCount);
146 |     }
147 | 
148 |     @Override
149 |     protected void computeRender(float renderProgress) {
150 |         // Moving the left ball to the left sides only occurs in the first 25% of a jump animation
151 |         if (renderProgress <= START_LEFT_DURATION_OFFSET) {
152 |             float startLeftOffsetProgress = renderProgress / START_LEFT_DURATION_OFFSET;
153 |             computeLeftBallMoveOffsets(DECELERATE_INTERPOLATOR.getInterpolation(startLeftOffsetProgress));
154 |             return;
155 |         }
156 | 
157 |         // Moving the left ball to the origin location only occurs between 25% and 50% of a jump ring animation
158 |         if (renderProgress <= START_RIGHT_DURATION_OFFSET) {
159 |             float startRightOffsetProgress = (renderProgress - START_LEFT_DURATION_OFFSET) / (START_RIGHT_DURATION_OFFSET - START_LEFT_DURATION_OFFSET);
160 |             computeLeftBallMoveOffsets(ACCELERATE_INTERPOLATOR.getInterpolation(1.0f - startRightOffsetProgress));
161 |             return;
162 |         }
163 | 
164 |         // Moving the right ball to the right sides only occurs between 50% and 75% of a jump animation
165 |         if (renderProgress <= END_RIGHT_DURATION_OFFSET) {
166 |             float endRightOffsetProgress = (renderProgress - START_RIGHT_DURATION_OFFSET) / (END_RIGHT_DURATION_OFFSET - START_RIGHT_DURATION_OFFSET);
167 |             computeRightBallMoveOffsets(DECELERATE_INTERPOLATOR.getInterpolation(endRightOffsetProgress));
168 |             return;
169 |         }
170 | 
171 |         // Moving the right ball to the origin location only occurs after 75% of a jump animation
172 |         if (renderProgress <= END_LEFT_DURATION_OFFSET) {
173 |             float endRightOffsetProgress = (renderProgress - END_RIGHT_DURATION_OFFSET) / (END_LEFT_DURATION_OFFSET - END_RIGHT_DURATION_OFFSET);
174 |             computeRightBallMoveOffsets(ACCELERATE_INTERPOLATOR.getInterpolation(1 - endRightOffsetProgress));
175 |             return;
176 |         }
177 | 
178 |     }
179 | 
180 |     private void computeLeftBallMoveOffsets(float progress) {
181 |         mRightBallMoveXOffsets = 0.0f;
182 |         mRightBallMoveYOffsets = 0.0f;
183 | 
184 |         mLeftOvalShapeRate = 1.0f - progress;
185 |         mLeftBallMoveXOffsets = mBallMoveXOffsets * progress;
186 |         mLeftBallMoveYOffsets = (float) (Math.pow(mLeftBallMoveXOffsets, 2) * mBallQuadCoefficient);
187 |     }
188 | 
189 |     private void computeRightBallMoveOffsets(float progress) {
190 |         mLeftBallMoveXOffsets = 0.0f;
191 |         mLeftBallMoveYOffsets = 0.0f;
192 | 
193 |         mRightOvalShapeRate = 1.0f - progress;
194 |         mRightBallMoveXOffsets = mBallMoveXOffsets * progress;
195 |         mRightBallMoveYOffsets = (float) (Math.pow(mRightBallMoveXOffsets, 2) * mBallQuadCoefficient);
196 |     }
197 | 
198 |     @Override
199 |     protected void setAlpha(int alpha) {
200 |         mPaint.setAlpha(alpha);
201 |     }
202 | 
203 |     @Override
204 |     protected void setColorFilter(ColorFilter cf) {
205 |         mPaint.setColorFilter(cf);
206 |     }
207 | 
208 |     @Override
209 |     protected void reset() {
210 |     }
211 | 
212 |     private void apply(Builder builder) {
213 |         this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
214 |         this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
215 | 
216 |         this.mOvalVerticalRadius = builder.mOvalVerticalRadius > 0 ? builder.mOvalVerticalRadius : this.mOvalVerticalRadius;
217 |         this.mBallRadius = builder.mBallRadius > 0 ? builder.mBallRadius : this.mBallRadius;
218 |         this.mBallMoveXOffsets = builder.mBallMoveXOffsets > 0 ? builder.mBallMoveXOffsets : this.mBallMoveXOffsets;
219 |         this.mBallQuadCoefficient = builder.mBallQuadCoefficient > 0 ? builder.mBallQuadCoefficient : this.mBallQuadCoefficient;
220 |         this.mBallCount = builder.mBallCount > 0 ? builder.mBallCount : this.mBallCount;
221 | 
222 |         this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
223 | 
224 |         this.mColors = builder.mColors != null ? builder.mColors : this.mColors;
225 | 
226 |         adjustParams();
227 |         setupPaint();
228 |     }
229 | 
230 |     public static class Builder {
231 |         private Context mContext;
232 | 
233 |         private int mWidth;
234 |         private int mHeight;
235 | 
236 |         private float mOvalVerticalRadius;
237 | 
238 |         private int mBallCount;
239 |         private float mBallRadius;
240 |         private float mBallMoveXOffsets;
241 |         private float mBallQuadCoefficient;
242 | 
243 |         private int mDuration;
244 | 
245 |         @Size(2)
246 |         private int[] mColors;
247 | 
248 |         public Builder(Context mContext) {
249 |             this.mContext = mContext;
250 |         }
251 | 
252 |         public Builder setWidth(int width) {
253 |             this.mWidth = width;
254 |             return this;
255 |         }
256 | 
257 |         public Builder setHeight(int height) {
258 |             this.mHeight = height;
259 |             return this;
260 |         }
261 | 
262 |         public Builder setOvalVerticalRadius(int ovalVerticalRadius) {
263 |             this.mOvalVerticalRadius = ovalVerticalRadius;
264 |             return this;
265 |         }
266 | 
267 |         public Builder setBallRadius(int ballRadius) {
268 |             this.mBallRadius = ballRadius;
269 |             return this;
270 |         }
271 | 
272 |         public Builder setBallMoveXOffsets(int ballMoveXOffsets) {
273 |             this.mBallMoveXOffsets = ballMoveXOffsets;
274 |             return this;
275 |         }
276 | 
277 |         public Builder setBallQuadCoefficient(int ballQuadCoefficient) {
278 |             this.mBallQuadCoefficient = ballQuadCoefficient;
279 |             return this;
280 |         }
281 | 
282 |         public Builder setBallCount(int ballCount) {
283 |             this.mBallCount = ballCount;
284 |             return this;
285 |         }
286 | 
287 |         public Builder setColors(@Size(2) int[] colors) {
288 |             this.mColors = colors;
289 |             return this;
290 |         }
291 | 
292 |         public Builder setDuration(int duration) {
293 |             this.mDuration = duration;
294 |             return this;
295 |         }
296 | 
297 |         public CollisionLoadingRenderer build() {
298 |             CollisionLoadingRenderer loadingRenderer = new CollisionLoadingRenderer(mContext);
299 |             loadingRenderer.apply(this);
300 |             return loadingRenderer;
301 |         }
302 |     }
303 | }
304 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/jump/DanceLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.jump;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Rect;
  9 | import android.graphics.RectF;
 10 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
 11 | import android.util.DisplayMetrics;
 12 | import android.util.TypedValue;
 13 | import android.view.animation.AccelerateInterpolator;
 14 | import android.view.animation.DecelerateInterpolator;
 15 | import android.view.animation.Interpolator;
 16 | 
 17 | import app.dinus.com.loadingdrawable.DensityUtil;
 18 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 19 | 
 20 | public class DanceLoadingRenderer extends LoadingRenderer {
 21 |     private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
 22 |     private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
 23 |     private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
 24 | 
 25 |     private static final long ANIMATION_DURATION = 1888;
 26 | 
 27 |     private static final float DEFAULT_CENTER_RADIUS = 12.5f;
 28 |     private static final float DEFAULT_STROKE_WIDTH = 1.5f;
 29 |     private static final float DEFAULT_DANCE_BALL_RADIUS = 2.0f;
 30 | 
 31 |     private static final int NUM_POINTS = 3;
 32 |     private static final int DEGREE_360 = 360;
 33 |     private static final int RING_START_ANGLE = -90;
 34 |     private static final int DANCE_START_ANGLE = 0;
 35 |     private static final int DANCE_INTERVAL_ANGLE = 60;
 36 | 
 37 |     private static final int DEFAULT_COLOR = Color.WHITE;
 38 | 
 39 |     //the center coordinate of the oval
 40 |     private static final float[] POINT_X = new float[NUM_POINTS];
 41 |     private static final float[] POINT_Y = new float[NUM_POINTS];
 42 |     //1: the coordinate x from small to large; -1: the coordinate x from large to small
 43 |     private static final int[] DIRECTION = new int[]{1, 1, -1};
 44 | 
 45 |     private static final float BALL_FORWARD_START_ENTER_DURATION_OFFSET = 0f;
 46 |     private static final float BALL_FORWARD_END_ENTER_DURATION_OFFSET = 0.125f;
 47 | 
 48 |     private static final float RING_FORWARD_START_ROTATE_DURATION_OFFSET = 0.125f;
 49 |     private static final float RING_FORWARD_END_ROTATE_DURATION_OFFSET = 0.375f;
 50 | 
 51 |     private static final float CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET = 0.225f;
 52 |     private static final float CENTER_CIRCLE_FORWARD_END_SCALE_DURATION_OFFSET = 0.475f;
 53 | 
 54 |     private static final float BALL_FORWARD_START_EXIT_DURATION_OFFSET = 0.375f;
 55 |     private static final float BALL_FORWARD_END_EXIT_DURATION_OFFSET = 0.54f;
 56 | 
 57 |     private static final float RING_REVERSAL_START_ROTATE_DURATION_OFFSET = 0.5f;
 58 |     private static final float RING_REVERSAL_END_ROTATE_DURATION_OFFSET = 0.75f;
 59 | 
 60 |     private static final float BALL_REVERSAL_START_ENTER_DURATION_OFFSET = 0.6f;
 61 |     private static final float BALL_REVERSAL_END_ENTER_DURATION_OFFSET = 0.725f;
 62 | 
 63 |     private static final float CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET = 0.675f;
 64 |     private static final float CENTER_CIRCLE_REVERSAL_END_SCALE_DURATION_OFFSET = 0.875f;
 65 | 
 66 |     private static final float BALL_REVERSAL_START_EXIT_DURATION_OFFSET = 0.875f;
 67 |     private static final float BALL_REVERSAL_END_EXIT_DURATION_OFFSET = 1.0f;
 68 | 
 69 |     private final Paint mPaint = new Paint();
 70 |     private final RectF mTempBounds = new RectF();
 71 |     private final RectF mCurrentBounds = new RectF();
 72 | 
 73 |     private float mScale;
 74 |     private float mRotation;
 75 |     private float mStrokeInset;
 76 | 
 77 |     private float mCenterRadius;
 78 |     private float mStrokeWidth;
 79 |     private float mDanceBallRadius;
 80 |     private float mShapeChangeWidth;
 81 |     private float mShapeChangeHeight;
 82 | 
 83 |     private int mColor;
 84 |     private int mArcColor;
 85 | 
 86 |     private DanceLoadingRenderer(Context context) {
 87 |         super(context);
 88 |         init(context);
 89 |         setupPaint();
 90 |     }
 91 | 
 92 |     private void init(Context context) {
 93 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 94 |         mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
 95 |         mDanceBallRadius = DensityUtil.dip2px(context, DEFAULT_DANCE_BALL_RADIUS);
 96 | 
 97 |         setColor(DEFAULT_COLOR);
 98 |         setInsets((int) mWidth, (int) mHeight);
 99 |         mDuration = ANIMATION_DURATION;
100 |     }
101 | 
102 |     private void setupPaint() {
103 |         mPaint.setAntiAlias(true);
104 |         mPaint.setStrokeWidth(mStrokeWidth);
105 |         mPaint.setStyle(Paint.Style.STROKE);
106 |     }
107 | 
108 |     @Override
109 |     protected void draw(Canvas canvas, Rect bounds) {
110 |         int saveCount = canvas.save();
111 | 
112 |         mTempBounds.set(bounds);
113 |         mTempBounds.inset(mStrokeInset, mStrokeInset);
114 |         mCurrentBounds.set(mTempBounds);
115 | 
116 |         float outerCircleRadius = Math.min(mTempBounds.height(), mTempBounds.width()) / 2.0f;
117 |         float interCircleRadius = outerCircleRadius / 2.0f;
118 |         float centerRingWidth = interCircleRadius - mStrokeWidth / 2;
119 | 
120 |         mPaint.setStyle(Paint.Style.STROKE);
121 |         mPaint.setColor(mColor);
122 |         mPaint.setStrokeWidth(mStrokeWidth);
123 |         canvas.drawCircle(mTempBounds.centerX(), mTempBounds.centerY(), outerCircleRadius, mPaint);
124 |         mPaint.setStyle(Paint.Style.FILL);
125 |         canvas.drawCircle(mTempBounds.centerX(), mTempBounds.centerY(), interCircleRadius * mScale, mPaint);
126 | 
127 |         if (mRotation != 0) {
128 |             mPaint.setColor(mArcColor);
129 |             mPaint.setStyle(Paint.Style.STROKE);
130 |             //strokeWidth / 2.0f + mStrokeWidth / 2.0f is the center of the inter circle width
131 |             mTempBounds.inset(centerRingWidth / 2.0f + mStrokeWidth / 2.0f, centerRingWidth / 2.0f + mStrokeWidth / 2.0f);
132 |             mPaint.setStrokeWidth(centerRingWidth);
133 |             canvas.drawArc(mTempBounds, RING_START_ANGLE, mRotation, false, mPaint);
134 |         }
135 | 
136 |         mPaint.setColor(mColor);
137 |         mPaint.setStyle(Paint.Style.FILL);
138 |         for (int i = 0; i < NUM_POINTS; i++) {
139 |             canvas.rotate(i * DANCE_INTERVAL_ANGLE, POINT_X[i], POINT_Y[i]);
140 |             RectF rectF = new RectF(POINT_X[i] - mDanceBallRadius - mShapeChangeWidth / 2.0f,
141 |                     POINT_Y[i] - mDanceBallRadius - mShapeChangeHeight / 2.0f,
142 |                     POINT_X[i] + mDanceBallRadius + mShapeChangeWidth / 2.0f,
143 |                     POINT_Y[i] + mDanceBallRadius + mShapeChangeHeight / 2.0f);
144 |             canvas.drawOval(rectF, mPaint);
145 |             canvas.rotate(-i * DANCE_INTERVAL_ANGLE, POINT_X[i], POINT_Y[i]);
146 |         }
147 | 
148 |         canvas.restoreToCount(saveCount);
149 |     }
150 | 
151 |     @Override
152 |     protected void computeRender(float renderProgress) {
153 |         float radius = Math.min(mCurrentBounds.height(), mCurrentBounds.width()) / 2.0f;
154 |         //the origin coordinate is the centerLeft of the field mCurrentBounds
155 |         float originCoordinateX = mCurrentBounds.left;
156 |         float originCoordinateY = mCurrentBounds.top + radius;
157 | 
158 |         if (renderProgress <= BALL_FORWARD_END_ENTER_DURATION_OFFSET && renderProgress > BALL_FORWARD_START_ENTER_DURATION_OFFSET) {
159 |             final float ballForwardEnterProgress = (renderProgress - BALL_FORWARD_START_ENTER_DURATION_OFFSET) / (BALL_FORWARD_END_ENTER_DURATION_OFFSET - BALL_FORWARD_START_ENTER_DURATION_OFFSET);
160 | 
161 |             mShapeChangeHeight = (0.5f - ballForwardEnterProgress) * mDanceBallRadius / 2.0f;
162 |             mShapeChangeWidth = -mShapeChangeHeight;
163 |             //y = k(x - r)--> k = tan(angle)
164 |             //(x - r)^2 + y^2 = r^2
165 |             // compute crossover point --> (k(x -r)) ^ 2 + (x - )^2 = r^2
166 |             // so x --> [r + r / sqrt(k ^ 2 + 1), r - r / sqrt(k ^ 2 + 1)]
167 |             for (int i = 0; i < NUM_POINTS; i++) {
168 |                 float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
169 |                 // progress[-1, 1]
170 |                 float progress = (ACCELERATE_INTERPOLATOR.getInterpolation(ballForwardEnterProgress) / 2.0f - 0.5f) * 2.0f * DIRECTION[i];
171 |                 POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
172 |                 POINT_Y[i] = k * (POINT_X[i] - radius);
173 | 
174 |                 POINT_X[i] += originCoordinateX;
175 |                 POINT_Y[i] += originCoordinateY;
176 |             }
177 |         }
178 | 
179 |         if (renderProgress <= RING_FORWARD_END_ROTATE_DURATION_OFFSET && renderProgress > RING_FORWARD_START_ROTATE_DURATION_OFFSET) {
180 |             final float forwardRotateProgress = (renderProgress - RING_FORWARD_START_ROTATE_DURATION_OFFSET) / (RING_FORWARD_END_ROTATE_DURATION_OFFSET - RING_FORWARD_START_ROTATE_DURATION_OFFSET);
181 |             mRotation = DEGREE_360 * MATERIAL_INTERPOLATOR.getInterpolation(forwardRotateProgress);
182 |         }
183 | 
184 |         if (renderProgress <= CENTER_CIRCLE_FORWARD_END_SCALE_DURATION_OFFSET && renderProgress > CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET) {
185 |             final float centerCircleScaleProgress = (renderProgress - CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET) / (CENTER_CIRCLE_FORWARD_END_SCALE_DURATION_OFFSET - CENTER_CIRCLE_FORWARD_START_SCALE_DURATION_OFFSET);
186 | 
187 |             if (centerCircleScaleProgress <= 0.5f) {
188 |                 mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(centerCircleScaleProgress * 2.0f) * 0.2f;
189 |             } else {
190 |                 mScale = 1.2f - ACCELERATE_INTERPOLATOR.getInterpolation((centerCircleScaleProgress - 0.5f) * 2.0f) * 0.2f;
191 |             }
192 | 
193 |         }
194 | 
195 |         if (renderProgress <= BALL_FORWARD_END_EXIT_DURATION_OFFSET && renderProgress > BALL_FORWARD_START_EXIT_DURATION_OFFSET) {
196 |             final float ballForwardExitProgress = (renderProgress - BALL_FORWARD_START_EXIT_DURATION_OFFSET) / (BALL_FORWARD_END_EXIT_DURATION_OFFSET - BALL_FORWARD_START_EXIT_DURATION_OFFSET);
197 |             mShapeChangeHeight = (ballForwardExitProgress - 0.5f) * mDanceBallRadius / 2.0f;
198 |             mShapeChangeWidth = -mShapeChangeHeight;
199 |             for (int i = 0; i < NUM_POINTS; i++) {
200 |                 float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
201 |                 float progress = (DECELERATE_INTERPOLATOR.getInterpolation(ballForwardExitProgress) / 2.0f) * 2.0f * DIRECTION[i];
202 |                 POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
203 |                 POINT_Y[i] = k * (POINT_X[i] - radius);
204 | 
205 |                 POINT_X[i] += originCoordinateX;
206 |                 POINT_Y[i] += originCoordinateY;
207 |             }
208 |         }
209 | 
210 |         if (renderProgress <= RING_REVERSAL_END_ROTATE_DURATION_OFFSET && renderProgress > RING_REVERSAL_START_ROTATE_DURATION_OFFSET) {
211 |             float scaledTime = (renderProgress - RING_REVERSAL_START_ROTATE_DURATION_OFFSET) / (RING_REVERSAL_END_ROTATE_DURATION_OFFSET - RING_REVERSAL_START_ROTATE_DURATION_OFFSET);
212 |             mRotation = DEGREE_360 * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime) - 360;
213 |         } else if (renderProgress > RING_REVERSAL_END_ROTATE_DURATION_OFFSET) {
214 |             mRotation = 0.0f;
215 |         }
216 | 
217 |         if (renderProgress <= BALL_REVERSAL_END_ENTER_DURATION_OFFSET && renderProgress > BALL_REVERSAL_START_ENTER_DURATION_OFFSET) {
218 |             final float ballReversalEnterProgress = (renderProgress - BALL_REVERSAL_START_ENTER_DURATION_OFFSET) / (BALL_REVERSAL_END_ENTER_DURATION_OFFSET - BALL_REVERSAL_START_ENTER_DURATION_OFFSET);
219 |             mShapeChangeHeight = (0.5f - ballReversalEnterProgress) * mDanceBallRadius / 2.0f;
220 |             mShapeChangeWidth = -mShapeChangeHeight;
221 | 
222 |             for (int i = 0; i < NUM_POINTS; i++) {
223 |                 float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
224 |                 float progress = (0.5f - ACCELERATE_INTERPOLATOR.getInterpolation(ballReversalEnterProgress) / 2.0f) * 2.0f * DIRECTION[i];
225 |                 POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
226 |                 POINT_Y[i] = k * (POINT_X[i] - radius);
227 | 
228 |                 POINT_X[i] += originCoordinateX;
229 |                 POINT_Y[i] += originCoordinateY;
230 |             }
231 |         }
232 | 
233 |         if (renderProgress <= CENTER_CIRCLE_REVERSAL_END_SCALE_DURATION_OFFSET && renderProgress > CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET) {
234 |             final float centerCircleScaleProgress = (renderProgress - CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET) / (CENTER_CIRCLE_REVERSAL_END_SCALE_DURATION_OFFSET - CENTER_CIRCLE_REVERSAL_START_SCALE_DURATION_OFFSET);
235 | 
236 |             if (centerCircleScaleProgress <= 0.5f) {
237 |                 mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(centerCircleScaleProgress * 2.0f) * 0.2f;
238 |             } else {
239 |                 mScale = 1.2f - ACCELERATE_INTERPOLATOR.getInterpolation((centerCircleScaleProgress - 0.5f) * 2.0f) * 0.2f;
240 |             }
241 | 
242 |         }
243 | 
244 |         if (renderProgress <= BALL_REVERSAL_END_EXIT_DURATION_OFFSET && renderProgress > BALL_REVERSAL_START_EXIT_DURATION_OFFSET) {
245 |             final float ballReversalExitProgress = (renderProgress - BALL_REVERSAL_START_EXIT_DURATION_OFFSET) / (BALL_REVERSAL_END_EXIT_DURATION_OFFSET - BALL_REVERSAL_START_EXIT_DURATION_OFFSET);
246 |             mShapeChangeHeight = (ballReversalExitProgress - 0.5f) * mDanceBallRadius / 2.0f;
247 |             mShapeChangeWidth = -mShapeChangeHeight;
248 | 
249 |             for (int i = 0; i < NUM_POINTS; i++) {
250 |                 float k = (float) Math.tan((DANCE_START_ANGLE + DANCE_INTERVAL_ANGLE * i) / 360.0f * (2.0f * Math.PI));
251 |                 float progress = (0.0f - DECELERATE_INTERPOLATOR.getInterpolation(ballReversalExitProgress) / 2.0f) * 2.0f * DIRECTION[i];
252 |                 POINT_X[i] = (float) (radius + progress * (radius / Math.sqrt(Math.pow(k, 2.0f) + 1.0f)));
253 |                 POINT_Y[i] = k * (POINT_X[i] - radius);
254 | 
255 |                 POINT_X[i] += originCoordinateX;
256 |                 POINT_Y[i] += originCoordinateY;
257 |             }
258 |         }
259 |     }
260 | 
261 |     @Override
262 |     protected void setAlpha(int alpha) {
263 |         mPaint.setAlpha(alpha);
264 | 
265 |     }
266 | 
267 |     @Override
268 |     protected void setColorFilter(ColorFilter cf) {
269 |         mPaint.setColorFilter(cf);
270 | 
271 |     }
272 | 
273 |     @Override
274 |     protected void reset() {
275 |         mScale = 1.0f;
276 |         mRotation = 0;
277 |     }
278 | 
279 |     private void setColor(int color) {
280 |         mColor = color;
281 |         mArcColor = halfAlphaColor(mColor);
282 |     }
283 | 
284 |     private void setRotation(float rotation) {
285 |         mRotation = rotation;
286 | 
287 |     }
288 | 
289 |     private void setDanceBallRadius(float danceBallRadius) {
290 |         this.mDanceBallRadius = danceBallRadius;
291 | 
292 |     }
293 | 
294 |     private float getDanceBallRadius() {
295 |         return mDanceBallRadius;
296 |     }
297 | 
298 |     private float getRotation() {
299 |         return mRotation;
300 |     }
301 | 
302 |     private void setInsets(int width, int height) {
303 |         final float minEdge = (float) Math.min(width, height);
304 |         float insets;
305 |         if (mCenterRadius <= 0 || minEdge < 0) {
306 |             insets = (float) Math.ceil(mStrokeWidth / 2.0f);
307 |         } else {
308 |             insets = minEdge / 2.0f - mCenterRadius;
309 |         }
310 |         mStrokeInset = insets;
311 |     }
312 | 
313 |     private int halfAlphaColor(int colorValue) {
314 |         int startA = (colorValue >> 24) & 0xff;
315 |         int startR = (colorValue >> 16) & 0xff;
316 |         int startG = (colorValue >> 8) & 0xff;
317 |         int startB = colorValue & 0xff;
318 | 
319 |         return ((startA / 2) << 24)
320 |                 | (startR << 16)
321 |                 | (startG << 8)
322 |                 | startB;
323 |     }
324 | 
325 |     public static class Builder {
326 |         private Context mContext;
327 | 
328 |         public Builder(Context mContext) {
329 |             this.mContext = mContext;
330 |         }
331 | 
332 |         public DanceLoadingRenderer build() {
333 |             DanceLoadingRenderer loadingRenderer = new DanceLoadingRenderer(mContext);
334 |             return loadingRenderer;
335 |         }
336 |     }
337 | }
338 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/jump/GuardLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.jump;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Path;
  9 | import android.graphics.PathMeasure;
 10 | import android.graphics.Rect;
 11 | import android.graphics.RectF;
 12 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
 13 | import android.util.DisplayMetrics;
 14 | import android.view.animation.AccelerateInterpolator;
 15 | import android.view.animation.DecelerateInterpolator;
 16 | import android.view.animation.Interpolator;
 17 | 
 18 | import app.dinus.com.loadingdrawable.DensityUtil;
 19 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 20 | 
 21 | public class GuardLoadingRenderer extends LoadingRenderer {
 22 |     private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
 23 |     private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
 24 |     private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
 25 | 
 26 |     private static final long ANIMATION_DURATION = 5000;
 27 | 
 28 |     private static final float DEFAULT_STROKE_WIDTH = 1.0f;
 29 |     private static final float DEFAULT_CENTER_RADIUS = 12.5f;
 30 |     private static final float DEFAULT_SKIP_BALL_RADIUS = 1.0f;
 31 | 
 32 |     private static final float START_TRIM_INIT_ROTATION = -0.5f;
 33 |     private static final float START_TRIM_MAX_ROTATION = -0.25f;
 34 |     private static final float END_TRIM_INIT_ROTATION = 0.25f;
 35 |     private static final float END_TRIM_MAX_ROTATION = 0.75f;
 36 | 
 37 |     private static final float START_TRIM_DURATION_OFFSET = 0.23f;
 38 |     private static final float WAVE_DURATION_OFFSET = 0.36f;
 39 |     private static final float BALL_SKIP_DURATION_OFFSET = 0.74f;
 40 |     private static final float BALL_SCALE_DURATION_OFFSET = 0.82f;
 41 |     private static final float END_TRIM_DURATION_OFFSET = 1.0f;
 42 | 
 43 |     private static final int DEFAULT_COLOR = Color.WHITE;
 44 |     private static final int DEFAULT_BALL_COLOR = Color.RED;
 45 | 
 46 |     private final Paint mPaint = new Paint();
 47 |     private final RectF mTempBounds = new RectF();
 48 |     private final RectF mCurrentBounds = new RectF();
 49 |     private final float[] mCurrentPosition = new float[2];
 50 | 
 51 |     private float mStrokeInset;
 52 |     private float mSkipBallSize;
 53 | 
 54 |     private float mScale;
 55 |     private float mEndTrim;
 56 |     private float mRotation;
 57 |     private float mStartTrim;
 58 |     private float mWaveProgress;
 59 | 
 60 |     private float mStrokeWidth;
 61 |     private float mCenterRadius;
 62 | 
 63 |     private int mColor;
 64 |     private int mBallColor;
 65 | 
 66 |     private PathMeasure mPathMeasure;
 67 | 
 68 |     private GuardLoadingRenderer(Context context) {
 69 |         super(context);
 70 | 
 71 |         mDuration = ANIMATION_DURATION;
 72 |         init(context);
 73 |         setupPaint();
 74 |     }
 75 | 
 76 |     private void init(Context context) {
 77 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 78 |         mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
 79 |         mSkipBallSize = DensityUtil.dip2px(context, DEFAULT_SKIP_BALL_RADIUS);
 80 | 
 81 |         mColor = DEFAULT_COLOR;
 82 |         mBallColor = DEFAULT_BALL_COLOR;
 83 |     }
 84 | 
 85 |     private void setupPaint() {
 86 |         mPaint.setAntiAlias(true);
 87 |         mPaint.setStrokeWidth(mStrokeWidth);
 88 |         mPaint.setStyle(Paint.Style.STROKE);
 89 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
 90 | 
 91 |         setInsets((int) mWidth, (int) mHeight);
 92 |     }
 93 | 
 94 |     @Override
 95 |     protected void draw(Canvas canvas, Rect bounds) {
 96 |         RectF arcBounds = mTempBounds;
 97 |         arcBounds.set(bounds);
 98 |         arcBounds.inset(mStrokeInset, mStrokeInset);
 99 |         mCurrentBounds.set(arcBounds);
100 | 
101 |         int saveCount = canvas.save();
102 | 
103 |         //draw circle trim
104 |         float startAngle = (mStartTrim + mRotation) * 360;
105 |         float endAngle = (mEndTrim + mRotation) * 360;
106 |         float sweepAngle = endAngle - startAngle;
107 |         if (sweepAngle != 0) {
108 |             mPaint.setColor(mColor);
109 |             mPaint.setStyle(Paint.Style.STROKE);
110 |             canvas.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
111 |         }
112 | 
113 |         //draw water wave
114 |         if (mWaveProgress < 1.0f) {
115 |             mPaint.setColor(Color.argb((int) (Color.alpha(mColor) * (1.0f - mWaveProgress)),
116 |                     Color.red(mColor), Color.green(mColor), Color.blue(mColor)));
117 |             mPaint.setStyle(Paint.Style.STROKE);
118 |             float radius = Math.min(arcBounds.width(), arcBounds.height()) / 2.0f;
119 |             canvas.drawCircle(arcBounds.centerX(), arcBounds.centerY(), radius * (1.0f + mWaveProgress), mPaint);
120 |         }
121 |         //draw ball bounce
122 |         if (mPathMeasure != null) {
123 |             mPaint.setColor(mBallColor);
124 |             mPaint.setStyle(Paint.Style.FILL);
125 |             canvas.drawCircle(mCurrentPosition[0], mCurrentPosition[1], mSkipBallSize * mScale, mPaint);
126 |         }
127 | 
128 |         canvas.restoreToCount(saveCount);
129 |     }
130 | 
131 |     @Override
132 |     protected void computeRender(float renderProgress) {
133 |         if (renderProgress <= START_TRIM_DURATION_OFFSET) {
134 |             final float startTrimProgress = (renderProgress) / START_TRIM_DURATION_OFFSET;
135 |             mEndTrim = -MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
136 |             mRotation = START_TRIM_INIT_ROTATION + START_TRIM_MAX_ROTATION
137 |                     * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
138 |         }
139 | 
140 |         if (renderProgress <= WAVE_DURATION_OFFSET && renderProgress > START_TRIM_DURATION_OFFSET) {
141 |             final float waveProgress = (renderProgress - START_TRIM_DURATION_OFFSET)
142 |                     / (WAVE_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
143 |             mWaveProgress = ACCELERATE_INTERPOLATOR.getInterpolation(waveProgress);
144 |         }
145 | 
146 |         if (renderProgress <= BALL_SKIP_DURATION_OFFSET && renderProgress > WAVE_DURATION_OFFSET) {
147 |             if (mPathMeasure == null) {
148 |                 mPathMeasure = new PathMeasure(createSkipBallPath(), false);
149 |             }
150 | 
151 |             final float ballSkipProgress = (renderProgress - WAVE_DURATION_OFFSET)
152 |                     / (BALL_SKIP_DURATION_OFFSET - WAVE_DURATION_OFFSET);
153 |             mPathMeasure.getPosTan(ballSkipProgress * mPathMeasure.getLength(), mCurrentPosition, null);
154 | 
155 |             mWaveProgress = 1.0f;
156 |         }
157 | 
158 |         if (renderProgress <= BALL_SCALE_DURATION_OFFSET && renderProgress > BALL_SKIP_DURATION_OFFSET) {
159 |             final float ballScaleProgress =
160 |                     (renderProgress - BALL_SKIP_DURATION_OFFSET)
161 |                             / (BALL_SCALE_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);
162 |             if (ballScaleProgress < 0.5f) {
163 |                 mScale = 1.0f + DECELERATE_INTERPOLATOR.getInterpolation(ballScaleProgress * 2.0f);
164 |             } else {
165 |                 mScale = 2.0f - ACCELERATE_INTERPOLATOR.getInterpolation((ballScaleProgress - 0.5f) * 2.0f) * 2.0f;
166 |             }
167 |         }
168 | 
169 |         if (renderProgress >= BALL_SCALE_DURATION_OFFSET) {
170 |             final float endTrimProgress =
171 |                     (renderProgress - BALL_SKIP_DURATION_OFFSET)
172 |                             / (END_TRIM_DURATION_OFFSET - BALL_SKIP_DURATION_OFFSET);
173 |             mEndTrim = -1 + MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
174 |             mRotation = END_TRIM_INIT_ROTATION + END_TRIM_MAX_ROTATION
175 |                     * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
176 | 
177 |             mScale = 1.0f;
178 |             mPathMeasure = null;
179 |         }
180 |     }
181 | 
182 |     @Override
183 |     protected void setAlpha(int alpha) {
184 |         mPaint.setAlpha(alpha);
185 | 
186 |     }
187 | 
188 |     @Override
189 |     protected void setColorFilter(ColorFilter cf) {
190 |         mPaint.setColorFilter(cf);
191 | 
192 |     }
193 | 
194 |     @Override
195 |     protected void reset() {
196 |         mScale = 1.0f;
197 |         mEndTrim = 0.0f;
198 |         mRotation = 0.0f;
199 |         mStartTrim = 0.0f;
200 |         mWaveProgress = 1.0f;
201 |     }
202 | 
203 |     private Path createSkipBallPath() {
204 |         float radius = Math.min(mCurrentBounds.width(), mCurrentBounds.height()) / 2.0f;
205 |         float radiusPow2 = (float) Math.pow(radius, 2.0f);
206 |         float originCoordinateX = mCurrentBounds.centerX();
207 |         float originCoordinateY = mCurrentBounds.centerY();
208 | 
209 |         float[] coordinateX = new float[]{0.0f, 0.0f, -0.8f * radius, 0.75f * radius,
210 |                 -0.45f * radius, 0.9f * radius, -0.5f * radius};
211 |         float[] sign = new float[]{1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f};
212 | 
213 |         Path path = new Path();
214 |         for (int i = 0; i < coordinateX.length; i++) {
215 |             // x^2 + y^2 = radius^2 --> y = sqrt(radius^2 - x^2)
216 |             if (i == 0) {
217 |                 path.moveTo(
218 |                         originCoordinateX + coordinateX[i],
219 |                         originCoordinateY + sign[i]
220 |                                 * (float) Math.sqrt(radiusPow2 - Math.pow(coordinateX[i], 2.0f)));
221 |                 continue;
222 |             }
223 | 
224 |             path.lineTo(
225 |                     originCoordinateX + coordinateX[i],
226 |                     originCoordinateY + sign[i]
227 |                             * (float) Math.sqrt(radiusPow2 - Math.pow(coordinateX[i], 2.0f)));
228 | 
229 |             if (i == coordinateX.length - 1) {
230 |                 path.lineTo(originCoordinateX, originCoordinateY);
231 |             }
232 |         }
233 |         return path;
234 |     }
235 | 
236 |     private void setInsets(int width, int height) {
237 |         final float minEdge = (float) Math.min(width, height);
238 |         float insets;
239 |         if (mCenterRadius <= 0 || minEdge < 0) {
240 |             insets = (float) Math.ceil(mStrokeWidth / 2.0f);
241 |         } else {
242 |             insets = minEdge / 2.0f - mCenterRadius;
243 |         }
244 |         mStrokeInset = insets;
245 |     }
246 | 
247 |     public static class Builder {
248 |         private Context mContext;
249 | 
250 |         public Builder(Context mContext) {
251 |             this.mContext = mContext;
252 |         }
253 | 
254 |         public GuardLoadingRenderer build() {
255 |             GuardLoadingRenderer loadingRenderer = new GuardLoadingRenderer(mContext);
256 |             return loadingRenderer;
257 |         }
258 |     }
259 | }
260 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/jump/SwapLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.jump;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.view.animation.AccelerateDecelerateInterpolator;
  9 | import android.view.animation.Interpolator;
 10 | 
 11 | import app.dinus.com.loadingdrawable.DensityUtil;
 12 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 13 | 
 14 | public class SwapLoadingRenderer extends LoadingRenderer {
 15 |     private static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
 16 | 
 17 |     private static final long ANIMATION_DURATION = 2500;
 18 | 
 19 |     private static final int DEFAULT_CIRCLE_COUNT = 5;
 20 | 
 21 |     private static final float DEFAULT_BALL_RADIUS = 7.5f;
 22 |     private static final float DEFAULT_WIDTH = 15.0f * 11;
 23 |     private static final float DEFAULT_HEIGHT = 15.0f * 5;
 24 |     private static final float DEFAULT_STROKE_WIDTH = 1.5f;
 25 | 
 26 |     private static final int DEFAULT_COLOR = Color.WHITE;
 27 | 
 28 |     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 29 | 
 30 |     private int mColor;
 31 | 
 32 |     private int mSwapIndex;
 33 |     private int mBallCount;
 34 | 
 35 |     private float mBallSideOffsets;
 36 |     private float mBallCenterY;
 37 |     private float mBallRadius;
 38 |     private float mBallInterval;
 39 |     private float mSwapBallOffsetX;
 40 |     private float mSwapBallOffsetY;
 41 |     private float mASwapThreshold;
 42 | 
 43 |     private float mStrokeWidth;
 44 | 
 45 |     private SwapLoadingRenderer(Context context) {
 46 |         super(context);
 47 | 
 48 |         init(context);
 49 |         adjustParams();
 50 |         setupPaint();
 51 |     }
 52 | 
 53 |     private void init(Context context) {
 54 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 55 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 56 |         mBallRadius = DensityUtil.dip2px(context, DEFAULT_BALL_RADIUS);
 57 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 58 | 
 59 |         mColor = DEFAULT_COLOR;
 60 |         mDuration = ANIMATION_DURATION;
 61 |         mBallCount = DEFAULT_CIRCLE_COUNT;
 62 | 
 63 |         mBallInterval = mBallRadius;
 64 |     }
 65 | 
 66 |     private void adjustParams() {
 67 |         mBallCenterY = mHeight / 2.0f;
 68 |         mBallSideOffsets = (mWidth - mBallRadius * 2 * mBallCount - mBallInterval * (mBallCount - 1)) / 2.0f;
 69 | 
 70 |         mASwapThreshold = 1.0f / mBallCount;
 71 |     }
 72 | 
 73 |     private void setupPaint() {
 74 |         mPaint.setColor(mColor);
 75 |         mPaint.setStrokeWidth(mStrokeWidth);
 76 |     }
 77 | 
 78 |     @Override
 79 |     protected void draw(Canvas canvas) {
 80 |         int saveCount = canvas.save();
 81 | 
 82 |         for (int i = 0; i < mBallCount; i++) {
 83 |             if (i == mSwapIndex) {
 84 |                 mPaint.setStyle(Paint.Style.FILL);
 85 |                 canvas.drawCircle(mBallSideOffsets + mBallRadius * (i * 2 + 1) + i * mBallInterval + mSwapBallOffsetX
 86 |                         , mBallCenterY - mSwapBallOffsetY, mBallRadius, mPaint);
 87 |             } else if (i == (mSwapIndex + 1) % mBallCount) {
 88 |                 mPaint.setStyle(Paint.Style.STROKE);
 89 |                 canvas.drawCircle(mBallSideOffsets + mBallRadius * (i * 2 + 1) + i * mBallInterval - mSwapBallOffsetX
 90 |                         , mBallCenterY + mSwapBallOffsetY, mBallRadius - mStrokeWidth / 2, mPaint);
 91 |             } else {
 92 |                 mPaint.setStyle(Paint.Style.STROKE);
 93 |                 canvas.drawCircle(mBallSideOffsets + mBallRadius * (i * 2 + 1) + i * mBallInterval, mBallCenterY
 94 |                         , mBallRadius - mStrokeWidth / 2, mPaint);
 95 |             }
 96 |         }
 97 | 
 98 |         canvas.restoreToCount(saveCount);
 99 |     }
100 | 
101 |     @Override
102 |     protected void computeRender(float renderProgress) {
103 |         mSwapIndex = (int) (renderProgress / mASwapThreshold);
104 | 
105 |         // Swap trace : x^2 + y^2 = r ^ 2
106 |         float swapTraceProgress = ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation(
107 |                 (renderProgress - mSwapIndex * mASwapThreshold) / mASwapThreshold);
108 | 
109 |         float swapTraceRadius = mSwapIndex == mBallCount - 1
110 |                 ? (mBallRadius * 2 * (mBallCount - 1) + mBallInterval * (mBallCount - 1)) / 2
111 |                 : (mBallRadius * 2 + mBallInterval) / 2;
112 | 
113 |         // Calculate the X offset of the swap ball
114 |         mSwapBallOffsetX = mSwapIndex == mBallCount - 1
115 |                 ? -swapTraceProgress * swapTraceRadius * 2
116 |                 : swapTraceProgress * swapTraceRadius * 2;
117 | 
118 |         // if mSwapIndex == mBallCount - 1 then (swapTraceRadius, swapTraceRadius) as the origin of coordinates
119 |         // else (-swapTraceRadius, -swapTraceRadius) as the origin of coordinates
120 |         float xCoordinate = mSwapIndex == mBallCount - 1
121 |                 ? mSwapBallOffsetX + swapTraceRadius
122 |                 : mSwapBallOffsetX - swapTraceRadius;
123 | 
124 |         // Calculate the Y offset of the swap ball
125 |         mSwapBallOffsetY = (float) (mSwapIndex % 2 == 0 && mSwapIndex != mBallCount - 1
126 |                 ? Math.sqrt(Math.pow(swapTraceRadius, 2.0f) - Math.pow(xCoordinate, 2.0f))
127 |                 : -Math.sqrt(Math.pow(swapTraceRadius, 2.0f) - Math.pow(xCoordinate, 2.0f)));
128 | 
129 |     }
130 | 
131 |     @Override
132 |     protected void setAlpha(int alpha) {
133 |         mPaint.setAlpha(alpha);
134 |     }
135 | 
136 |     @Override
137 |     protected void setColorFilter(ColorFilter cf) {
138 |         mPaint.setColorFilter(cf);
139 |     }
140 | 
141 |     @Override
142 |     protected void reset() {
143 |     }
144 | 
145 |     private void apply(Builder builder) {
146 |         this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
147 |         this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
148 |         this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
149 | 
150 |         this.mBallRadius = builder.mBallRadius > 0 ? builder.mBallRadius : this.mBallRadius;
151 |         this.mBallInterval = builder.mBallInterval > 0 ? builder.mBallInterval : this.mBallInterval;
152 |         this.mBallCount = builder.mBallCount > 0 ? builder.mBallCount : this.mBallCount;
153 | 
154 |         this.mColor = builder.mColor != 0 ? builder.mColor : this.mColor;
155 | 
156 |         this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
157 | 
158 |         adjustParams();
159 |         setupPaint();
160 |     }
161 | 
162 |     public static class Builder {
163 |         private Context mContext;
164 | 
165 |         private int mWidth;
166 |         private int mHeight;
167 |         private int mStrokeWidth;
168 | 
169 |         private int mBallCount;
170 |         private int mBallRadius;
171 |         private int mBallInterval;
172 | 
173 |         private int mDuration;
174 | 
175 |         private int mColor;
176 | 
177 |         public Builder(Context mContext) {
178 |             this.mContext = mContext;
179 |         }
180 | 
181 |         public Builder setWidth(int width) {
182 |             this.mWidth = width;
183 |             return this;
184 |         }
185 | 
186 |         public Builder setHeight(int height) {
187 |             this.mHeight = height;
188 |             return this;
189 |         }
190 | 
191 |         public Builder setStrokeWidth(int strokeWidth) {
192 |             this.mStrokeWidth = strokeWidth;
193 |             return this;
194 |         }
195 | 
196 |         public Builder setBallRadius(int ballRadius) {
197 |             this.mBallRadius = ballRadius;
198 |             return this;
199 |         }
200 | 
201 |         public Builder setBallInterval(int ballInterval) {
202 |             this.mBallInterval = ballInterval;
203 |             return this;
204 |         }
205 | 
206 |         public Builder setBallCount(int ballCount) {
207 |             this.mBallCount = ballCount;
208 |             return this;
209 |         }
210 | 
211 |         public Builder setColor(int color) {
212 |             this.mColor = color;
213 |             return this;
214 |         }
215 | 
216 |         public Builder setDuration(int duration) {
217 |             this.mDuration = duration;
218 |             return this;
219 |         }
220 | 
221 |         public SwapLoadingRenderer build() {
222 |             SwapLoadingRenderer loadingRenderer = new SwapLoadingRenderer(mContext);
223 |             loadingRenderer.apply(this);
224 |             return loadingRenderer;
225 |         }
226 |     }
227 | }
228 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/rotate/GearLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.rotate;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.AnimatorListenerAdapter;
  5 | import android.content.Context;
  6 | import android.graphics.Canvas;
  7 | import android.graphics.Color;
  8 | import android.graphics.ColorFilter;
  9 | import android.graphics.Paint;
 10 | import android.graphics.RectF;
 11 | import android.support.annotation.IntRange;
 12 | import android.view.animation.AccelerateInterpolator;
 13 | import android.view.animation.DecelerateInterpolator;
 14 | import android.view.animation.Interpolator;
 15 | 
 16 | import app.dinus.com.loadingdrawable.DensityUtil;
 17 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 18 | 
 19 | public class GearLoadingRenderer extends LoadingRenderer {
 20 |     private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
 21 |     private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
 22 | 
 23 |     private static final int GEAR_COUNT = 4;
 24 |     private static final int NUM_POINTS = 3;
 25 |     private static final int MAX_ALPHA = 255;
 26 |     private static final int DEGREE_360 = 360;
 27 | 
 28 |     private static final int DEFAULT_GEAR_SWIPE_DEGREES = 60;
 29 | 
 30 |     private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
 31 | 
 32 |     private static final float START_SCALE_DURATION_OFFSET = 0.3f;
 33 |     private static final float START_TRIM_DURATION_OFFSET = 0.5f;
 34 |     private static final float END_TRIM_DURATION_OFFSET = 0.7f;
 35 |     private static final float END_SCALE_DURATION_OFFSET = 1.0f;
 36 | 
 37 |     private static final float DEFAULT_CENTER_RADIUS = 12.5f;
 38 |     private static final float DEFAULT_STROKE_WIDTH = 2.5f;
 39 | 
 40 |     private static final int DEFAULT_COLOR = Color.WHITE;
 41 | 
 42 |     private final Paint mPaint = new Paint();
 43 |     private final RectF mTempBounds = new RectF();
 44 | 
 45 |     private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
 46 |         @Override
 47 |         public void onAnimationRepeat(Animator animator) {
 48 |             super.onAnimationRepeat(animator);
 49 |             storeOriginals();
 50 | 
 51 |             mStartDegrees = mEndDegrees;
 52 |             mRotationCount = (mRotationCount + 1) % NUM_POINTS;
 53 |         }
 54 | 
 55 |         @Override
 56 |         public void onAnimationStart(Animator animation) {
 57 |             super.onAnimationStart(animation);
 58 |             mRotationCount = 0;
 59 |         }
 60 |     };
 61 | 
 62 |     private int mColor;
 63 | 
 64 |     private int mGearCount;
 65 |     private int mGearSwipeDegrees;
 66 | 
 67 |     private float mStrokeInset;
 68 | 
 69 |     private float mRotationCount;
 70 |     private float mGroupRotation;
 71 | 
 72 |     private float mScale;
 73 |     private float mEndDegrees;
 74 |     private float mStartDegrees;
 75 |     private float mSwipeDegrees;
 76 |     private float mOriginEndDegrees;
 77 |     private float mOriginStartDegrees;
 78 | 
 79 |     private float mStrokeWidth;
 80 |     private float mCenterRadius;
 81 | 
 82 |     private GearLoadingRenderer(Context context) {
 83 |         super(context);
 84 | 
 85 |         init(context);
 86 |         setupPaint();
 87 |         addRenderListener(mAnimatorListener);
 88 |     }
 89 | 
 90 |     private void init(Context context) {
 91 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 92 |         mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
 93 | 
 94 |         mColor = DEFAULT_COLOR;
 95 | 
 96 |         mGearCount = GEAR_COUNT;
 97 |         mGearSwipeDegrees = DEFAULT_GEAR_SWIPE_DEGREES;
 98 |     }
 99 | 
100 |     private void setupPaint() {
101 |         mPaint.setAntiAlias(true);
102 |         mPaint.setStrokeWidth(mStrokeWidth);
103 |         mPaint.setStyle(Paint.Style.STROKE);
104 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
105 | 
106 |         initStrokeInset(mWidth, mHeight);
107 |     }
108 | 
109 |     @Override
110 |     protected void draw(Canvas canvas) {
111 |         int saveCount = canvas.save();
112 | 
113 |         mTempBounds.set(mBounds);
114 |         mTempBounds.inset(mStrokeInset, mStrokeInset);
115 |         mTempBounds.inset(mTempBounds.width() * (1.0f - mScale) / 2.0f, mTempBounds.width() * (1.0f - mScale) / 2.0f);
116 | 
117 |         canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
118 | 
119 |         mPaint.setColor(mColor);
120 |         mPaint.setAlpha((int) (MAX_ALPHA * mScale));
121 |         mPaint.setStrokeWidth(mStrokeWidth * mScale);
122 | 
123 |         if (mSwipeDegrees != 0) {
124 |             for (int i = 0; i < mGearCount; i++) {
125 |                 canvas.drawArc(mTempBounds, mStartDegrees + DEGREE_360 / mGearCount * i, mSwipeDegrees, false, mPaint);
126 |             }
127 |         }
128 | 
129 |         canvas.restoreToCount(saveCount);
130 |     }
131 | 
132 |     @Override
133 |     protected void computeRender(float renderProgress) {
134 |         // Scaling up the start size only occurs in the first 20% of a single ring animation
135 |         if (renderProgress <= START_SCALE_DURATION_OFFSET) {
136 |             float startScaleProgress = (renderProgress) / START_SCALE_DURATION_OFFSET;
137 |             mScale = DECELERATE_INTERPOLATOR.getInterpolation(startScaleProgress);
138 |         }
139 | 
140 |         // Moving the start trim only occurs between 20% to 50% of a single ring animation
141 |         if (renderProgress <= START_TRIM_DURATION_OFFSET && renderProgress > START_SCALE_DURATION_OFFSET) {
142 |             float startTrimProgress = (renderProgress - START_SCALE_DURATION_OFFSET) / (START_TRIM_DURATION_OFFSET - START_SCALE_DURATION_OFFSET);
143 |             mStartDegrees = mOriginStartDegrees + mGearSwipeDegrees * startTrimProgress;
144 |         }
145 | 
146 |         // Moving the end trim starts between 50% to 80% of a single ring animation
147 |         if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > START_TRIM_DURATION_OFFSET) {
148 |             float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
149 |             mEndDegrees = mOriginEndDegrees + mGearSwipeDegrees * endTrimProgress;
150 |         }
151 | 
152 |         // Scaling down the end size starts after 80% of a single ring animation
153 |         if (renderProgress > END_TRIM_DURATION_OFFSET) {
154 |             float endScaleProgress = (renderProgress - END_TRIM_DURATION_OFFSET) / (END_SCALE_DURATION_OFFSET - END_TRIM_DURATION_OFFSET);
155 |             mScale = 1.0f - ACCELERATE_INTERPOLATOR.getInterpolation(endScaleProgress);
156 |         }
157 | 
158 |         if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > START_SCALE_DURATION_OFFSET) {
159 |             float rotateProgress = (renderProgress - START_SCALE_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_SCALE_DURATION_OFFSET);
160 |             mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * rotateProgress) + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
161 |         }
162 | 
163 |         if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
164 |             mSwipeDegrees = mEndDegrees - mStartDegrees;
165 |         }
166 |     }
167 | 
168 |     @Override
169 |     protected void setAlpha(int alpha) {
170 |         mPaint.setAlpha(alpha);
171 |     }
172 | 
173 |     @Override
174 |     protected void setColorFilter(ColorFilter cf) {
175 |         mPaint.setColorFilter(cf);
176 |     }
177 | 
178 |     @Override
179 |     protected void reset() {
180 |         resetOriginals();
181 |     }
182 | 
183 |     private void initStrokeInset(float width, float height) {
184 |         float minSize = Math.min(width, height);
185 |         float strokeInset = minSize / 2.0f - mCenterRadius;
186 |         float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
187 |         mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
188 |     }
189 | 
190 |     private void storeOriginals() {
191 |         mOriginEndDegrees = mEndDegrees;
192 |         mOriginStartDegrees = mEndDegrees;
193 |     }
194 | 
195 |     private void resetOriginals() {
196 |         mOriginEndDegrees = 0;
197 |         mOriginStartDegrees = 0;
198 | 
199 |         mEndDegrees = 0;
200 |         mStartDegrees = 0;
201 | 
202 |         mSwipeDegrees = 1;
203 |     }
204 | 
205 |     private void apply(Builder builder) {
206 |         this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
207 |         this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
208 |         this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
209 |         this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
210 | 
211 |         this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
212 | 
213 |         this.mColor = builder.mColor != 0 ? builder.mColor : this.mColor;
214 | 
215 |         this.mGearCount = builder.mGearCount > 0 ? builder.mGearCount : this.mGearCount;
216 |         this.mGearSwipeDegrees = builder.mGearSwipeDegrees > 0 ? builder.mGearSwipeDegrees : this.mGearSwipeDegrees;
217 | 
218 |         setupPaint();
219 |         initStrokeInset(this.mWidth, this.mHeight);
220 |     }
221 | 
222 |     public static class Builder {
223 |         private Context mContext;
224 | 
225 |         private int mWidth;
226 |         private int mHeight;
227 |         private int mStrokeWidth;
228 |         private int mCenterRadius;
229 | 
230 |         private int mDuration;
231 | 
232 |         private int mColor;
233 | 
234 |         private int mGearCount;
235 |         private int mGearSwipeDegrees;
236 | 
237 |         public Builder(Context mContext) {
238 |             this.mContext = mContext;
239 |         }
240 | 
241 |         public Builder setWidth(int width) {
242 |             this.mWidth = width;
243 |             return this;
244 |         }
245 | 
246 |         public Builder setHeight(int height) {
247 |             this.mHeight = height;
248 |             return this;
249 |         }
250 | 
251 |         public Builder setStrokeWidth(int strokeWidth) {
252 |             this.mStrokeWidth = strokeWidth;
253 |             return this;
254 |         }
255 | 
256 |         public Builder setCenterRadius(int centerRadius) {
257 |             this.mCenterRadius = centerRadius;
258 |             return this;
259 |         }
260 | 
261 |         public Builder setDuration(int duration) {
262 |             this.mDuration = duration;
263 |             return this;
264 |         }
265 | 
266 |         public Builder setColor(int color) {
267 |             this.mColor = color;
268 |             return this;
269 |         }
270 | 
271 |         public Builder setGearCount(int gearCount) {
272 |             this.mGearCount = gearCount;
273 |             return this;
274 |         }
275 | 
276 |         public Builder setGearSwipeDegrees(@IntRange(from = 0, to = 360) int gearSwipeDegrees) {
277 |             this.mGearSwipeDegrees = gearSwipeDegrees;
278 |             return this;
279 |         }
280 | 
281 |         public GearLoadingRenderer build() {
282 |             GearLoadingRenderer loadingRenderer = new GearLoadingRenderer(mContext);
283 |             loadingRenderer.apply(this);
284 |             return loadingRenderer;
285 |         }
286 |     }
287 | }
288 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/rotate/LevelLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.rotate;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.AnimatorListenerAdapter;
  5 | import android.content.Context;
  6 | import android.graphics.Canvas;
  7 | import android.graphics.Color;
  8 | import android.graphics.ColorFilter;
  9 | import android.graphics.Paint;
 10 | import android.graphics.Rect;
 11 | import android.graphics.RectF;
 12 | import android.support.annotation.Size;
 13 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
 14 | import android.view.animation.AccelerateInterpolator;
 15 | import android.view.animation.DecelerateInterpolator;
 16 | import android.view.animation.Interpolator;
 17 | import android.view.animation.LinearInterpolator;
 18 | 
 19 | import app.dinus.com.loadingdrawable.DensityUtil;
 20 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 21 | 
 22 | public class LevelLoadingRenderer extends LoadingRenderer {
 23 |     private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
 24 |     private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
 25 |     private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
 26 |     private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
 27 | 
 28 |     private static final int NUM_POINTS = 5;
 29 |     private static final int DEGREE_360 = 360;
 30 | 
 31 |     private static final float MAX_SWIPE_DEGREES = 0.8f * DEGREE_360;
 32 |     private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
 33 | 
 34 |     private static final float[] LEVEL_SWEEP_ANGLE_OFFSETS = new float[]{1.0f, 7.0f / 8.0f, 5.0f / 8.0f};
 35 | 
 36 |     private static final float START_TRIM_DURATION_OFFSET = 0.5f;
 37 |     private static final float END_TRIM_DURATION_OFFSET = 1.0f;
 38 | 
 39 |     private static final float DEFAULT_CENTER_RADIUS = 12.5f;
 40 |     private static final float DEFAULT_STROKE_WIDTH = 2.5f;
 41 | 
 42 |     private static final int[] DEFAULT_LEVEL_COLORS = new int[]{Color.parseColor("#55ffffff"),
 43 |             Color.parseColor("#b1ffffff"), Color.parseColor("#ffffffff")};
 44 | 
 45 |     private final Paint mPaint = new Paint();
 46 |     private final RectF mTempBounds = new RectF();
 47 | 
 48 |     private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
 49 |         @Override
 50 |         public void onAnimationRepeat(Animator animator) {
 51 |             super.onAnimationRepeat(animator);
 52 |             storeOriginals();
 53 | 
 54 |             mStartDegrees = mEndDegrees;
 55 |             mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
 56 |         }
 57 | 
 58 |         @Override
 59 |         public void onAnimationStart(Animator animation) {
 60 |             super.onAnimationStart(animation);
 61 |             mRotationCount = 0;
 62 |         }
 63 |     };
 64 | 
 65 |     @Size(3)
 66 |     private int[] mLevelColors;
 67 |     @Size(3)
 68 |     private float[] mLevelSwipeDegrees;
 69 | 
 70 |     private float mStrokeInset;
 71 | 
 72 |     private float mRotationCount;
 73 |     private float mGroupRotation;
 74 | 
 75 |     private float mEndDegrees;
 76 |     private float mStartDegrees;
 77 |     private float mOriginEndDegrees;
 78 |     private float mOriginStartDegrees;
 79 | 
 80 |     private float mStrokeWidth;
 81 |     private float mCenterRadius;
 82 | 
 83 |     private LevelLoadingRenderer(Context context) {
 84 |         super(context);
 85 |         init(context);
 86 |         setupPaint();
 87 |         addRenderListener(mAnimatorListener);
 88 |     }
 89 | 
 90 |     private void init(Context context) {
 91 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 92 |         mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
 93 | 
 94 |         mLevelSwipeDegrees = new float[3];
 95 |         mLevelColors = DEFAULT_LEVEL_COLORS;
 96 |     }
 97 | 
 98 |     private void setupPaint() {
 99 | 
100 |         mPaint.setAntiAlias(true);
101 |         mPaint.setStrokeWidth(mStrokeWidth);
102 |         mPaint.setStyle(Paint.Style.STROKE);
103 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
104 | 
105 |         initStrokeInset((int) mWidth, (int) mHeight);
106 |     }
107 | 
108 |     @Override
109 |     protected void draw(Canvas canvas) {
110 |         int saveCount = canvas.save();
111 | 
112 |         mTempBounds.set(mBounds);
113 |         mTempBounds.inset(mStrokeInset, mStrokeInset);
114 |         canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
115 | 
116 |         for (int i = 0; i < 3; i++) {
117 |             if (mLevelSwipeDegrees[i] != 0) {
118 |                 mPaint.setColor(mLevelColors[i]);
119 |                 canvas.drawArc(mTempBounds, mEndDegrees, mLevelSwipeDegrees[i], false, mPaint);
120 |             }
121 |         }
122 | 
123 |         canvas.restoreToCount(saveCount);
124 |     }
125 | 
126 |     @Override
127 |     protected void computeRender(float renderProgress) {
128 |         // Moving the start trim only occurs in the first 50% of a single ring animation
129 |         if (renderProgress <= START_TRIM_DURATION_OFFSET) {
130 |             float startTrimProgress = (renderProgress) / START_TRIM_DURATION_OFFSET;
131 |             mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
132 | 
133 |             float mSwipeDegrees = mEndDegrees - mStartDegrees;
134 |             float levelSwipeDegreesProgress = Math.abs(mSwipeDegrees) / MAX_SWIPE_DEGREES;
135 | 
136 |             float level1Increment = DECELERATE_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress) - LINEAR_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress);
137 |             float level3Increment = ACCELERATE_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress) - LINEAR_INTERPOLATOR.getInterpolation(levelSwipeDegreesProgress);
138 | 
139 |             mLevelSwipeDegrees[0] = -mSwipeDegrees * LEVEL_SWEEP_ANGLE_OFFSETS[0] * (1.0f + level1Increment);
140 |             mLevelSwipeDegrees[1] = -mSwipeDegrees * LEVEL_SWEEP_ANGLE_OFFSETS[1] * 1.0f;
141 |             mLevelSwipeDegrees[2] = -mSwipeDegrees * LEVEL_SWEEP_ANGLE_OFFSETS[2] * (1.0f + level3Increment);
142 |         }
143 | 
144 |         // Moving the end trim starts after 50% of a single ring animation
145 |         if (renderProgress > START_TRIM_DURATION_OFFSET) {
146 |             float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
147 |             mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
148 | 
149 |             float mSwipeDegrees = mEndDegrees - mStartDegrees;
150 |             float levelSwipeDegreesProgress = Math.abs(mSwipeDegrees) / MAX_SWIPE_DEGREES;
151 | 
152 |             if (levelSwipeDegreesProgress > LEVEL_SWEEP_ANGLE_OFFSETS[1]) {
153 |                 mLevelSwipeDegrees[0] = -mSwipeDegrees;
154 |                 mLevelSwipeDegrees[1] = MAX_SWIPE_DEGREES * LEVEL_SWEEP_ANGLE_OFFSETS[1];
155 |                 mLevelSwipeDegrees[2] = MAX_SWIPE_DEGREES * LEVEL_SWEEP_ANGLE_OFFSETS[2];
156 |             } else if (levelSwipeDegreesProgress > LEVEL_SWEEP_ANGLE_OFFSETS[2]) {
157 |                 mLevelSwipeDegrees[0] = 0;
158 |                 mLevelSwipeDegrees[1] = -mSwipeDegrees;
159 |                 mLevelSwipeDegrees[2] = MAX_SWIPE_DEGREES * LEVEL_SWEEP_ANGLE_OFFSETS[2];
160 |             } else {
161 |                 mLevelSwipeDegrees[0] = 0;
162 |                 mLevelSwipeDegrees[1] = 0;
163 |                 mLevelSwipeDegrees[2] = -mSwipeDegrees;
164 |             }
165 |         }
166 | 
167 |         mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * renderProgress) + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
168 |     }
169 | 
170 |     @Override
171 |     protected void setAlpha(int alpha) {
172 |         mPaint.setAlpha(alpha);
173 |     }
174 | 
175 |     @Override
176 |     protected void setColorFilter(ColorFilter cf) {
177 |         mPaint.setColorFilter(cf);
178 |     }
179 | 
180 |     @Override
181 |     protected void reset() {
182 |         resetOriginals();
183 |     }
184 | 
185 |     private void initStrokeInset(float width, float height) {
186 |         float minSize = Math.min(width, height);
187 |         float strokeInset = minSize / 2.0f - mCenterRadius;
188 |         float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
189 |         mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
190 |     }
191 | 
192 |     private void storeOriginals() {
193 |         mOriginEndDegrees = mEndDegrees;
194 |         mOriginStartDegrees = mEndDegrees;
195 |     }
196 | 
197 |     private void resetOriginals() {
198 |         mOriginEndDegrees = 0;
199 |         mOriginStartDegrees = 0;
200 | 
201 |         mEndDegrees = 0;
202 |         mStartDegrees = 0;
203 | 
204 |         mLevelSwipeDegrees[0] = 0;
205 |         mLevelSwipeDegrees[1] = 0;
206 |         mLevelSwipeDegrees[2] = 0;
207 |     }
208 | 
209 |     private void apply(Builder builder) {
210 |         this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
211 |         this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
212 |         this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
213 |         this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
214 | 
215 |         this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
216 | 
217 |         this.mLevelColors = builder.mLevelColors != null ? builder.mLevelColors : this.mLevelColors;
218 | 
219 |         setupPaint();
220 |         initStrokeInset(this.mWidth, this.mHeight);
221 |     }
222 | 
223 |     public static class Builder {
224 |         private Context mContext;
225 | 
226 |         private int mWidth;
227 |         private int mHeight;
228 |         private int mStrokeWidth;
229 |         private int mCenterRadius;
230 | 
231 |         private int mDuration;
232 | 
233 |         @Size(3)
234 |         private int[] mLevelColors;
235 | 
236 |         public Builder(Context mContext) {
237 |             this.mContext = mContext;
238 |         }
239 | 
240 |         public Builder setWidth(int width) {
241 |             this.mWidth = width;
242 |             return this;
243 |         }
244 | 
245 |         public Builder setHeight(int height) {
246 |             this.mHeight = height;
247 |             return this;
248 |         }
249 | 
250 |         public Builder setStrokeWidth(int strokeWidth) {
251 |             this.mStrokeWidth = strokeWidth;
252 |             return this;
253 |         }
254 | 
255 |         public Builder setCenterRadius(int centerRadius) {
256 |             this.mCenterRadius = centerRadius;
257 |             return this;
258 |         }
259 | 
260 |         public Builder setDuration(int duration) {
261 |             this.mDuration = duration;
262 |             return this;
263 |         }
264 | 
265 |         public Builder setLevelColors(@Size(3) int[] colors) {
266 |             this.mLevelColors = colors;
267 |             return this;
268 |         }
269 | 
270 |         public Builder setLevelColor(int color) {
271 |             return setLevelColors(new int[]{oneThirdAlphaColor(color), twoThirdAlphaColor(color), color});
272 |         }
273 | 
274 |         public LevelLoadingRenderer build() {
275 |             LevelLoadingRenderer loadingRenderer = new LevelLoadingRenderer(mContext);
276 |             loadingRenderer.apply(this);
277 |             return loadingRenderer;
278 |         }
279 | 
280 |         private int oneThirdAlphaColor(int colorValue) {
281 |             int startA = (colorValue >> 24) & 0xff;
282 |             int startR = (colorValue >> 16) & 0xff;
283 |             int startG = (colorValue >> 8) & 0xff;
284 |             int startB = colorValue & 0xff;
285 | 
286 |             return (startA / 3 << 24) | (startR << 16) | (startG << 8) | startB;
287 |         }
288 | 
289 |         private int twoThirdAlphaColor(int colorValue) {
290 |             int startA = (colorValue >> 24) & 0xff;
291 |             int startR = (colorValue >> 16) & 0xff;
292 |             int startG = (colorValue >> 8) & 0xff;
293 |             int startB = colorValue & 0xff;
294 | 
295 |             return (startA * 2 / 3 << 24) | (startR << 16) | (startG << 8) | startB;
296 |         }
297 |     }
298 | 
299 | }
300 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/rotate/MaterialLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.rotate;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.AnimatorListenerAdapter;
  5 | import android.content.Context;
  6 | import android.graphics.Canvas;
  7 | import android.graphics.Color;
  8 | import android.graphics.ColorFilter;
  9 | import android.graphics.Paint;
 10 | import android.graphics.RectF;
 11 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
 12 | import android.view.animation.Interpolator;
 13 | 
 14 | import app.dinus.com.loadingdrawable.DensityUtil;
 15 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 16 | 
 17 | public class MaterialLoadingRenderer extends LoadingRenderer {
 18 |     private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
 19 | 
 20 |     private static final int DEGREE_360 = 360;
 21 |     private static final int NUM_POINTS = 5;
 22 | 
 23 |     private static final float MAX_SWIPE_DEGREES = 0.8f * DEGREE_360;
 24 |     private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
 25 | 
 26 |     private static final float COLOR_START_DELAY_OFFSET = 0.8f;
 27 |     private static final float END_TRIM_DURATION_OFFSET = 1.0f;
 28 |     private static final float START_TRIM_DURATION_OFFSET = 0.5f;
 29 | 
 30 |     private static final float DEFAULT_CENTER_RADIUS = 12.5f;
 31 |     private static final float DEFAULT_STROKE_WIDTH = 2.5f;
 32 | 
 33 |     private static final int[] DEFAULT_COLORS = new int[]{
 34 |             Color.RED, Color.GREEN, Color.BLUE
 35 |     };
 36 | 
 37 |     private final Paint mPaint = new Paint();
 38 |     private final RectF mTempBounds = new RectF();
 39 | 
 40 |     private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
 41 |         @Override
 42 |         public void onAnimationRepeat(Animator animator) {
 43 |             super.onAnimationRepeat(animator);
 44 |             storeOriginals();
 45 |             goToNextColor();
 46 | 
 47 |             mStartDegrees = mEndDegrees;
 48 |             mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
 49 |         }
 50 | 
 51 |         @Override
 52 |         public void onAnimationStart(Animator animation) {
 53 |             super.onAnimationStart(animation);
 54 |             mRotationCount = 0;
 55 |         }
 56 |     };
 57 | 
 58 |     private int[] mColors;
 59 |     private int mColorIndex;
 60 |     private int mCurrentColor;
 61 | 
 62 |     private float mStrokeInset;
 63 | 
 64 |     private float mRotationCount;
 65 |     private float mGroupRotation;
 66 | 
 67 |     private float mEndDegrees;
 68 |     private float mStartDegrees;
 69 |     private float mSwipeDegrees;
 70 |     private float mOriginEndDegrees;
 71 |     private float mOriginStartDegrees;
 72 | 
 73 |     private float mStrokeWidth;
 74 |     private float mCenterRadius;
 75 | 
 76 |     private MaterialLoadingRenderer(Context context) {
 77 |         super(context);
 78 |         init(context);
 79 |         setupPaint();
 80 |         addRenderListener(mAnimatorListener);
 81 |     }
 82 | 
 83 |     private void init(Context context) {
 84 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 85 |         mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
 86 | 
 87 |         mColors = DEFAULT_COLORS;
 88 | 
 89 |         setColorIndex(0);
 90 |         initStrokeInset(mWidth, mHeight);
 91 |     }
 92 | 
 93 |     private void setupPaint() {
 94 |         mPaint.setAntiAlias(true);
 95 |         mPaint.setStrokeWidth(mStrokeWidth);
 96 |         mPaint.setStyle(Paint.Style.STROKE);
 97 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
 98 |     }
 99 | 
100 |     @Override
101 |     protected void draw(Canvas canvas) {
102 |         int saveCount = canvas.save();
103 | 
104 |         mTempBounds.set(mBounds);
105 |         mTempBounds.inset(mStrokeInset, mStrokeInset);
106 | 
107 |         canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
108 | 
109 |         if (mSwipeDegrees != 0) {
110 |             mPaint.setColor(mCurrentColor);
111 |             canvas.drawArc(mTempBounds, mStartDegrees, mSwipeDegrees, false, mPaint);
112 |         }
113 | 
114 |         canvas.restoreToCount(saveCount);
115 |     }
116 | 
117 |     @Override
118 |     protected void computeRender(float renderProgress) {
119 |         updateRingColor(renderProgress);
120 | 
121 |         // Moving the start trim only occurs in the first 50% of a single ring animation
122 |         if (renderProgress <= START_TRIM_DURATION_OFFSET) {
123 |             float startTrimProgress = renderProgress / START_TRIM_DURATION_OFFSET;
124 |             mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES
125 |                     * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
126 |         }
127 | 
128 |         // Moving the end trim starts after 50% of a single ring animation completes
129 |         if (renderProgress > START_TRIM_DURATION_OFFSET) {
130 |             float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET)
131 |                     / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
132 |             mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES
133 |                     * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
134 |         }
135 | 
136 |         if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
137 |             mSwipeDegrees = mEndDegrees - mStartDegrees;
138 |         }
139 | 
140 |         mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * renderProgress)
141 |                 + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
142 |     }
143 | 
144 |     @Override
145 |     protected void setAlpha(int alpha) {
146 |         mPaint.setAlpha(alpha);
147 |     }
148 | 
149 |     @Override
150 |     protected void setColorFilter(ColorFilter cf) {
151 |         mPaint.setColorFilter(cf);
152 |     }
153 | 
154 |     @Override
155 |     protected void reset() {
156 |         resetOriginals();
157 |     }
158 | 
159 |     private void setColorIndex(int index) {
160 |         mColorIndex = index;
161 |         mCurrentColor = mColors[mColorIndex];
162 |     }
163 | 
164 |     private int getNextColor() {
165 |         return mColors[getNextColorIndex()];
166 |     }
167 | 
168 |     private int getNextColorIndex() {
169 |         return (mColorIndex + 1) % (mColors.length);
170 |     }
171 | 
172 |     private void goToNextColor() {
173 |         setColorIndex(getNextColorIndex());
174 |     }
175 | 
176 |     private void initStrokeInset(float width, float height) {
177 |         float minSize = Math.min(width, height);
178 |         float strokeInset = minSize / 2.0f - mCenterRadius;
179 |         float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
180 |         mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
181 |     }
182 | 
183 |     private void storeOriginals() {
184 |         mOriginEndDegrees = mEndDegrees;
185 |         mOriginStartDegrees = mEndDegrees;
186 |     }
187 | 
188 |     private void resetOriginals() {
189 |         mOriginEndDegrees = 0;
190 |         mOriginStartDegrees = 0;
191 | 
192 |         mEndDegrees = 0;
193 |         mStartDegrees = 0;
194 |     }
195 | 
196 |     private int getStartingColor() {
197 |         return mColors[mColorIndex];
198 |     }
199 | 
200 |     private void updateRingColor(float interpolatedTime) {
201 |         if (interpolatedTime > COLOR_START_DELAY_OFFSET) {
202 |             mCurrentColor = evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)
203 |                     / (1.0f - COLOR_START_DELAY_OFFSET), getStartingColor(), getNextColor());
204 |         }
205 |     }
206 | 
207 |     private int evaluateColorChange(float fraction, int startValue, int endValue) {
208 |         int startA = (startValue >> 24) & 0xff;
209 |         int startR = (startValue >> 16) & 0xff;
210 |         int startG = (startValue >> 8) & 0xff;
211 |         int startB = startValue & 0xff;
212 | 
213 |         int endA = (endValue >> 24) & 0xff;
214 |         int endR = (endValue >> 16) & 0xff;
215 |         int endG = (endValue >> 8) & 0xff;
216 |         int endB = endValue & 0xff;
217 | 
218 |         return ((startA + (int) (fraction * (endA - startA))) << 24)
219 |                 | ((startR + (int) (fraction * (endR - startR))) << 16)
220 |                 | ((startG + (int) (fraction * (endG - startG))) << 8)
221 |                 | ((startB + (int) (fraction * (endB - startB))));
222 |     }
223 | 
224 |     private void apply(Builder builder) {
225 |         this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
226 |         this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
227 |         this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
228 |         this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
229 | 
230 |         this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
231 | 
232 |         this.mColors = builder.mColors != null && builder.mColors.length > 0 ? builder.mColors : this.mColors;
233 | 
234 |         setColorIndex(0);
235 |         setupPaint();
236 |         initStrokeInset(this.mWidth, this.mHeight);
237 |     }
238 | 
239 |     public static class Builder {
240 |         private Context mContext;
241 | 
242 |         private int mWidth;
243 |         private int mHeight;
244 |         private int mStrokeWidth;
245 |         private int mCenterRadius;
246 | 
247 |         private int mDuration;
248 | 
249 |         private int[] mColors;
250 | 
251 |         public Builder(Context mContext) {
252 |             this.mContext = mContext;
253 |         }
254 | 
255 |         public Builder setWidth(int width) {
256 |             this.mWidth = width;
257 |             return this;
258 |         }
259 | 
260 |         public Builder setHeight(int height) {
261 |             this.mHeight = height;
262 |             return this;
263 |         }
264 | 
265 |         public Builder setStrokeWidth(int strokeWidth) {
266 |             this.mStrokeWidth = strokeWidth;
267 |             return this;
268 |         }
269 | 
270 |         public Builder setCenterRadius(int centerRadius) {
271 |             this.mCenterRadius = centerRadius;
272 |             return this;
273 |         }
274 | 
275 |         public Builder setDuration(int duration) {
276 |             this.mDuration = duration;
277 |             return this;
278 |         }
279 | 
280 |         public Builder setColors(int[] colors) {
281 |             this.mColors = colors;
282 |             return this;
283 |         }
284 | 
285 |         public MaterialLoadingRenderer build() {
286 |             MaterialLoadingRenderer loadingRenderer = new MaterialLoadingRenderer(mContext);
287 |             loadingRenderer.apply(this);
288 |             return loadingRenderer;
289 |         }
290 |     }
291 | }
292 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/circle/rotate/WhorlLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.circle.rotate;
  2 | 
  3 | import android.animation.Animator;
  4 | import android.animation.AnimatorListenerAdapter;
  5 | import android.content.Context;
  6 | import android.graphics.Canvas;
  7 | import android.graphics.Color;
  8 | import android.graphics.ColorFilter;
  9 | import android.graphics.Paint;
 10 | import android.graphics.Rect;
 11 | import android.graphics.RectF;
 12 | import android.support.annotation.NonNull;
 13 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
 14 | import android.view.animation.Interpolator;
 15 | 
 16 | import app.dinus.com.loadingdrawable.DensityUtil;
 17 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 18 | 
 19 | public class WhorlLoadingRenderer extends LoadingRenderer {
 20 |     private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
 21 | 
 22 |     private static final int DEGREE_180 = 180;
 23 |     private static final int DEGREE_360 = 360;
 24 |     private static final int NUM_POINTS = 5;
 25 | 
 26 |     private static final float MAX_SWIPE_DEGREES = 0.6f * DEGREE_360;
 27 |     private static final float FULL_GROUP_ROTATION = 3.0f * DEGREE_360;
 28 | 
 29 |     private static final float START_TRIM_DURATION_OFFSET = 0.5f;
 30 |     private static final float END_TRIM_DURATION_OFFSET = 1.0f;
 31 | 
 32 |     private static final float DEFAULT_CENTER_RADIUS = 12.5f;
 33 |     private static final float DEFAULT_STROKE_WIDTH = 2.5f;
 34 | 
 35 |     private static final int[] DEFAULT_COLORS = new int[]{
 36 |             Color.RED, Color.GREEN, Color.BLUE
 37 |     };
 38 | 
 39 |     private final Paint mPaint = new Paint();
 40 |     private final RectF mTempBounds = new RectF();
 41 |     private final RectF mTempArcBounds = new RectF();
 42 | 
 43 |     private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
 44 |         @Override
 45 |         public void onAnimationRepeat(Animator animator) {
 46 |             super.onAnimationRepeat(animator);
 47 |             storeOriginals();
 48 | 
 49 |             mStartDegrees = mEndDegrees;
 50 |             mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
 51 |         }
 52 | 
 53 |         @Override
 54 |         public void onAnimationStart(Animator animation) {
 55 |             super.onAnimationStart(animation);
 56 |             mRotationCount = 0;
 57 |         }
 58 |     };
 59 | 
 60 |     private int[] mColors;
 61 | 
 62 |     private float mStrokeInset;
 63 | 
 64 |     private float mRotationCount;
 65 |     private float mGroupRotation;
 66 | 
 67 |     private float mEndDegrees;
 68 |     private float mStartDegrees;
 69 |     private float mSwipeDegrees;
 70 |     private float mOriginEndDegrees;
 71 |     private float mOriginStartDegrees;
 72 | 
 73 |     private float mStrokeWidth;
 74 |     private float mCenterRadius;
 75 | 
 76 |     private WhorlLoadingRenderer(Context context) {
 77 |         super(context);
 78 |         init(context);
 79 |         setupPaint();
 80 |         addRenderListener(mAnimatorListener);
 81 |     }
 82 | 
 83 |     private void init(Context context) {
 84 |         mColors = DEFAULT_COLORS;
 85 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 86 |         mCenterRadius = DensityUtil.dip2px(context, DEFAULT_CENTER_RADIUS);
 87 | 
 88 |         initStrokeInset(mWidth, mHeight);
 89 |     }
 90 | 
 91 |     private void setupPaint() {
 92 |         mPaint.setAntiAlias(true);
 93 |         mPaint.setStrokeWidth(mStrokeWidth);
 94 |         mPaint.setStyle(Paint.Style.STROKE);
 95 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
 96 |     }
 97 | 
 98 |     @Override
 99 |     protected void draw(Canvas canvas) {
100 |         int saveCount = canvas.save();
101 | 
102 |         mTempBounds.set(mBounds);
103 |         mTempBounds.inset(mStrokeInset, mStrokeInset);
104 | 
105 |         canvas.rotate(mGroupRotation, mTempBounds.centerX(), mTempBounds.centerY());
106 | 
107 |         if (mSwipeDegrees != 0) {
108 |             for (int i = 0; i < mColors.length; i++) {
109 |                 mPaint.setStrokeWidth(mStrokeWidth / (i + 1));
110 |                 mPaint.setColor(mColors[i]);
111 |                 canvas.drawArc(createArcBounds(mTempBounds, i), mStartDegrees + DEGREE_180 * (i % 2),
112 |                         mSwipeDegrees, false, mPaint);
113 |             }
114 |         }
115 | 
116 |         canvas.restoreToCount(saveCount);
117 |     }
118 | 
119 |     private RectF createArcBounds(RectF sourceArcBounds, int index) {
120 |         int intervalWidth = 0;
121 | 
122 |         for (int i = 0; i < index; i++) {
123 |             intervalWidth += mStrokeWidth / (i + 1.0f) * 1.5f;
124 |         }
125 | 
126 |         int arcBoundsLeft = (int) (sourceArcBounds.left + intervalWidth);
127 |         int arcBoundsTop = (int) (sourceArcBounds.top + intervalWidth);
128 |         int arcBoundsRight = (int) (sourceArcBounds.right - intervalWidth);
129 |         int arcBoundsBottom = (int) (sourceArcBounds.bottom - intervalWidth);
130 |         mTempArcBounds.set(arcBoundsLeft, arcBoundsTop, arcBoundsRight, arcBoundsBottom);
131 | 
132 |         return mTempArcBounds;
133 |     }
134 | 
135 |     @Override
136 |     protected void computeRender(float renderProgress) {
137 |         // Moving the start trim only occurs in the first 50% of a single ring animation
138 |         if (renderProgress <= START_TRIM_DURATION_OFFSET) {
139 |             float startTrimProgress = (renderProgress) / (1.0f - START_TRIM_DURATION_OFFSET);
140 |             mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(startTrimProgress);
141 |         }
142 | 
143 |         // Moving the end trim starts after 50% of a single ring animation
144 |         if (renderProgress > START_TRIM_DURATION_OFFSET) {
145 |             float endTrimProgress = (renderProgress - START_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - START_TRIM_DURATION_OFFSET);
146 |             mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES * MATERIAL_INTERPOLATOR.getInterpolation(endTrimProgress);
147 |         }
148 | 
149 |         if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
150 |             mSwipeDegrees = mEndDegrees - mStartDegrees;
151 |         }
152 | 
153 |         mGroupRotation = ((FULL_GROUP_ROTATION / NUM_POINTS) * renderProgress) + (FULL_GROUP_ROTATION * (mRotationCount / NUM_POINTS));
154 |     }
155 | 
156 |     @Override
157 |     protected void setAlpha(int alpha) {
158 |         mPaint.setAlpha(alpha);
159 | 
160 |     }
161 | 
162 |     @Override
163 |     protected void setColorFilter(ColorFilter cf) {
164 |         mPaint.setColorFilter(cf);
165 | 
166 |     }
167 | 
168 |     @Override
169 |     protected void reset() {
170 |         resetOriginals();
171 |     }
172 | 
173 |     private void initStrokeInset(float width, float height) {
174 |         float minSize = Math.min(width, height);
175 |         float strokeInset = minSize / 2.0f - mCenterRadius;
176 |         float minStrokeInset = (float) Math.ceil(mStrokeWidth / 2.0f);
177 |         mStrokeInset = strokeInset < minStrokeInset ? minStrokeInset : strokeInset;
178 |     }
179 | 
180 |     private void storeOriginals() {
181 |         mOriginEndDegrees = mEndDegrees;
182 |         mOriginStartDegrees = mEndDegrees;
183 |     }
184 | 
185 |     private void resetOriginals() {
186 |         mOriginEndDegrees = 0;
187 |         mOriginStartDegrees = 0;
188 | 
189 |         mEndDegrees = 0;
190 |         mStartDegrees = 0;
191 | 
192 |         mSwipeDegrees = 0;
193 |     }
194 | 
195 |     private void apply(Builder builder) {
196 |         this.mWidth = builder.mWidth > 0 ? builder.mWidth : this.mWidth;
197 |         this.mHeight = builder.mHeight > 0 ? builder.mHeight : this.mHeight;
198 |         this.mStrokeWidth = builder.mStrokeWidth > 0 ? builder.mStrokeWidth : this.mStrokeWidth;
199 |         this.mCenterRadius = builder.mCenterRadius > 0 ? builder.mCenterRadius : this.mCenterRadius;
200 | 
201 |         this.mDuration = builder.mDuration > 0 ? builder.mDuration : this.mDuration;
202 | 
203 |         this.mColors = builder.mColors != null && builder.mColors.length > 0 ? builder.mColors : this.mColors;
204 | 
205 |         setupPaint();
206 |         initStrokeInset(this.mWidth, this.mHeight);
207 |     }
208 | 
209 |     public static class Builder {
210 |         private Context mContext;
211 | 
212 |         private int mWidth;
213 |         private int mHeight;
214 |         private int mStrokeWidth;
215 |         private int mCenterRadius;
216 | 
217 |         private int mDuration;
218 | 
219 |         private int[] mColors;
220 | 
221 |         public Builder(Context mContext) {
222 |             this.mContext = mContext;
223 |         }
224 | 
225 |         public Builder setWidth(int width) {
226 |             this.mWidth = width;
227 |             return this;
228 |         }
229 | 
230 |         public Builder setHeight(int height) {
231 |             this.mHeight = height;
232 |             return this;
233 |         }
234 | 
235 |         public Builder setStrokeWidth(int strokeWidth) {
236 |             this.mStrokeWidth = strokeWidth;
237 |             return this;
238 |         }
239 | 
240 |         public Builder setCenterRadius(int centerRadius) {
241 |             this.mCenterRadius = centerRadius;
242 |             return this;
243 |         }
244 | 
245 |         public Builder setDuration(int duration) {
246 |             this.mDuration = duration;
247 |             return this;
248 |         }
249 | 
250 |         public Builder setColors(int[] colors) {
251 |             this.mColors = colors;
252 |             return this;
253 |         }
254 | 
255 |         public WhorlLoadingRenderer build() {
256 |             WhorlLoadingRenderer loadingRenderer = new WhorlLoadingRenderer(mContext);
257 |             loadingRenderer.apply(this);
258 |             return loadingRenderer;
259 |         }
260 |     }
261 | }
262 | 


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/goods/BalloonLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.goods;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Path;
  9 | import android.graphics.Rect;
 10 | import android.graphics.RectF;
 11 | import android.util.DisplayMetrics;
 12 | import android.view.animation.AccelerateInterpolator;
 13 | import android.view.animation.Interpolator;
 14 | 
 15 | import app.dinus.com.loadingdrawable.DensityUtil;
 16 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 17 | 
 18 | public class BalloonLoadingRenderer extends LoadingRenderer {
 19 |     private static final String PERCENT_SIGN = "%";
 20 | 
 21 |     private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
 22 | 
 23 |     private static final float START_INHALE_DURATION_OFFSET = 0.4f;
 24 | 
 25 |     private static final float DEFAULT_WIDTH = 200.0f;
 26 |     private static final float DEFAULT_HEIGHT = 150.0f;
 27 |     private static final float DEFAULT_STROKE_WIDTH = 2.0f;
 28 |     private static final float DEFAULT_GAS_TUBE_WIDTH = 48;
 29 |     private static final float DEFAULT_GAS_TUBE_HEIGHT = 20;
 30 |     private static final float DEFAULT_CANNULA_WIDTH = 13;
 31 |     private static final float DEFAULT_CANNULA_HEIGHT = 37;
 32 |     private static final float DEFAULT_CANNULA_OFFSET_Y = 3;
 33 |     private static final float DEFAULT_CANNULA_MAX_OFFSET_Y = 15;
 34 |     private static final float DEFAULT_PIPE_BODY_WIDTH = 16;
 35 |     private static final float DEFAULT_PIPE_BODY_HEIGHT = 36;
 36 |     private static final float DEFAULT_BALLOON_WIDTH = 38;
 37 |     private static final float DEFAULT_BALLOON_HEIGHT = 48;
 38 |     private static final float DEFAULT_RECT_CORNER_RADIUS = 2;
 39 | 
 40 |     private static final int DEFAULT_BALLOON_COLOR = Color.parseColor("#ffF3C211");
 41 |     private static final int DEFAULT_GAS_TUBE_COLOR = Color.parseColor("#ff174469");
 42 |     private static final int DEFAULT_PIPE_BODY_COLOR = Color.parseColor("#aa2369B1");
 43 |     private static final int DEFAULT_CANNULA_COLOR = Color.parseColor("#ff174469");
 44 | 
 45 |     private static final float DEFAULT_TEXT_SIZE = 7.0f;
 46 | 
 47 |     private static final long ANIMATION_DURATION = 3333;
 48 | 
 49 |     private final Paint mPaint = new Paint();
 50 |     private final RectF mCurrentBounds = new RectF();
 51 |     private final RectF mGasTubeBounds = new RectF();
 52 |     private final RectF mPipeBodyBounds = new RectF();
 53 |     private final RectF mCannulaBounds = new RectF();
 54 |     private final RectF mBalloonBounds = new RectF();
 55 | 
 56 |     private final Rect mProgressBounds = new Rect();
 57 | 
 58 |     private float mTextSize;
 59 |     private float mProgress;
 60 | 
 61 |     private String mProgressText;
 62 | 
 63 |     private float mGasTubeWidth;
 64 |     private float mGasTubeHeight;
 65 |     private float mCannulaWidth;
 66 |     private float mCannulaHeight;
 67 |     private float mCannulaMaxOffsetY;
 68 |     private float mCannulaOffsetY;
 69 |     private float mPipeBodyWidth;
 70 |     private float mPipeBodyHeight;
 71 |     private float mBalloonWidth;
 72 |     private float mBalloonHeight;
 73 |     private float mRectCornerRadius;
 74 |     private float mStrokeWidth;
 75 | 
 76 |     private int mBalloonColor;
 77 |     private int mGasTubeColor;
 78 |     private int mCannulaColor;
 79 |     private int mPipeBodyColor;
 80 | 
 81 |     private BalloonLoadingRenderer(Context context) {
 82 |         super(context);
 83 |         init(context);
 84 |         setupPaint();
 85 |     }
 86 | 
 87 |     private void init(Context context) {
 88 |         mTextSize = DensityUtil.dip2px(context, DEFAULT_TEXT_SIZE);
 89 | 
 90 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 91 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 92 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 93 | 
 94 |         mGasTubeWidth = DensityUtil.dip2px(context, DEFAULT_GAS_TUBE_WIDTH);
 95 |         mGasTubeHeight = DensityUtil.dip2px(context, DEFAULT_GAS_TUBE_HEIGHT);
 96 |         mCannulaWidth = DensityUtil.dip2px(context, DEFAULT_CANNULA_WIDTH);
 97 |         mCannulaHeight = DensityUtil.dip2px(context, DEFAULT_CANNULA_HEIGHT);
 98 |         mCannulaOffsetY = DensityUtil.dip2px(context, DEFAULT_CANNULA_OFFSET_Y);
 99 |         mCannulaMaxOffsetY = DensityUtil.dip2px(context, DEFAULT_CANNULA_MAX_OFFSET_Y);
100 |         mPipeBodyWidth = DensityUtil.dip2px(context, DEFAULT_PIPE_BODY_WIDTH);
101 |         mPipeBodyHeight = DensityUtil.dip2px(context, DEFAULT_PIPE_BODY_HEIGHT);
102 |         mBalloonWidth = DensityUtil.dip2px(context, DEFAULT_BALLOON_WIDTH);
103 |         mBalloonHeight = DensityUtil.dip2px(context, DEFAULT_BALLOON_HEIGHT);
104 |         mRectCornerRadius = DensityUtil.dip2px(context, DEFAULT_RECT_CORNER_RADIUS);
105 | 
106 |         mBalloonColor = DEFAULT_BALLOON_COLOR;
107 |         mGasTubeColor = DEFAULT_GAS_TUBE_COLOR;
108 |         mCannulaColor = DEFAULT_CANNULA_COLOR;
109 |         mPipeBodyColor = DEFAULT_PIPE_BODY_COLOR;
110 | 
111 |         mProgressText = 10 + PERCENT_SIGN;
112 | 
113 |         mDuration = ANIMATION_DURATION;
114 |     }
115 | 
116 |     private void setupPaint() {
117 |         mPaint.setAntiAlias(true);
118 |         mPaint.setStrokeWidth(mStrokeWidth);
119 |     }
120 | 
121 |     @Override
122 |     protected void draw(Canvas canvas, Rect bounds) {
123 |         int saveCount = canvas.save();
124 | 
125 |         RectF arcBounds = mCurrentBounds;
126 |         arcBounds.set(bounds);
127 | 
128 |         //draw draw gas tube
129 |         mPaint.setColor(mGasTubeColor);
130 |         mPaint.setStyle(Paint.Style.STROKE);
131 |         mPaint.setStrokeWidth(mStrokeWidth);
132 |         canvas.drawPath(createGasTubePath(mGasTubeBounds), mPaint);
133 | 
134 |         //draw balloon
135 |         mPaint.setColor(mBalloonColor);
136 |         mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
137 |         canvas.drawPath(createBalloonPath(mBalloonBounds, mProgress), mPaint);
138 | 
139 |         //draw progress
140 |         mPaint.setColor(mGasTubeColor);
141 |         mPaint.setTextSize(mTextSize);
142 |         mPaint.setStrokeWidth(mStrokeWidth / 5.0f);
143 |         canvas.drawText(mProgressText, arcBounds.centerX() - mProgressBounds.width() / 2.0f,
144 |                 mGasTubeBounds.centerY() + mProgressBounds.height() / 2.0f, mPaint);
145 | 
146 |         //draw cannula
147 |         mPaint.setColor(mCannulaColor);
148 |         mPaint.setStyle(Paint.Style.STROKE);
149 |         mPaint.setStrokeWidth(mStrokeWidth);
150 |         canvas.drawPath(createCannulaHeadPath(mCannulaBounds), mPaint);
151 |         mPaint.setStyle(Paint.Style.FILL);
152 |         canvas.drawPath(createCannulaBottomPath(mCannulaBounds), mPaint);
153 | 
154 |         //draw pipe body
155 |         mPaint.setColor(mPipeBodyColor);
156 |         mPaint.setStyle(Paint.Style.FILL);
157 |         canvas.drawRoundRect(mPipeBodyBounds, mRectCornerRadius, mRectCornerRadius, mPaint);
158 | 
159 |         canvas.restoreToCount(saveCount);
160 |     }
161 | 
162 |     @Override
163 |     protected void computeRender(float renderProgress) {
164 |         RectF arcBounds = mCurrentBounds;
165 |         //compute gas tube bounds
166 |         mGasTubeBounds.set(arcBounds.centerX() - mGasTubeWidth / 2.0f, arcBounds.centerY(),
167 |                 arcBounds.centerX() + mGasTubeWidth / 2.0f, arcBounds.centerY() + mGasTubeHeight);
168 |         //compute pipe body bounds
169 |         mPipeBodyBounds.set(arcBounds.centerX() + mGasTubeWidth / 2.0f - mPipeBodyWidth / 2.0f, arcBounds.centerY() - mPipeBodyHeight,
170 |                 arcBounds.centerX() + mGasTubeWidth / 2.0f + mPipeBodyWidth / 2.0f, arcBounds.centerY());
171 |         //compute cannula bounds
172 |         mCannulaBounds.set(arcBounds.centerX() + mGasTubeWidth / 2.0f - mCannulaWidth / 2.0f, arcBounds.centerY() - mCannulaHeight - mCannulaOffsetY,
173 |                 arcBounds.centerX() + mGasTubeWidth / 2.0f + mCannulaWidth / 2.0f, arcBounds.centerY() - mCannulaOffsetY);
174 |         //compute balloon bounds
175 |         float insetX = mBalloonWidth * 0.333f * (1 - mProgress);
176 |         float insetY = mBalloonHeight * 0.667f * (1 - mProgress);
177 |         mBalloonBounds.set(arcBounds.centerX() - mGasTubeWidth / 2.0f - mBalloonWidth / 2.0f + insetX, arcBounds.centerY() - mBalloonHeight + insetY,
178 |                 arcBounds.centerX() - mGasTubeWidth / 2.0f + mBalloonWidth / 2.0f - insetX, arcBounds.centerY());
179 | 
180 |         if (renderProgress <= START_INHALE_DURATION_OFFSET) {
181 |             mCannulaBounds.offset(0, -mCannulaMaxOffsetY * renderProgress / START_INHALE_DURATION_OFFSET);
182 | 
183 |             mProgress = 0.0f;
184 |             mProgressText = 10 + PERCENT_SIGN;
185 | 
186 |             mPaint.setTextSize(mTextSize);
187 |             mPaint.getTextBounds(mProgressText, 0, mProgressText.length(), mProgressBounds);
188 |         } else {
189 |             float exhaleProgress = ACCELERATE_INTERPOLATOR.getInterpolation(1.0f - (renderProgress - START_INHALE_DURATION_OFFSET) / (1.0f - START_INHALE_DURATION_OFFSET));
190 |             mCannulaBounds.offset(0, -mCannulaMaxOffsetY * exhaleProgress);
191 | 
192 |             mProgress = 1.0f - exhaleProgress;
193 |             mProgressText = adjustProgress((int) (exhaleProgress * 100.0f)) + PERCENT_SIGN;
194 | 
195 |             mPaint.setTextSize(mTextSize);
196 |             mPaint.getTextBounds(mProgressText, 0, mProgressText.length(), mProgressBounds);
197 |         }
198 |     }
199 | 
200 |     private int adjustProgress(int progress) {
201 |         progress = progress / 10 * 10;
202 |         progress = 100 - progress + 10;
203 |         if (progress > 100) {
204 |             progress = 100;
205 |         }
206 | 
207 |         return progress;
208 |     }
209 | 
210 |     private Path createGasTubePath(RectF gasTubeRect) {
211 |         Path path = new Path();
212 |         path.moveTo(gasTubeRect.left, gasTubeRect.top);
213 |         path.lineTo(gasTubeRect.left, gasTubeRect.bottom);
214 |         path.lineTo(gasTubeRect.right, gasTubeRect.bottom);
215 |         path.lineTo(gasTubeRect.right, gasTubeRect.top);
216 |         return path;
217 |     }
218 | 
219 |     private Path createCannulaHeadPath(RectF cannulaRect) {
220 |         Path path = new Path();
221 |         path.moveTo(cannulaRect.left, cannulaRect.top);
222 |         path.lineTo(cannulaRect.right, cannulaRect.top);
223 |         path.moveTo(cannulaRect.centerX(), cannulaRect.top);
224 |         path.lineTo(cannulaRect.centerX(), cannulaRect.bottom - 0.833f * cannulaRect.width());
225 |         return path;
226 |     }
227 | 
228 |     private Path createCannulaBottomPath(RectF cannulaRect) {
229 |         RectF cannulaHeadRect = new RectF(cannulaRect.left, cannulaRect.bottom - 0.833f * cannulaRect.width(),
230 |                 cannulaRect.right, cannulaRect.bottom);
231 | 
232 |         Path path = new Path();
233 |         path.addRoundRect(cannulaHeadRect, mRectCornerRadius, mRectCornerRadius, Path.Direction.CCW);
234 |         return path;
235 |     }
236 | 
237 |     /**
238 |      * Coordinates are approximate, you have better cooperate with the designer's design draft
239 |      */
240 |     private Path createBalloonPath(RectF balloonRect, float progress) {
241 | 
242 |         Path path = new Path();
243 |         path.moveTo(balloonRect.centerX(), balloonRect.bottom);
244 | 
245 |         float progressWidth = balloonRect.width() * progress;
246 |         float progressHeight = balloonRect.height() * progress;
247 |         //draw left half
248 |         float leftIncrementX1 = progressWidth * -0.48f;
249 |         float leftIncrementY1 = progressHeight * 0.75f;
250 |         float leftIncrementX2 = progressWidth * -0.03f;
251 |         float leftIncrementY2 = progressHeight * -1.6f;
252 |         float leftIncrementX3 = progressWidth * 0.9f;
253 |         float leftIncrementY3 = progressHeight * -1.0f;
254 | 
255 |         path.cubicTo(balloonRect.left + balloonRect.width() * 0.25f + leftIncrementX1, balloonRect.centerY() - balloonRect.height() * 0.4f + leftIncrementY1,
256 |                 balloonRect.left - balloonRect.width() * 0.20f + leftIncrementX2, balloonRect.centerY() + balloonRect.height() * 1.15f + leftIncrementY2,
257 |                 balloonRect.left - balloonRect.width() * 0.4f + leftIncrementX3, balloonRect.bottom + leftIncrementY3);
258 | 
259 | //        the results of the left final transformation
260 | //        path.cubicTo(balloonRect.left - balloonRect.width() * 0.13f, balloonRect.centerY() + balloonRect.height() * 0.35f,
261 | //                balloonRect.left - balloonRect.width() * 0.23f, balloonRect.centerY() - balloonRect.height() * 0.45f,
262 | //                balloonRect.left + balloonRect.width() * 0.5f, balloonRect.bottom - balloonRect.height());
263 | 
264 |         //draw right half
265 |         float rightIncrementX1 = progressWidth * 1.51f;
266 |         float rightIncrementY1 = progressHeight * -0.05f;
267 |         float rightIncrementX2 = progressWidth * 0.03f;
268 |         float rightIncrementY2 = progressHeight * 0.5f;
269 |         float rightIncrementX3 = 0.0f;
270 |         float rightIncrementY3 = 0.0f;
271 | 
272 |         path.cubicTo(balloonRect.left - balloonRect.width() * 0.38f + rightIncrementX1, balloonRect.centerY() - balloonRect.height() * 0.4f + rightIncrementY1,
273 |                 balloonRect.left + balloonRect.width() * 1.1f + rightIncrementX2, balloonRect.centerY() - balloonRect.height() * 0.15f + rightIncrementY2,
274 |                 balloonRect.left + balloonRect.width() * 0.5f + rightIncrementX3, balloonRect.bottom + rightIncrementY3);
275 | 
276 | //        the results of the right final transformation
277 | //        path.cubicTo(balloonRect.left + balloonRect.width() * 1.23f, balloonRect.centerY() - balloonRect.height() * 0.45f,
278 | //                balloonRect.left + balloonRect.width() * 1.13f, balloonRect.centerY() + balloonRect.height() * 0.35f,
279 | //                balloonRect.left + balloonRect.width() * 0.5f, balloonRect.bottom);
280 | 
281 |         return path;
282 |     }
283 | 
284 |     @Override
285 |     protected void setAlpha(int alpha) {
286 |         mPaint.setAlpha(alpha);
287 | 
288 |     }
289 | 
290 |     @Override
291 |     protected void setColorFilter(ColorFilter cf) {
292 |         mPaint.setColorFilter(cf);
293 |     }
294 | 
295 |     @Override
296 |     protected void reset() {
297 |     }
298 | 
299 |     public static class Builder {
300 |         private Context mContext;
301 | 
302 |         public Builder(Context mContext) {
303 |             this.mContext = mContext;
304 |         }
305 | 
306 |         public BalloonLoadingRenderer build() {
307 |             BalloonLoadingRenderer loadingRenderer = new BalloonLoadingRenderer(mContext);
308 |             return loadingRenderer;
309 |         }
310 |     }
311 | }


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/goods/WaterBottleLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.goods;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Path;
  9 | import android.graphics.Rect;
 10 | import android.graphics.RectF;
 11 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
 12 | import android.util.DisplayMetrics;
 13 | import android.view.animation.Interpolator;
 14 | 
 15 | import java.util.ArrayList;
 16 | import java.util.List;
 17 | import java.util.Random;
 18 | 
 19 | import app.dinus.com.loadingdrawable.DensityUtil;
 20 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 21 | 
 22 | public class WaterBottleLoadingRenderer extends LoadingRenderer {
 23 |     private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
 24 | 
 25 |     private static final float DEFAULT_WIDTH = 200.0f;
 26 |     private static final float DEFAULT_HEIGHT = 150.0f;
 27 |     private static final float DEFAULT_STROKE_WIDTH = 1.5f;
 28 |     private static final float DEFAULT_BOTTLE_WIDTH = 30;
 29 |     private static final float DEFAULT_BOTTLE_HEIGHT = 43;
 30 |     private static final float WATER_LOWEST_POINT_TO_BOTTLENECK_DISTANCE = 30;
 31 | 
 32 |     private static final int DEFAULT_WAVE_COUNT = 5;
 33 |     private static final int DEFAULT_WATER_DROP_COUNT = 25;
 34 | 
 35 |     private static final int MAX_WATER_DROP_RADIUS = 5;
 36 |     private static final int MIN_WATER_DROP_RADIUS = 1;
 37 | 
 38 |     private static final int DEFAULT_BOTTLE_COLOR = Color.parseColor("#FFDAEBEB");
 39 |     private static final int DEFAULT_WATER_COLOR = Color.parseColor("#FF29E3F2");
 40 | 
 41 |     private static final float DEFAULT_TEXT_SIZE = 7.0f;
 42 | 
 43 |     private static final String LOADING_TEXT = "loading";
 44 | 
 45 |     private static final long ANIMATION_DURATION = 11111;
 46 | 
 47 |     private final Random mRandom = new Random();
 48 | 
 49 |     private final Paint mPaint = new Paint();
 50 |     private final RectF mCurrentBounds = new RectF();
 51 |     private final RectF mBottleBounds = new RectF();
 52 |     private final RectF mWaterBounds = new RectF();
 53 |     private final Rect mLoadingBounds = new Rect();
 54 |     private final List<WaterDropHolder> mWaterDropHolders = new ArrayList<>();
 55 | 
 56 |     private float mTextSize;
 57 |     private float mProgress;
 58 | 
 59 |     private float mBottleWidth;
 60 |     private float mBottleHeight;
 61 |     private float mStrokeWidth;
 62 |     private float mWaterLowestPointToBottleneckDistance;
 63 | 
 64 |     private int mBottleColor;
 65 |     private int mWaterColor;
 66 | 
 67 |     private int mWaveCount;
 68 | 
 69 |     private WaterBottleLoadingRenderer(Context context) {
 70 |         super(context);
 71 |         init(context);
 72 |         setupPaint();
 73 |     }
 74 | 
 75 |     private void init(Context context) {
 76 |         mTextSize = DensityUtil.dip2px(context, DEFAULT_TEXT_SIZE);
 77 | 
 78 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 79 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 80 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 81 | 
 82 |         mBottleWidth = DensityUtil.dip2px(context, DEFAULT_BOTTLE_WIDTH);
 83 |         mBottleHeight = DensityUtil.dip2px(context, DEFAULT_BOTTLE_HEIGHT);
 84 |         mWaterLowestPointToBottleneckDistance = DensityUtil.dip2px(context, WATER_LOWEST_POINT_TO_BOTTLENECK_DISTANCE);
 85 | 
 86 |         mBottleColor = DEFAULT_BOTTLE_COLOR;
 87 |         mWaterColor = DEFAULT_WATER_COLOR;
 88 | 
 89 |         mWaveCount = DEFAULT_WAVE_COUNT;
 90 | 
 91 |         mDuration = ANIMATION_DURATION;
 92 |     }
 93 | 
 94 |     private void setupPaint() {
 95 |         mPaint.setAntiAlias(true);
 96 |         mPaint.setStrokeWidth(mStrokeWidth);
 97 |         mPaint.setStrokeJoin(Paint.Join.ROUND);
 98 |     }
 99 | 
100 |     @Override
101 |     protected void draw(Canvas canvas, Rect bounds) {
102 |         int saveCount = canvas.save();
103 | 
104 |         RectF arcBounds = mCurrentBounds;
105 |         arcBounds.set(bounds);
106 |         //draw bottle
107 |         mPaint.setStyle(Paint.Style.STROKE);
108 |         mPaint.setColor(mBottleColor);
109 |         canvas.drawPath(createBottlePath(mBottleBounds), mPaint);
110 | 
111 |         //draw water
112 |         mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
113 |         mPaint.setColor(mWaterColor);
114 |         canvas.drawPath(createWaterPath(mWaterBounds, mProgress), mPaint);
115 | 
116 |         //draw water drop
117 |         mPaint.setStyle(Paint.Style.FILL);
118 |         mPaint.setColor(mWaterColor);
119 |         for (WaterDropHolder waterDropHolder : mWaterDropHolders) {
120 |             if (waterDropHolder.mNeedDraw) {
121 |                 canvas.drawCircle(waterDropHolder.mInitX, waterDropHolder.mCurrentY, waterDropHolder.mRadius, mPaint);
122 |             }
123 |         }
124 | 
125 |         //draw loading text
126 |         mPaint.setColor(mBottleColor);
127 |         canvas.drawText(LOADING_TEXT, mBottleBounds.centerX() - mLoadingBounds.width() / 2.0f,
128 |                 mBottleBounds.bottom + mBottleBounds.height() * 0.2f, mPaint);
129 |         canvas.restoreToCount(saveCount);
130 |     }
131 | 
132 |     @Override
133 |     protected void computeRender(float renderProgress) {
134 |         if (mCurrentBounds.width() <= 0) {
135 |             return;
136 |         }
137 | 
138 |         RectF arcBounds = mCurrentBounds;
139 |         //compute gas tube bounds
140 |         mBottleBounds.set(arcBounds.centerX() - mBottleWidth / 2.0f, arcBounds.centerY() - mBottleHeight / 2.0f,
141 |                 arcBounds.centerX() + mBottleWidth / 2.0f, arcBounds.centerY() + mBottleHeight / 2.0f);
142 |         //compute pipe body bounds
143 |         mWaterBounds.set(mBottleBounds.left + mStrokeWidth * 1.5f, mBottleBounds.top + mWaterLowestPointToBottleneckDistance,
144 |                 mBottleBounds.right - mStrokeWidth * 1.5f, mBottleBounds.bottom - mStrokeWidth * 1.5f);
145 | 
146 |         //compute wave progress
147 |         float totalWaveProgress = renderProgress * mWaveCount;
148 |         float currentWaveProgress = totalWaveProgress - ((int) totalWaveProgress);
149 | 
150 |         if (currentWaveProgress > 0.5f) {
151 |             mProgress = 1.0f - MATERIAL_INTERPOLATOR.getInterpolation((currentWaveProgress - 0.5f) * 2.0f);
152 |         } else {
153 |             mProgress = MATERIAL_INTERPOLATOR.getInterpolation(currentWaveProgress * 2.0f);
154 |         }
155 | 
156 |         //init water drop holders
157 |         if (mWaterDropHolders.isEmpty()) {
158 |             initWaterDropHolders(mBottleBounds, mWaterBounds);
159 |         }
160 | 
161 |         //compute the location of these water drops
162 |         for (WaterDropHolder waterDropHolder : mWaterDropHolders) {
163 |             if (waterDropHolder.mDelayDuration < renderProgress
164 |                     && waterDropHolder.mDelayDuration + waterDropHolder.mDuration > renderProgress) {
165 |                 float riseProgress = (renderProgress - waterDropHolder.mDelayDuration) / waterDropHolder.mDuration;
166 |                 riseProgress = riseProgress < 0.5f ? riseProgress * 2.0f : 1.0f - (riseProgress - 0.5f) * 2.0f;
167 |                 waterDropHolder.mCurrentY = waterDropHolder.mInitY -
168 |                         MATERIAL_INTERPOLATOR.getInterpolation(riseProgress) * waterDropHolder.mRiseHeight;
169 |                 waterDropHolder.mNeedDraw = true;
170 |             } else {
171 |                 waterDropHolder.mNeedDraw = false;
172 |             }
173 |         }
174 | 
175 |         //measure loading text
176 |         mPaint.setTextSize(mTextSize);
177 |         mPaint.getTextBounds(LOADING_TEXT, 0, LOADING_TEXT.length(), mLoadingBounds);
178 |     }
179 | 
180 |     private Path createBottlePath(RectF bottleRect) {
181 |         float bottleneckWidth = bottleRect.width() * 0.3f;
182 |         float bottleneckHeight = bottleRect.height() * 0.415f;
183 |         float bottleneckDecorationWidth = bottleneckWidth * 1.1f;
184 |         float bottleneckDecorationHeight = bottleneckHeight * 0.167f;
185 | 
186 |         Path path = new Path();
187 |         //draw the left side of the bottleneck decoration
188 |         path.moveTo(bottleRect.centerX() - bottleneckDecorationWidth * 0.5f, bottleRect.top);
189 |         path.quadTo(bottleRect.centerX() - bottleneckDecorationWidth * 0.5f - bottleneckWidth * 0.15f, bottleRect.top + bottleneckDecorationHeight * 0.5f,
190 |                 bottleRect.centerX() - bottleneckWidth * 0.5f, bottleRect.top + bottleneckDecorationHeight);
191 |         path.lineTo(bottleRect.centerX() - bottleneckWidth * 0.5f, bottleRect.top + bottleneckHeight);
192 | 
193 |         //draw the left side of the bottle's body
194 |         float radius = (bottleRect.width() - mStrokeWidth) / 2.0f;
195 |         float centerY = bottleRect.bottom - 0.86f * radius;
196 |         RectF bodyRect = new RectF(bottleRect.left, centerY - radius, bottleRect.right, centerY + radius);
197 |         path.addArc(bodyRect, 255, -135);
198 | 
199 |         //draw the bottom of the bottle
200 |         float bottleBottomWidth = bottleRect.width() / 2.0f;
201 |         path.lineTo(bottleRect.centerX() - bottleBottomWidth / 2.0f, bottleRect.bottom);
202 |         path.lineTo(bottleRect.centerX() + bottleBottomWidth / 2.0f, bottleRect.bottom);
203 | 
204 |         //draw the right side of the bottle's body
205 |         path.addArc(bodyRect, 60, -135);
206 | 
207 |         //draw the right side of the bottleneck decoration
208 |         path.lineTo(bottleRect.centerX() + bottleneckWidth * 0.5f, bottleRect.top + bottleneckDecorationHeight);
209 |         path.quadTo(bottleRect.centerX() + bottleneckDecorationWidth * 0.5f + bottleneckWidth * 0.15f, bottleRect.top + bottleneckDecorationHeight * 0.5f,
210 |                 bottleRect.centerX() + bottleneckDecorationWidth * 0.5f, bottleRect.top);
211 | 
212 |         return path;
213 |     }
214 | 
215 |     private Path createWaterPath(RectF waterRect, float progress) {
216 |         Path path = new Path();
217 | 
218 |         path.moveTo(waterRect.left, waterRect.top);
219 | 
220 |         //Similar to the way draw the bottle's bottom sides
221 |         float radius = (waterRect.width() - mStrokeWidth) / 2.0f;
222 |         float centerY = waterRect.bottom - 0.86f * radius;
223 |         float bottleBottomWidth = waterRect.width() / 2.0f;
224 |         RectF bodyRect = new RectF(waterRect.left, centerY - radius, waterRect.right, centerY + radius);
225 | 
226 |         path.addArc(bodyRect, 187.5f, -67.5f);
227 |         path.lineTo(waterRect.centerX() - bottleBottomWidth / 2.0f, waterRect.bottom);
228 |         path.lineTo(waterRect.centerX() + bottleBottomWidth / 2.0f, waterRect.bottom);
229 |         path.addArc(bodyRect, 60, -67.5f);
230 | 
231 |         //draw the water waves
232 |         float cubicXChangeSize = waterRect.width() * 0.35f * progress;
233 |         float cubicYChangeSize = waterRect.height() * 1.2f * progress;
234 | 
235 |         path.cubicTo(waterRect.left + waterRect.width() * 0.80f - cubicXChangeSize, waterRect.top - waterRect.height() * 1.2f + cubicYChangeSize,
236 |                 waterRect.left + waterRect.width() * 0.55f - cubicXChangeSize, waterRect.top - cubicYChangeSize,
237 |                 waterRect.left, waterRect.top - mStrokeWidth / 2.0f);
238 | 
239 |         path.lineTo(waterRect.left, waterRect.top);
240 | 
241 |         return path;
242 |     }
243 | 
244 |     private void initWaterDropHolders(RectF bottleRect, RectF waterRect) {
245 |         float bottleRadius = bottleRect.width() / 2.0f;
246 |         float lowestWaterPointY = waterRect.top;
247 |         float twoSidesInterval = 0.2f * bottleRect.width();
248 |         float atLeastDelayDuration = 0.1f;
249 | 
250 |         float unitDuration = 0.1f;
251 |         float delayDurationRange = 0.6f;
252 |         int radiusRandomRange = MAX_WATER_DROP_RADIUS - MIN_WATER_DROP_RADIUS;
253 |         float currentXRandomRange = bottleRect.width() * 0.6f;
254 | 
255 |         for (int i = 0; i < DEFAULT_WATER_DROP_COUNT; i++) {
256 |             WaterDropHolder waterDropHolder = new WaterDropHolder();
257 |             waterDropHolder.mRadius = MIN_WATER_DROP_RADIUS + mRandom.nextInt(radiusRandomRange);
258 |             waterDropHolder.mInitX = bottleRect.left + twoSidesInterval + mRandom.nextFloat() * currentXRandomRange;
259 |             waterDropHolder.mInitY = lowestWaterPointY + waterDropHolder.mRadius / 2.0f;
260 |             waterDropHolder.mRiseHeight = getMaxRiseHeight(bottleRadius, waterDropHolder.mRadius, waterDropHolder.mInitX - bottleRect.left)
261 |                     * (0.2f + 0.8f * mRandom.nextFloat());
262 |             waterDropHolder.mDelayDuration = atLeastDelayDuration + mRandom.nextFloat() * delayDurationRange;
263 |             waterDropHolder.mDuration = waterDropHolder.mRiseHeight / bottleRadius * unitDuration;
264 | 
265 |             mWaterDropHolders.add(waterDropHolder);
266 |         }
267 |     }
268 | 
269 |     private float getMaxRiseHeight(float bottleRadius, float waterDropRadius, float currentX) {
270 |         float coordinateX = currentX - bottleRadius;
271 |         float bottleneckRadius = bottleRadius * 0.3f;
272 |         if (coordinateX - waterDropRadius > -bottleneckRadius
273 |                 && coordinateX + waterDropRadius < bottleneckRadius) {
274 |             return bottleRadius * 2.0f;
275 |         }
276 | 
277 |         return (float) (Math.sqrt(Math.pow(bottleRadius, 2.0f) - Math.pow(coordinateX, 2.0f)) - waterDropRadius);
278 |     }
279 | 
280 |     @Override
281 |     protected void setAlpha(int alpha) {
282 |         mPaint.setAlpha(alpha);
283 | 
284 |     }
285 | 
286 |     @Override
287 |     protected void setColorFilter(ColorFilter cf) {
288 |         mPaint.setColorFilter(cf);
289 | 
290 |     }
291 | 
292 |     @Override
293 |     protected void reset() {
294 |     }
295 | 
296 |     private class WaterDropHolder {
297 |         public float mCurrentY;
298 | 
299 |         public float mInitX;
300 |         public float mInitY;
301 |         public float mDelayDuration;
302 |         public float mRiseHeight;
303 | 
304 |         public float mRadius;
305 |         public float mDuration;
306 | 
307 |         public boolean mNeedDraw;
308 |     }
309 | 
310 |     public static class Builder {
311 |         private Context mContext;
312 | 
313 |         public Builder(Context mContext) {
314 |             this.mContext = mContext;
315 |         }
316 | 
317 |         public WaterBottleLoadingRenderer build() {
318 |             WaterBottleLoadingRenderer loadingRenderer = new WaterBottleLoadingRenderer(mContext);
319 |             return loadingRenderer;
320 |         }
321 |     }
322 | }


--------------------------------------------------------------------------------
/library/src/main/java/app/dinus/com/loadingdrawable/render/shapechange/CoolWaitLoadingRenderer.java:
--------------------------------------------------------------------------------
  1 | package app.dinus.com.loadingdrawable.render.shapechange;
  2 | 
  3 | import android.content.Context;
  4 | import android.graphics.Canvas;
  5 | import android.graphics.Color;
  6 | import android.graphics.ColorFilter;
  7 | import android.graphics.Paint;
  8 | import android.graphics.Path;
  9 | import android.graphics.PathMeasure;
 10 | import android.graphics.Rect;
 11 | import android.graphics.RectF;
 12 | import android.util.DisplayMetrics;
 13 | import android.view.animation.AccelerateDecelerateInterpolator;
 14 | import android.view.animation.AccelerateInterpolator;
 15 | import android.view.animation.DecelerateInterpolator;
 16 | import android.view.animation.Interpolator;
 17 | 
 18 | import app.dinus.com.loadingdrawable.DensityUtil;
 19 | import app.dinus.com.loadingdrawable.render.LoadingRenderer;
 20 | 
 21 | public class CoolWaitLoadingRenderer extends LoadingRenderer {
 22 |     private final Interpolator ACCELERATE_INTERPOLATOR08 = new AccelerateInterpolator(0.8f);
 23 |     private final Interpolator ACCELERATE_INTERPOLATOR10 = new AccelerateInterpolator(1.0f);
 24 |     private final Interpolator ACCELERATE_INTERPOLATOR15 = new AccelerateInterpolator(1.5f);
 25 | 
 26 |     private final Interpolator DECELERATE_INTERPOLATOR03 = new DecelerateInterpolator(0.3f);
 27 |     private final Interpolator DECELERATE_INTERPOLATOR05 = new DecelerateInterpolator(0.5f);
 28 |     private final Interpolator DECELERATE_INTERPOLATOR08 = new DecelerateInterpolator(0.8f);
 29 |     private final Interpolator DECELERATE_INTERPOLATOR10 = new DecelerateInterpolator(1.0f);
 30 | 
 31 |     private static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
 32 | 
 33 |     private final float DEFAULT_WIDTH = 200.0f;
 34 |     private final float DEFAULT_HEIGHT = 150.0f;
 35 |     private final float DEFAULT_STROKE_WIDTH = 8.0f;
 36 |     private final float WAIT_CIRCLE_RADIUS = 50.0f;
 37 | 
 38 |     private static final float WAIT_TRIM_DURATION_OFFSET = 0.5f;
 39 |     private static final float END_TRIM_DURATION_OFFSET = 1.0f;
 40 | 
 41 |     private final long ANIMATION_DURATION = 2222;
 42 | 
 43 |     private final Paint mPaint = new Paint();
 44 | 
 45 |     private final Path mWaitPath = new Path();
 46 |     private final Path mCurrentTopWaitPath = new Path();
 47 |     private final Path mCurrentMiddleWaitPath = new Path();
 48 |     private final Path mCurrentBottomWaitPath = new Path();
 49 |     private final PathMeasure mWaitPathMeasure = new PathMeasure();
 50 | 
 51 |     private final RectF mCurrentBounds = new RectF();
 52 | 
 53 |     private float mStrokeWidth;
 54 |     private float mWaitCircleRadius;
 55 |     private float mOriginEndDistance;
 56 |     private float mOriginStartDistance;
 57 |     private float mWaitPathLength;
 58 | 
 59 |     private int mTopColor;
 60 |     private int mMiddleColor;
 61 |     private int mBottomColor;
 62 | 
 63 |     private CoolWaitLoadingRenderer(Context context) {
 64 |         super(context);
 65 |         init(context);
 66 |         setupPaint();
 67 |     }
 68 | 
 69 |     private void init(Context context) {
 70 |         mWidth = DensityUtil.dip2px(context, DEFAULT_WIDTH);
 71 |         mHeight = DensityUtil.dip2px(context, DEFAULT_HEIGHT);
 72 |         mStrokeWidth = DensityUtil.dip2px(context, DEFAULT_STROKE_WIDTH);
 73 |         mWaitCircleRadius = DensityUtil.dip2px(context, WAIT_CIRCLE_RADIUS);
 74 | 
 75 |         mTopColor = Color.WHITE;
 76 |         mMiddleColor = Color.parseColor("#FFF3C742");
 77 |         mBottomColor = Color.parseColor("#FF89CC59");
 78 | 
 79 |         mDuration = ANIMATION_DURATION;
 80 |     }
 81 | 
 82 |     private void setupPaint() {
 83 |         mPaint.setAntiAlias(true);
 84 |         mPaint.setStrokeWidth(mStrokeWidth);
 85 |         mPaint.setStyle(Paint.Style.STROKE);
 86 |         mPaint.setStrokeJoin(Paint.Join.ROUND);
 87 |         mPaint.setStrokeCap(Paint.Cap.ROUND);
 88 |     }
 89 | 
 90 |     @Override
 91 |     protected void draw(Canvas canvas, Rect bounds) {
 92 |         int saveCount = canvas.save();
 93 |         RectF arcBounds = mCurrentBounds;
 94 |         arcBounds.set(bounds);
 95 | 
 96 |         mPaint.setColor(mBottomColor);
 97 |         canvas.drawPath(mCurrentBottomWaitPath, mPaint);
 98 | 
 99 |         mPaint.setColor(mMiddleColor);
100 |         canvas.drawPath(mCurrentMiddleWaitPath, mPaint);
101 | 
102 |         mPaint.setColor(mTopColor);
103 |         canvas.drawPath(mCurrentTopWaitPath, mPaint);
104 | 
105 |         canvas.restoreToCount(saveCount);
106 |     }
107 | 
108 |     private Path createWaitPath(RectF bounds) {
109 |         Path path = new Path();
110 |         //create circle
111 |         path.moveTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY());
112 | 
113 |         //create w
114 |         path.cubicTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius * 0.5f,
115 |                 bounds.centerX() + mWaitCircleRadius * 0.3f, bounds.centerY() - mWaitCircleRadius,
116 |                 bounds.centerX() - mWaitCircleRadius * 0.35f, bounds.centerY() + mWaitCircleRadius * 0.5f);
117 |         path.quadTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
118 |                 bounds.centerX() + mWaitCircleRadius * 0.05f, bounds.centerY() + mWaitCircleRadius * 0.5f);
119 |         path.lineTo(bounds.centerX() + mWaitCircleRadius * 0.75f, bounds.centerY() - mWaitCircleRadius * 0.2f);
120 | 
121 |         path.cubicTo(bounds.centerX(), bounds.centerY() + mWaitCircleRadius * 1f,
122 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius * 0.4f,
123 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY());
124 | 
125 |         //create arc
126 |         path.arcTo(new RectF(bounds.centerX() - mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
127 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius), 0, -359);
128 |         path.arcTo(new RectF(bounds.centerX() - mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
129 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius), 1, -359);
130 |         path.arcTo(new RectF(bounds.centerX() - mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
131 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius), 2, -2);
132 |         //create w
133 |         path.cubicTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius * 0.5f,
134 |                 bounds.centerX() + mWaitCircleRadius * 0.3f, bounds.centerY() - mWaitCircleRadius,
135 |                 bounds.centerX() - mWaitCircleRadius * 0.35f, bounds.centerY() + mWaitCircleRadius * 0.5f);
136 |         path.quadTo(bounds.centerX() + mWaitCircleRadius, bounds.centerY() - mWaitCircleRadius,
137 |                 bounds.centerX() + mWaitCircleRadius * 0.05f, bounds.centerY() + mWaitCircleRadius * 0.5f);
138 |         path.lineTo(bounds.centerX() + mWaitCircleRadius * 0.75f, bounds.centerY() - mWaitCircleRadius * 0.2f);
139 | 
140 |         path.cubicTo(bounds.centerX(), bounds.centerY() + mWaitCircleRadius * 1f,
141 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY() + mWaitCircleRadius * 0.4f,
142 |                 bounds.centerX() + mWaitCircleRadius, bounds.centerY());
143 | 
144 |         return path;
145 |     }
146 | 
147 |     @Override
148 |     protected void computeRender(float renderProgress) {
149 |         if (mCurrentBounds.isEmpty()) {
150 |             return;
151 |         }
152 | 
153 |         if (mWaitPath.isEmpty()) {
154 |             mWaitPath.set(createWaitPath(mCurrentBounds));
155 |             mWaitPathMeasure.setPath(mWaitPath, false);
156 |             mWaitPathLength = mWaitPathMeasure.getLength();
157 | 
158 |             mOriginEndDistance = mWaitPathLength * 0.255f;
159 |             mOriginStartDistance = mWaitPathLength * 0.045f;
160 |         }
161 | 
162 |         mCurrentTopWaitPath.reset();
163 |         mCurrentMiddleWaitPath.reset();
164 |         mCurrentBottomWaitPath.reset();
165 | 
166 |         //draw the first half : top
167 |         if (renderProgress <= WAIT_TRIM_DURATION_OFFSET) {
168 |             float topTrimProgress = ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation(renderProgress / WAIT_TRIM_DURATION_OFFSET);
169 |             float topEndDistance = mOriginEndDistance + mWaitPathLength * 0.3f * topTrimProgress;
170 |             float topStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f * topTrimProgress;
171 |             mWaitPathMeasure.getSegment(topStartDistance, topEndDistance, mCurrentTopWaitPath, true);
172 |         }
173 | 
174 |         //draw the first half : middle
175 |         if (renderProgress > 0.02f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET * 0.75f) {
176 |             float middleStartTrimProgress = ACCELERATE_INTERPOLATOR10.getInterpolation((renderProgress - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.73f));
177 |             float middleEndTrimProgress = DECELERATE_INTERPOLATOR08.getInterpolation((renderProgress - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.73f));
178 | 
179 |             float middleEndDistance = mOriginStartDistance + mWaitPathLength * 0.42f * middleEndTrimProgress;
180 |             float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.42f * middleStartTrimProgress;
181 |             mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
182 |         }
183 | 
184 |         //draw the first half : bottom
185 |         if (renderProgress > 0.04f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET * 0.75f) {
186 |             float bottomStartTrimProgress = ACCELERATE_INTERPOLATOR15.getInterpolation((renderProgress - 0.04f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.71f));
187 |             float bottomEndTrimProgress = DECELERATE_INTERPOLATOR05.getInterpolation((renderProgress - 0.04f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.71f));
188 | 
189 |             float bottomEndDistance = mOriginStartDistance + mWaitPathLength * 0.42f * bottomEndTrimProgress;
190 |             float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.42f * bottomStartTrimProgress;
191 |             mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
192 |         }
193 | 
194 |         //draw the last half : top
195 |         if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > WAIT_TRIM_DURATION_OFFSET) {
196 |             float trimProgress = ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - WAIT_TRIM_DURATION_OFFSET));
197 |             float topEndDistance = mOriginEndDistance + mWaitPathLength * 0.3f + mWaitPathLength * 0.45f * trimProgress;
198 |             float topStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.27f * trimProgress;
199 |             mWaitPathMeasure.getSegment(topStartDistance, topEndDistance, mCurrentTopWaitPath, true);
200 |         }
201 | 
202 |         //draw the last half : middle
203 |         if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.02f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET + WAIT_TRIM_DURATION_OFFSET * 0.62f) {
204 |             float middleStartTrimProgress = ACCELERATE_INTERPOLATOR08.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
205 |             float middleEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
206 | 
207 |             float middleEndDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.20f * middleEndTrimProgress;
208 |             float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.10f * middleStartTrimProgress;
209 |             mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
210 |         }
211 | 
212 |         if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.62f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= END_TRIM_DURATION_OFFSET) {
213 |             float middleStartTrimProgress = DECELERATE_INTERPOLATOR10.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.62f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.38f));
214 |             float middleEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.62f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.38f));
215 | 
216 |             float middleEndDistance = mOriginStartDistance + mWaitPathLength * 0.68f + mWaitPathLength * 0.325f * middleEndTrimProgress;
217 |             float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.58f + mWaitPathLength * 0.17f * middleStartTrimProgress;
218 |             mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
219 |         }
220 | 
221 |         //draw the last half : bottom
222 |         if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.10f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET + WAIT_TRIM_DURATION_OFFSET * 0.70f) {
223 |             float bottomStartTrimProgress = ACCELERATE_INTERPOLATOR15.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.10f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
224 |             float bottomEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.10f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
225 | 
226 |             float bottomEndDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.20f * bottomEndTrimProgress;
227 |             float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.10f * bottomStartTrimProgress;
228 |             mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
229 |         }
230 | 
231 |         if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.70f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= END_TRIM_DURATION_OFFSET) {
232 |             float bottomStartTrimProgress = DECELERATE_INTERPOLATOR05.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.70f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.30f));
233 |             float bottomEndTrimProgress = DECELERATE_INTERPOLATOR03.getInterpolation((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.70f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.30f));
234 | 
235 |             float bottomEndDistance = mOriginStartDistance + mWaitPathLength * 0.68f + mWaitPathLength * 0.325f * bottomEndTrimProgress;
236 |             float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.58f + mWaitPathLength * 0.17f * bottomStartTrimProgress;
237 |             mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
238 |         }
239 | 
240 |     }
241 | 
242 |     @Override
243 |     protected void setAlpha(int alpha) {
244 |         mPaint.setAlpha(alpha);
245 | 
246 |     }
247 | 
248 |     @Override
249 |     protected void setColorFilter(ColorFilter cf) {
250 |         mPaint.setColorFilter(cf);
251 | 
252 |     }
253 | 
254 |     @Override
255 |     protected void reset() {
256 |     }
257 | 
258 |     public static class Builder {
259 |         private Context mContext;
260 | 
261 |         public Builder(Context mContext) {
262 |             this.mContext = mContext;
263 |         }
264 | 
265 |         public CoolWaitLoadingRenderer build() {
266 |             CoolWaitLoadingRenderer loadingRenderer = new CoolWaitLoadingRenderer(mContext);
267 |             return loadingRenderer;
268 |         }
269 |     }
270 | }
271 | 


--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/ic_eletric_fan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/library/src/main/res/drawable-xhdpi/ic_eletric_fan.png


--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/ic_leaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/library/src/main/res/drawable-xhdpi/ic_leaf.png


--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/ic_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinuscxj/LoadingDrawable/db4ceb6b90cabee18f33f89e04f6bb3a7a0b1f64/library/src/main/res/drawable-xhdpi/ic_loading.png


--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <resources>
 3 |     <declare-styleable name="LoadingView">
 4 |         <attr name="loading_renderer">
 5 |             <!--circle rotate-->
 6 |             <enum name="MaterialLoadingRenderer" value="0"/>
 7 |             <enum name="LevelLoadingRenderer" value="1"/>
 8 |             <enum name="WhorlLoadingRenderer" value="2"/>
 9 |             <enum name="GearLoadingRenderer" value="3"/>
10 |             <!--circle jump-->
11 |             <enum name="SwapLoadingRenderer" value="4"/>
12 |             <enum name="GuardLoadingRenderer" value="5"/>
13 |             <enum name="DanceLoadingRenderer" value="6"/>
14 |             <enum name="CollisionLoadingRenderer" value="7"/>
15 |             <!--Scenery-->
16 |             <enum name="DayNightLoadingRenderer" value="8"/>
17 |             <enum name="ElectricFanLoadingRenderer" value="9"/>
18 |             <!--Animal-->
19 |             <enum name="FishLoadingRenderer" value="10"/>
20 |             <enum name="GhostsEyeLoadingRenderer" value="11"/>
21 |             <!--Goods-->
22 |             <enum name="BalloonLoadingRenderer" value="12"/>
23 |             <enum name="WaterBottleLoadingRenderer" value="13"/>
24 |             <!--ShapeChange-->
25 |             <enum name="CircleBroodLoadingRenderer" value="14"/>
26 |             <enum name="CoolWaitLoadingRenderer" value="15"/>
27 |         </attr>
28 |     </declare-styleable>
29 | </resources>


--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 | <resources>
2 |     <string name="app_name">Library</string>
3 | </resources>
4 | 


--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 | 


--------------------------------------------------------------------------------