├── .gitignore ├── Art ├── loadingTest.gif ├── logo_512.png ├── model.gif └── title.png ├── LICENSE ├── Library ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── slibrary_leafloading.iml └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── sloop │ │ └── view │ │ ├── loading │ │ └── LeafLoading.java │ │ └── utils │ │ └── UiUtils.java │ └── res │ └── drawable-xxhdpi │ ├── fan_xxhdpi.png │ ├── leaf_xxhdpi.png │ └── outer_xxhdpi.png ├── README.md └── Resources ├── fan.png ├── leaf.png └── outer.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | -------------------------------------------------------------------------------- /Art/loadingTest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Art/loadingTest.gif -------------------------------------------------------------------------------- /Art/logo_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Art/logo_512.png -------------------------------------------------------------------------------- /Art/model.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Art/model.gif -------------------------------------------------------------------------------- /Art/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Art/title.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 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 | testCompile 'junit:junit:4.12' 24 | compile 'com.android.support:appcompat-v7:23.0.1' 25 | } 26 | -------------------------------------------------------------------------------- /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 D:\Program\Android\SDK/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/slibrary_leafloading.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Library/src/main/java/com/sloop/view/loading/LeafLoading.java: -------------------------------------------------------------------------------- 1 | package com.sloop.view.loading; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Matrix; 8 | import android.graphics.Paint; 9 | import android.graphics.Rect; 10 | import android.graphics.RectF; 11 | import android.graphics.drawable.BitmapDrawable; 12 | import android.util.AttributeSet; 13 | import android.view.ContextThemeWrapper; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | 17 | import com.sloop.view.utils.UiUtils; 18 | 19 | import java.util.LinkedList; 20 | import java.util.List; 21 | import java.util.Random; 22 | 23 | /** 24 | * 一个叶子飞旋的Loading核心类 25 | * 参考资料:http://blog.csdn.net/tianjian4592/article/details/44538605 26 | * Author: Sloop 27 | * Version: v1.0 28 | * Date: 2015/12/9 29 | * 34 | */ 35 | public class LeafLoading extends View { 36 | private static final String TAG = "SloopLeafLoading"; 37 | 38 | private int mDefaultWidth = 300; //默认宽高 39 | private int mDefaultHeight = 60; 40 | 41 | private static final int COLOR_WHITE = 0xfffce094; //淡白色 42 | private static final int COLOR_ORANGE = 0xffffa800; //橘黄色 43 | 44 | private static final int MIDDLE_AMPLITUDE = 13; //中等振幅大小 45 | private static final int AMPLITUDE_DISPARITY = 5; //不同振幅之间的振幅差 46 | 47 | private static final int TOTAL_PROGRESS = 100; //默认进度最大值 48 | private static final long LEAF_FLOAT_TIME = 3000; //叶子飘动一个周期花费的时间 49 | private static final long LEAF_ROTATE_TIME = 2000; //叶子旋转一个周期花费的时间 50 | 51 | private int mLeftMargin, mRightMargin; //用于控制进度条外边距 52 | 53 | private int mMiddleAmplitude = MIDDLE_AMPLITUDE; //中等振幅大小 54 | private int mAmplitudeDisparity = AMPLITUDE_DISPARITY; //振幅差 55 | 56 | private long mLeafFloatTime = LEAF_FLOAT_TIME; //叶子飘动一个周期花费的时间 57 | private long mLeafRotateTime = LEAF_ROTATE_TIME; //叶子旋转一个周期花费的时间 58 | 59 | private Paint mBitmapPaint, mWhitePaint, mOrangePaint; //画笔 60 | private RectF mWhiteRectF, mOrangeRectF, mArcRectF; //区域 61 | 62 | private int mCurrentProgress; //当前进度 63 | private int mProgressWidth; //当前进度条的宽度 64 | private int mCurrentProgressPosition; //当前所绘制部分进度条的位置 65 | private int mArcRadius; //弧形半径 66 | 67 | private int mArcRightLocation; //arc的右上角x坐标,也是矩形x坐标的起始点 68 | private Resources mResources; //资源 69 | 70 | //叶子相关 71 | private int mAddTime; //用于控制随机增加的时间,防止抱团 72 | private Bitmap mLeafBitmap; //叶子图片 73 | private List mLeafInfos; //叶子信息 74 | private LeafFactory mLeafFactory; //用于产生叶子信息 75 | private float mLeafBitmapWidth, mLeafBitmapHeight; //叶子图片宽 76 | private float mLeafPaintWidth, mLeafPaintHeight; //叶子绘制时宽高 77 | //外边框相关 78 | private Bitmap mOuterBitmap; 79 | private int mOuterWidth, mOuterHeight; 80 | private Rect mOuterSrcRect, mOuterDestRect; 81 | //风扇相关 82 | private Fan mFan; //风扇对象 83 | private static final long FAN_SPEED = 6; //风扇默认旋转速率级别(越大越快) 84 | private static final long FAN_SPEED_DISPARITY = 150; //风扇相邻速率级别之间的差值 85 | private Bitmap mFanBitmap; 86 | private float mFanBitmapSideLength; //风扇图片边长 87 | private float mFanPaintSideLength; //风扇绘制时边长 88 | private long mFanSpeed = FAN_SPEED; //风扇旋转速率级别(1-10) 89 | 90 | //回调接口 91 | private ProgressChangedListener mListener; 92 | 93 | /** 94 | * 构造函数 95 | * 96 | * @param context 上下文 97 | */ 98 | public LeafLoading(Context context) { 99 | super(context); 100 | init((ContextThemeWrapper) context); 101 | } 102 | 103 | /** 104 | * 构造函数 105 | * 106 | * @param context 上下文 107 | * @param attrs 参数集 108 | */ 109 | public LeafLoading(Context context, AttributeSet attrs) { 110 | super(context, attrs); 111 | init((ContextThemeWrapper) context); 112 | } 113 | 114 | /** 115 | * 初始化 116 | * 117 | * @param context 上下文 118 | */ 119 | private void init(ContextThemeWrapper context) { 120 | initResources(context); 121 | initBitmap(); 122 | initPaint(); 123 | mLeafFactory = new LeafFactory(); 124 | mLeafInfos = mLeafFactory.generateLeafs(); 125 | mFan = new FanFactory().generateFan(); 126 | } 127 | 128 | /** 129 | * 初始化资源 130 | * 131 | * @param context 上下文 132 | */ 133 | private void initResources(ContextThemeWrapper context) { 134 | mResources = getResources(); 135 | mLeafFloatTime = LEAF_FLOAT_TIME; 136 | mLeafRotateTime = LEAF_ROTATE_TIME; 137 | mDefaultWidth = UiUtils.dip2px(context, mDefaultWidth); 138 | mDefaultHeight = UiUtils.dip2px(context, mDefaultHeight); 139 | } 140 | 141 | /** 142 | * 初始化Bitmap 143 | */ 144 | private void initBitmap() { 145 | mLeafBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.leaf_xxhdpi)).getBitmap(); 146 | mLeafBitmapWidth = mLeafBitmap.getWidth(); 147 | mLeafBitmapHeight = mLeafBitmap.getHeight(); 148 | 149 | mOuterBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.outer_xxhdpi)).getBitmap(); 150 | mOuterWidth = mOuterBitmap.getWidth(); 151 | mOuterHeight = mOuterBitmap.getHeight(); 152 | 153 | mFanBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.fan_xxhdpi)).getBitmap(); 154 | mFanBitmapSideLength = mFanBitmap.getWidth(); 155 | } 156 | 157 | /** 158 | * 初始化画笔 159 | */ 160 | private void initPaint() { 161 | mBitmapPaint = new Paint(); 162 | mBitmapPaint.setAntiAlias(true); 163 | mBitmapPaint.setDither(true); 164 | mBitmapPaint.setFilterBitmap(true); 165 | 166 | mWhitePaint = new Paint(); 167 | mWhitePaint.setAntiAlias(true); 168 | mWhitePaint.setColor(COLOR_WHITE); 169 | 170 | mOrangePaint = new Paint(); 171 | mOrangePaint.setAntiAlias(true); 172 | mOrangePaint.setColor(COLOR_ORANGE); 173 | } 174 | 175 | /** 176 | * 测量视图大小 177 | * 178 | * @param widthMeasureSpec 用户设置的宽度 179 | * @param heightMeasureSpec 用户设置的高度 180 | */ 181 | @Override 182 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 183 | ViewGroup.LayoutParams params = this.getLayoutParams(); 184 | //获取宽高测量值 185 | int measureWidth = getMeasureWidth(widthMeasureSpec, params); 186 | int measureHeight = getMeasureHeight(heightMeasureSpec, params); 187 | //按比例重置宽高 按小一边的取值 (宽 : 高 = 300 : 60) 188 | measureWidth = measureWidth > measureHeight * 5 ? measureHeight * 5 : measureWidth; 189 | measureHeight = measureWidth / 5; 190 | //自定义宽高 191 | setMeasuredDimension(measureWidth, measureHeight); 192 | } 193 | 194 | /** 195 | * 获取宽度测量值 196 | * 197 | * @param widthMeasureSpec 用户设置的宽度 198 | * @param params 布局参数 199 | * @return 实际宽度 200 | */ 201 | private int getMeasureWidth(int widthMeasureSpec, ViewGroup.LayoutParams params) { 202 | int measureWidth; 203 | if (params.width == ViewGroup.LayoutParams.MATCH_PARENT) { 204 | measureWidth = MeasureSpec.getSize(widthMeasureSpec); 205 | } else if (params.width >= 0) { 206 | measureWidth = params.width; 207 | } else { 208 | measureWidth = mDefaultWidth; 209 | } 210 | return measureWidth; 211 | } 212 | 213 | /** 214 | * 获取测量的高度值 215 | * 216 | * @param heightMeasureSpec 用户设置的高度 217 | * @param params 布局参数 218 | * @return 实际的高度 219 | */ 220 | private int getMeasureHeight(int heightMeasureSpec, ViewGroup.LayoutParams params) { 221 | int measureHeight; 222 | if (params.height == ViewGroup.LayoutParams.MATCH_PARENT) { 223 | measureHeight = MeasureSpec.getSize(heightMeasureSpec); 224 | } else if (params.height >= 0) { 225 | measureHeight = params.height; 226 | } else { 227 | measureHeight = mDefaultHeight; 228 | } 229 | return measureHeight; 230 | } 231 | 232 | 233 | /** 234 | * 在视图大小确定后的回调函数 235 | * 进行参数初始化(各个数值之间存在比例关系,详情请见说明文件) 236 | * 237 | * @param w 当前宽度 238 | * @param h 当前高度 239 | * @param oldw 上一次宽度 240 | * @param oldh 上一次高度 241 | */ 242 | @Override 243 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 244 | int mTotalWidth = w > h * 5 ? h * 5 : w; 245 | int mTotalHeight = mTotalWidth / 5; 246 | 247 | mLeftMargin = mTotalHeight / 12; 248 | mRightMargin = mTotalHeight / 2; 249 | 250 | mProgressWidth = mTotalWidth - mLeftMargin - mRightMargin; 251 | mArcRadius = (mTotalHeight - 2 * mLeftMargin) / 2; 252 | 253 | mOuterSrcRect = new Rect(0, 0, mOuterWidth, mOuterHeight); 254 | mOuterDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight); 255 | 256 | //风扇相关 257 | mFan.x = (float) mTotalWidth * 49 / 60; 258 | mFan.y = (float) mTotalHeight / 12; 259 | mFanPaintSideLength = (float) mTotalWidth / 6; 260 | //叶子相关 261 | mLeafPaintWidth = (float) mTotalHeight / 3; 262 | mLeafPaintHeight = (float) mTotalHeight / 6; 263 | 264 | mWhiteRectF = new RectF(mLeftMargin + mCurrentProgressPosition, mLeftMargin, mTotalWidth 265 | - mRightMargin, mTotalHeight - mLeftMargin); 266 | mOrangeRectF = new RectF(mLeftMargin + mArcRadius, mLeftMargin, mCurrentProgressPosition, 267 | mTotalHeight - mLeftMargin); 268 | 269 | mArcRectF = new RectF(mLeftMargin, mLeftMargin, mLeftMargin + 2 * mArcRadius, 270 | mTotalHeight - mLeftMargin); 271 | mArcRightLocation = mLeftMargin + mArcRadius; 272 | } 273 | 274 | /** 275 | * 绘制内容 276 | * 277 | * @param canvas 画布 278 | */ 279 | @Override 280 | protected void onDraw(Canvas canvas) { 281 | super.onDraw(canvas); 282 | //绘制进度条和叶子(之所以把叶子放进进度条中绘制,主要是层级原因。) 283 | drawProgressAndLeafs(canvas); 284 | canvas.drawBitmap(mOuterBitmap, mOuterSrcRect, mOuterDestRect, mBitmapPaint); 285 | drawFan(canvas); 286 | postInvalidate(); 287 | } 288 | 289 | /** 290 | * 绘制进度条和叶子 291 | * 292 | * @param canvas 画布 293 | */ 294 | private void drawProgressAndLeafs(Canvas canvas) { 295 | if (mCurrentProgress >= TOTAL_PROGRESS) { 296 | mCurrentProgress = TOTAL_PROGRESS; 297 | } 298 | // mProgressWidth为进度条的宽度,根据当前进度算出进度条的位置 299 | mCurrentProgressPosition = mProgressWidth * mCurrentProgress / TOTAL_PROGRESS; 300 | 301 | /* Log.e(TAG, "当前进度值:" + mCurrentProgress + "\t||" + "当前进度位置:" + mCurrentProgressPosition + 302 | "\t||" + "圆弧半径:" + mArcRadius);*/ 303 | 304 | //在进度在圆弧之内 305 | if (mCurrentProgressPosition < mArcRadius) { 306 | // 1.绘制white ARC,绘制orange ARC 307 | // 2.绘制白色矩形 308 | 309 | //1.绘制白色ARC 310 | canvas.drawArc(mArcRectF, 90, 180, false, mWhitePaint); 311 | //2.绘制白色矩形 312 | mWhiteRectF.left = mArcRightLocation; 313 | canvas.drawRect(mWhiteRectF, mWhitePaint); 314 | //3.绘制叶子 315 | drawLeafs(canvas); 316 | //4.绘制棕色ARC 317 | //单边角度 318 | int angle = (int) Math.toDegrees(Math.acos((mArcRadius - mCurrentProgressPosition) / 319 | (float) mArcRadius)); 320 | //起始位置 321 | int startAngle = 180 - angle; 322 | //扫过的角度 323 | int sweepAngle = 2 * angle; 324 | canvas.drawArc(mArcRectF, startAngle, sweepAngle, false, mOrangePaint); 325 | } else { 326 | // 1.绘制white RECT 327 | // 2.绘制Orange ARC 328 | // 3.绘制orange RECT 329 | // 这个层级进行绘制能让叶子感觉是融入棕色进度条中 330 | 331 | //1.绘制white RECT 332 | mWhiteRectF.left = mCurrentProgressPosition; 333 | canvas.drawRect(mWhiteRectF, mWhitePaint); 334 | //绘制叶子 335 | drawLeafs(canvas); 336 | //绘制orange ARC 337 | canvas.drawArc(mArcRectF, 90, 180, false, mOrangePaint); 338 | //绘制orange RECT 339 | mOrangeRectF.left = mArcRightLocation; 340 | mOrangeRectF.right = mCurrentProgressPosition; 341 | canvas.drawRect(mOrangeRectF, mOrangePaint); 342 | } 343 | 344 | } 345 | 346 | /** 347 | * 绘制叶子 348 | * 349 | * @param canvas 画布 350 | */ 351 | private void drawLeafs(Canvas canvas) { 352 | mLeafRotateTime = mLeafRotateTime <= 0 ? LEAF_ROTATE_TIME : mLeafRotateTime; 353 | long currentTime = System.currentTimeMillis(); 354 | for (int i = 0; i < mLeafInfos.size(); i++) { 355 | Leaf leaf = mLeafInfos.get(i); 356 | if (currentTime > leaf.startTime && leaf.startTime != 0) { 357 | //绘制叶子 -- 根据叶子的类型和当前时间 358 | getLeafLocation(leaf, currentTime); 359 | // Log.i(TAG, "left.x = " + leaf.x + "--leaf.y=" + leaf.y + "Type:" + leaf.type); 360 | //根据时间计算旋转角度 361 | canvas.save(); 362 | //通过Matrix控制叶子的旋转 363 | Matrix matrix = new Matrix(); 364 | //缩放 365 | float scaleX = mLeafPaintWidth / mLeafBitmapWidth; 366 | float scaleY = mLeafPaintHeight / mLeafBitmapHeight; 367 | matrix.postScale(scaleX, scaleY); 368 | //位移 369 | float transX = mLeftMargin + leaf.x; 370 | float transY = mLeftMargin + leaf.y; 371 | matrix.postTranslate(transX, transY); 372 | //通过时间关联旋转角度,则可以直接通过修改LEAF_ROTATE_TIME调节叶子旋转快慢 373 | float rotateFraction = ((currentTime - leaf.startTime) % mLeafRotateTime) / 374 | (float) mLeafRotateTime; 375 | int angle = (int) (rotateFraction * 360); 376 | //根据叶子旋转方向确定叶子旋转的角度 377 | int rotate = leaf.rotateDirection == 0 ? angle + leaf.rotateAngle : -angle + leaf 378 | .rotateAngle; 379 | matrix.postRotate(rotate, transX + mLeafBitmapWidth / 2, transY + mLeafBitmapHeight / 2); 380 | canvas.drawBitmap(mLeafBitmap, matrix, mBitmapPaint); 381 | canvas.restore(); 382 | } else { 383 | continue; 384 | } 385 | } 386 | } 387 | 388 | /** 389 | * 获取当前坐标 390 | * 391 | * @param leaf 叶子对象 392 | * @param currentTime 当前时间 393 | */ 394 | private void getLeafLocation(Leaf leaf, long currentTime) { 395 | mLeafFloatTime = mLeafFloatTime <= 0 ? LEAF_FLOAT_TIME : mLeafFloatTime; 396 | long intervalTime = currentTime - leaf.startTime; 397 | 398 | if (intervalTime <= 0) { 399 | return; 400 | } else if (intervalTime > mLeafFloatTime) { 401 | leaf.startTime = System.currentTimeMillis() + new Random().nextInt((int) 402 | mLeafFloatTime); 403 | } 404 | // Log.e(TAG, "mLeafFloatTime = " + mLeafFloatTime + "--intervalTime=" + intervalTime); 405 | float fraction = (float) intervalTime / mLeafFloatTime; 406 | // Log.i(TAG, "mLeafFloatTime = " + mLeafFloatTime + "--fraction=" + fraction); 407 | leaf.x = mProgressWidth - mProgressWidth * fraction; 408 | leaf.y = getLeafLocationY(leaf); 409 | // Log.e(TAG, "mProgressWidth = " + mProgressWidth + "--fraction=" + fraction); 410 | } 411 | 412 | /** 413 | * 获取叶子的当前的Y坐标 414 | * 415 | * @param leaf 叶子对象 416 | * @return 当前y轴坐标 417 | */ 418 | private float getLeafLocationY(Leaf leaf) { 419 | // y= A sin(ωx+φ) + h 正弦函数 420 | float ω = (float) ((float) 2 * Math.PI / mProgressWidth); 421 | float A = mMiddleAmplitude; //默认中等振幅 422 | if (leaf.type == StartType.LITTLE) { //小振幅 = 中等振幅 - 振幅差 423 | A = mMiddleAmplitude - mAmplitudeDisparity; 424 | } else if (leaf.type == StartType.BIG) { //大振幅 = 中等振幅 + 振幅差 425 | A = mMiddleAmplitude + mAmplitudeDisparity; 426 | } 427 | float y = (float) (A * Math.sin(ω * leaf.x + leaf.φ) + mArcRadius * 2 / 3); 428 | return y; 429 | } 430 | 431 | /** 432 | * 绘制风扇 433 | * 434 | * @param canvas 画布 435 | */ 436 | private void drawFan(Canvas canvas) { 437 | long currentTime = System.currentTimeMillis(); 438 | long mFanRotateTime = (10 - mFanSpeed) * FAN_SPEED_DISPARITY + 500; 439 | // Log.e(TAG, "mFanRotateTime:" + mFanRotateTime); 440 | canvas.save(); 441 | //通过Matrix控制叶子的旋转 442 | Matrix matrix = new Matrix(); 443 | float transX = mFan.x; 444 | float transY = mFan.y; 445 | float scale = mFanPaintSideLength / mFanBitmapSideLength; //缩放比例 446 | matrix.postScale(scale, scale); //设置缩放 447 | matrix.postTranslate(transX, transY); //设置位移 448 | 449 | //通过时间关联旋转角度,则可以直接通过修mFan.speed调节叶子旋转快慢 450 | float rotateFraction = ((currentTime - mFan.startTime) % mFanRotateTime) / (float) mFanRotateTime; 451 | int angle = (int) (rotateFraction * 360); 452 | //根据叶子旋转方向确定叶子旋转的角度 453 | int rotate = mFan.rotateDirection == 0 ? angle + mFan.rotateAngle : -angle + mFan.rotateAngle; 454 | 455 | matrix.postRotate(rotate, transX + mFanPaintSideLength / 2, transY + mFanPaintSideLength / 2); 456 | canvas.drawBitmap(mFanBitmap, matrix, mBitmapPaint); 457 | canvas.restore(); 458 | } 459 | 460 | private class Fan { 461 | float x, y; //左上角坐标 462 | int rotateAngle; //旋转角度 463 | int rotateDirection; //旋转方向(0-顺时针 1-逆时针) 464 | long startTime; //起始时间(ms) 465 | } 466 | 467 | private class FanFactory { 468 | public Fan generateFan() { 469 | Fan fan = new Fan(); 470 | fan.startTime = System.currentTimeMillis(); 471 | fan.rotateDirection = -1; 472 | fan.rotateAngle = 0; 473 | return fan; 474 | } 475 | } 476 | 477 | private enum StartType { 478 | LITTLE, MIDDLE, BIG; 479 | } 480 | 481 | private class Leaf { 482 | float x, y; //在绘制部分的位置 483 | StartType type; //叶子飘动的振幅 484 | int rotateAngle; //旋转角度 485 | int rotateDirection; //旋转方向(0-顺时针 1-逆时针) 486 | long startTime; //起始时间(ms) 487 | int φ; //初相位 488 | } 489 | 490 | private class LeafFactory { 491 | private static final int MAX_LEAFS = 8; 492 | Random random = new Random(); 493 | 494 | //根据最大叶子数产生叶子 495 | public List generateLeafs() { 496 | return generateLeafs(MAX_LEAFS); 497 | } 498 | 499 | //根据传入叶子的数量产生叶子 500 | public List generateLeafs(int leafSize) { 501 | List leafs = new LinkedList<>(); 502 | for (int i = 0; i < leafSize; i++) { 503 | leafs.add(generateLeaf()); 504 | } 505 | return leafs; 506 | } 507 | 508 | //生成一个叶子信息 509 | public Leaf generateLeaf() { 510 | Leaf leaf = new Leaf(); 511 | //获取一个随机振幅类型 512 | int tempType = random.nextInt(3); 513 | StartType type = StartType.MIDDLE; 514 | switch (tempType) { 515 | case 0: 516 | type = StartType.LITTLE; 517 | break; 518 | case 1: 519 | break; 520 | case 2: 521 | type = StartType.BIG; 522 | break; 523 | } 524 | leaf.type = type; 525 | leaf.φ = random.nextInt(20); //添加初相位 526 | 527 | //叶子起始旋转角度 528 | leaf.rotateAngle = random.nextInt(360); 529 | //叶子旋转方向 530 | leaf.rotateDirection = random.nextInt(2); 531 | //为了产生交错感,让开始时间具有随机性 532 | mLeafFloatTime = mLeafFloatTime <= 0 ? LEAF_FLOAT_TIME : mLeafFloatTime; 533 | mAddTime += random.nextInt((int) (mLeafFloatTime * 2)); 534 | leaf.startTime = System.currentTimeMillis() + mAddTime; 535 | return leaf; 536 | } 537 | } 538 | 539 | /** 540 | * 设置中等振幅 541 | * 542 | * @param amplitude 中等振幅 543 | */ 544 | public void setMiddleAmplitude(int amplitude) { 545 | this.mMiddleAmplitude = amplitude; 546 | } 547 | 548 | /** 549 | * 设置振幅差 550 | * 551 | * @param disparity 振幅差 552 | */ 553 | public void setMplitudeDisparity(int disparity) { 554 | this.mAmplitudeDisparity = disparity; 555 | } 556 | 557 | /** 558 | * 获取中等振幅 559 | * 560 | * @return 中等振幅 561 | */ 562 | public int getMiddleAmplitude() { 563 | return mMiddleAmplitude; 564 | } 565 | 566 | /** 567 | * 获取振幅差 568 | * 569 | * @return 振幅差 570 | */ 571 | public int getMplitudeDisparity() { 572 | return mAmplitudeDisparity; 573 | } 574 | 575 | /** 576 | * 设置进度 577 | * 578 | * @param progress 当前进度 579 | */ 580 | public void setProgress(int progress) { 581 | this.mCurrentProgress = progress; 582 | //添加回调 583 | if (mListener != null) { 584 | mListener.onProgressChanged(progress, TOTAL_PROGRESS); 585 | } 586 | postInvalidate(); 587 | } 588 | 589 | /** 590 | * 设置叶子飘完一个周期所花的时间 591 | * 592 | * @param time 飘完一个周期所花的时间 593 | */ 594 | public void setLeafFloatTime(long time) { 595 | this.mLeafFloatTime = time; 596 | } 597 | 598 | /** 599 | * 设置叶子旋转一周所花的时间 600 | * 601 | * @param time 有叶子旋转周期 602 | */ 603 | public void setLeafRotateTime(long time) { 604 | this.mLeafRotateTime = time; 605 | } 606 | 607 | /** 608 | * 获取叶子飘完一个周期所花的时间 609 | * 610 | * @return 叶子飘完一个周期所花的时间 611 | */ 612 | public long getLeafFloatTime() { 613 | mLeafFloatTime = mLeafFloatTime == 0 ? LEAF_FLOAT_TIME : mLeafFloatTime; 614 | return mLeafFloatTime; 615 | } 616 | 617 | /** 618 | * 获取叶子旋转周期 619 | * 620 | * @return 叶子旋转一周所花的时间 621 | */ 622 | public long getLeafRotateTime() { 623 | mLeafRotateTime = mLeafRotateTime == 0 ? LEAF_ROTATE_TIME : mLeafRotateTime; 624 | return mLeafRotateTime; 625 | } 626 | 627 | /** 628 | * 获取风扇转速 629 | * 630 | * @return 风扇转速 631 | */ 632 | public long getmFanSpeed() { 633 | return mFanSpeed; 634 | } 635 | 636 | /** 637 | * 设置风扇转速 638 | * 639 | * @param mFanSpeed 风扇转速 640 | */ 641 | public void setmFanSpeed(long mFanSpeed) { 642 | if (mFanSpeed < 0) return; 643 | if (mFanSpeed > 10) return; 644 | this.mFanSpeed = mFanSpeed; 645 | } 646 | 647 | 648 | /** 649 | * 设置监听回调接口 650 | * 651 | * @param mListener 监听器 652 | */ 653 | public void setListener(ProgressChangedListener mListener) { 654 | this.mListener = mListener; 655 | } 656 | 657 | /** 658 | * 监听回调接口 659 | */ 660 | public interface ProgressChangedListener { 661 | void onProgressChanged(int currentProgress, int totalProgress); 662 | } 663 | } -------------------------------------------------------------------------------- /Library/src/main/java/com/sloop/view/utils/UiUtils.java: -------------------------------------------------------------------------------- 1 | package com.sloop.view.utils; 2 | 3 | import android.view.ContextThemeWrapper; 4 | 5 | /** 6 | * UI相关工具类 7 | * Author: Sloop 8 | * Version: v1.1 9 | * Date: 2015/12/9 10 | * 15 | */ 16 | public class UiUtils { 17 | 18 | /** 19 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 20 | * 21 | * @param context 上下文 22 | * @param dpValue dp 23 | * @return px 24 | */ 25 | public static int dip2px(ContextThemeWrapper context, float dpValue) { 26 | return (int) (dpValue * getDensity(context) + 0.5f); 27 | } 28 | 29 | /** 30 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 31 | * 32 | * @param context 上下文 33 | * @param pxValue px 34 | * @return dp 35 | */ 36 | public static int px2dip(ContextThemeWrapper context, float pxValue) { 37 | return (int) (pxValue / getDensity(context) + 0.5f); 38 | } 39 | 40 | /** 41 | * 获取当前手机的屏幕像素密度 42 | * 43 | * @param context 上下文 44 | * @return 像素密度 45 | */ 46 | public static float getDensity(ContextThemeWrapper context) { 47 | return context.getResources().getDisplayMetrics().density; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Library/src/main/res/drawable-xxhdpi/fan_xxhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Library/src/main/res/drawable-xxhdpi/fan_xxhdpi.png -------------------------------------------------------------------------------- /Library/src/main/res/drawable-xxhdpi/leaf_xxhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Library/src/main/res/drawable-xxhdpi/leaf_xxhdpi.png -------------------------------------------------------------------------------- /Library/src/main/res/drawable-xxhdpi/outer_xxhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Library/src/main/res/drawable-xxhdpi/outer_xxhdpi.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | title 2 | 3 | [![License](https://img.shields.io/badge/license-Apache%202-green.svg)](https://www.apache.org/licenses/LICENSE-2.0) 4 | [![Maven Central](https://img.shields.io/bintray/v/gcssloop/maven/leafloding.svg)](https://bintray.com/gcssloop/maven/leafloding/view) 5 | ### 作者微博: [@攻城师sloop](http://weibo.com/5459430586) 6 | ## 声明 7 | 由于该项目的创意来自于网络,并未寻找到原作者,所以可能存在侵权行为,使用前请慎重。如果您是该创意原作者,感觉侵犯了您的权利,可以在微博上联系我,侵删。 8 | 9 | 另外,该项目核心实现代码来参考了一位CSDN大神的博客:详戳->[【一个绚丽的loading动效分析与实现!】](http://blog.csdn.net/tianjian4592/article/details/44538605)。 10 | ``` 11 | 我在原有代码基础上进行了部分内容的修改,主要包括以下方面: 12 | 0.添加风扇部分绘制 13 | 1.修改了View测量逻辑 14 | 2.对视图大小进行了适配 15 | 3.添加了监听回调接口 16 | 17 | 目前仍存在的问题(待完善或者添加的功能) 18 | 0.绑定风扇转速 树叶数量 和进度快慢之间的关系 19 | 20 | 如果你对此有好的想法欢迎在Issues中提交。 21 | ``` 22 | 23 | 24 | ## 创意原型 25 | 26 | #### 原型效果图如下: 27 | ![LeafLoading](https://github.com/GcsSloop/LeafLoading/blob/master/Art/model.gif) 28 | #### 实现效果图如下: 29 | ![LeafLoadingDemo](https://github.com/GcsSloop/LeafLoading/blob/master/Art/loadingTest.gif) 30 | 31 | 目前实现了原型中百分之九十左右的内容。 32 | 33 | --- 34 | ## 如何使用 35 | ### 1.在布局文件中添加LeafLoding 36 | ``` xml 37 | 112 | 113 | -------------------------------------------------------------------------------- /Resources/fan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Resources/fan.png -------------------------------------------------------------------------------- /Resources/leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Resources/leaf.png -------------------------------------------------------------------------------- /Resources/outer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GcsSloop/LeafLoading/a25cb2fae7fac3014eced1efde3ac24fd7352b2b/Resources/outer.png --------------------------------------------------------------------------------