├── .gitattributes ├── .gitignore ├── Loadings ├── .gitignore ├── LoadingApp │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── zhaoli │ │ │ └── loadings │ │ │ ├── LoadingAdapter.java │ │ │ ├── MainActivity.java │ │ │ ├── TestActivity.java │ │ │ ├── interpolator │ │ │ └── DecelerateAccelerateInterpolator.java │ │ │ └── loadingViews │ │ │ ├── AnnulusWhirlLoading.java │ │ │ ├── BaseLoading.java │ │ │ ├── ConvertBallLoading.java │ │ │ ├── DownloadLoading.java │ │ │ ├── JumpWhirlGraphLoading.java │ │ │ ├── LineWhirlLoading1.java │ │ │ ├── LineWhirlLoading2.java │ │ │ ├── RotateSquareLoading.java │ │ │ ├── SegmentSquareLoading.java │ │ │ ├── SwingCollisionLoading.java │ │ │ ├── TestView.java │ │ │ └── TranslationBallLoading.java │ │ └── res │ │ ├── drawable │ │ └── loading_item_border.xml │ │ ├── layout │ │ ├── loading_item_view.xml │ │ ├── main_activity.xml │ │ └── test_item.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ └── strings.xml ├── build.gradle ├── gradle.properties ├── settings.gradle └── 示例 │ ├── Loading01.gif │ ├── Loading02.gif │ ├── Loading03.gif │ ├── Loading04.gif │ ├── Loading06.gif │ ├── Loading07.gif │ ├── Loading08.gif │ ├── Loading09.gif │ ├── Loading10.gif │ └── Loading11.gif └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | 50 | .gradle 51 | /local.properties 52 | .idea/ 53 | .DS_Store 54 | build/ 55 | /Loadings/gradlew 56 | /Loadings/gradlew.bat 57 | /Loadings/gradle/wrapper/ -------------------------------------------------------------------------------- /Loadings/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.zhaoli.loadings" 9 | minSdkVersion 14 10 | targetSdkVersion 22 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(include: ['*.jar'], dir: 'libs') 24 | compile 'com.android.support:recyclerview-v7:22+' 25 | } 26 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/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:\AndroidStudio\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 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/LoadingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.view.ViewParent; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by zhaoli on 2016/6/8. 15 | */ 16 | public class LoadingAdapter extends RecyclerView.Adapter { 17 | 18 | private Context context = null; 19 | private List loadingViewList = new ArrayList<>(); 20 | 21 | public LoadingAdapter(Context context) { 22 | this.context = context; 23 | } 24 | 25 | public void setData(List data) { 26 | loadingViewList.addAll(data); 27 | } 28 | 29 | @Override 30 | public LoadingAdapter.LoadingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 31 | View view = LayoutInflater.from(context).inflate(R.layout.loading_item_view, parent, false); 32 | LoadingViewHolder viewHolder = new LoadingViewHolder(view); 33 | return viewHolder; 34 | } 35 | 36 | @Override 37 | public void onBindViewHolder(LoadingAdapter.LoadingViewHolder holder, int position) { 38 | holder.rootView.removeAllViews(); 39 | ViewParent viewParent = loadingViewList.get(position).getParent(); 40 | if (viewParent != null) { 41 | if (viewParent instanceof ViewGroup) { 42 | ((ViewGroup) viewParent).removeAllViews(); 43 | } else { 44 | return; 45 | } 46 | } 47 | holder.rootView.addView(loadingViewList.get(position)); 48 | } 49 | 50 | @Override 51 | public int getItemCount() { 52 | return loadingViewList.size(); 53 | } 54 | 55 | public class LoadingViewHolder extends RecyclerView.ViewHolder { 56 | 57 | public ViewGroup rootView = null; 58 | 59 | public LoadingViewHolder(View itemView) { 60 | super(itemView); 61 | rootView = (ViewGroup) itemView; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.View; 8 | 9 | import com.zhaoli.loadings.loadingViews.AnnulusWhirlLoading; 10 | import com.zhaoli.loadings.loadingViews.ConvertBallLoading; 11 | import com.zhaoli.loadings.loadingViews.DownloadLoading; 12 | import com.zhaoli.loadings.loadingViews.JumpWhirlGraphLoading; 13 | import com.zhaoli.loadings.loadingViews.LineWhirlLoading1; 14 | import com.zhaoli.loadings.loadingViews.LineWhirlLoading2; 15 | import com.zhaoli.loadings.loadingViews.TestView; 16 | import com.zhaoli.loadings.loadingViews.TranslationBallLoading; 17 | import com.zhaoli.loadings.loadingViews.RotateSquareLoading; 18 | import com.zhaoli.loadings.loadingViews.SegmentSquareLoading; 19 | import com.zhaoli.loadings.loadingViews.SwingCollisionLoading; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Created by zhaoli on 2016/6/8. 26 | */ 27 | public class MainActivity extends Activity { 28 | 29 | private RecyclerView recyclerView = null; 30 | private LoadingAdapter adapter = null; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.main_activity); 36 | 37 | recyclerView = (RecyclerView) findViewById(R.id.loadingRecyclerView); 38 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 39 | 40 | adapter = new LoadingAdapter(this); 41 | 42 | List loadingViewList = new ArrayList<>(); 43 | 44 | loadingViewList.add(new AnnulusWhirlLoading(this)); 45 | loadingViewList.add(new DownloadLoading(this)); 46 | loadingViewList.add(new LineWhirlLoading1(this)); 47 | loadingViewList.add(new LineWhirlLoading2(this)); 48 | loadingViewList.add(new ConvertBallLoading(this)); 49 | loadingViewList.add(new JumpWhirlGraphLoading(this)); 50 | loadingViewList.add(new TranslationBallLoading(this)); 51 | loadingViewList.add(new SegmentSquareLoading(this)); 52 | loadingViewList.add(new SwingCollisionLoading(this)); 53 | loadingViewList.add(new RotateSquareLoading(this)); 54 | 55 | adapter.setData(loadingViewList); 56 | recyclerView.setAdapter(adapter); 57 | adapter.notifyDataSetChanged(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | /** 7 | * Created by zhaoli on 2016/6/23. 8 | */ 9 | public class TestActivity extends Activity { 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.test_item); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/interpolator/DecelerateAccelerateInterpolator.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.interpolator; 2 | 3 | import android.view.animation.Interpolator; 4 | 5 | /** 6 | * Created by zhaoli on 2016/6/21. 7 | * 8 | * 减速加速 9 | */ 10 | public class DecelerateAccelerateInterpolator implements Interpolator { 11 | 12 | public float getInterpolation(float t) { 13 | float x = 2.0f * t - 1.0f; 14 | return 0.5f * ( x * x * x + 1.0f); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/AnnulusWhirlLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.graphics.RectF; 12 | import android.util.AttributeSet; 13 | import android.view.animation.LinearInterpolator; 14 | 15 | /** 16 | * Created by zhaoli on 2016/6/30. 17 | * 18 | * 环形旋转动画 19 | */ 20 | public class AnnulusWhirlLoading extends BaseLoading { 21 | 22 | private final static int BACKGROUND_COLOR = Color.parseColor("#FFFFFF"); 23 | private final static int ANNULUS_COLOR_1 = Color.parseColor("#2A343D"); 24 | private final static int ANNULUS_COLOR_2 = Color.parseColor("#3DB0EA"); 25 | private final static int ANNULUS_COLOR_3 = Color.parseColor("#57B779"); 26 | private final static int ANNULUS_COLOR_4 = Color.parseColor("#EC6D63"); 27 | 28 | private final static int LINE_WIDTH = 80; 29 | private final static int ANNULUS_RADIUS = 2 * LINE_WIDTH; 30 | 31 | private final static float ANNULUS_LENGTH = (float) (2 * LINE_WIDTH * Math.PI) / 4 / 7; 32 | private final static float ANNULUS_ANGLE_1 = -((float)90 / 14 * 11); 33 | private final static float ANNULUS_ANGLE_2 = -((float)90 / 14 * 7); 34 | private final static float ANNULUS_ANGLE_3 = -((float)90 / 14 * 3); 35 | 36 | private final RectF rectF = new RectF(-ANNULUS_RADIUS, -ANNULUS_RADIUS, ANNULUS_RADIUS, ANNULUS_RADIUS); 37 | //计算区 38 | private float currentAnnulusLeftAngle = 270; 39 | private float currentAnnulusRightAngle = 0; 40 | 41 | private float currentLineWidth = LINE_WIDTH; 42 | 43 | private float currentAnnulusAngle1 = ANNULUS_ANGLE_1; 44 | private float currentAnnulusAngle2 = ANNULUS_ANGLE_2; 45 | private float currentAnnulusAngle3 = ANNULUS_ANGLE_3; 46 | 47 | public AnnulusWhirlLoading(Context context) { 48 | super(context); 49 | initAnim(); 50 | } 51 | 52 | public AnnulusWhirlLoading(Context context, AttributeSet attrs) { 53 | super(context, attrs); 54 | initAnim(); 55 | } 56 | 57 | public AnnulusWhirlLoading(Context context, AttributeSet attrs, int defStyleAttr) { 58 | super(context, attrs, defStyleAttr); 59 | initAnim(); 60 | } 61 | 62 | @Override 63 | protected void onDraw(Canvas canvas) { 64 | super.onDraw(canvas); 65 | 66 | canvas.translate(getWidth() / 2, getHeight() / 2); 67 | 68 | if (currentAnnulusRightAngle > (currentAnnulusAngle1 - (float)90 / 14)) { 69 | drawAnnulus(currentAnnulusAngle1, ANNULUS_COLOR_2, ANNULUS_RADIUS + LINE_WIDTH / 2, canvas); 70 | } 71 | if (currentAnnulusRightAngle > (currentAnnulusAngle2 - (float)90 / 14)) { 72 | drawAnnulus(currentAnnulusAngle2, ANNULUS_COLOR_3, ANNULUS_RADIUS + LINE_WIDTH / 2, canvas); 73 | } 74 | if (currentAnnulusRightAngle > (currentAnnulusAngle3 - (float)90 / 14)) { 75 | drawAnnulus(currentAnnulusAngle3, ANNULUS_COLOR_4, ANNULUS_RADIUS + LINE_WIDTH / 2, canvas); 76 | } 77 | 78 | loadingPaint.setStrokeWidth(LINE_WIDTH); 79 | loadingPaint.setStyle(Paint.Style.STROKE); 80 | loadingPaint.setColor(ANNULUS_COLOR_1); 81 | canvas.drawArc(rectF, currentAnnulusRightAngle, currentAnnulusLeftAngle - currentAnnulusRightAngle, false, loadingPaint); 82 | 83 | //当长度变化的时候 绘制线 84 | if (currentLineWidth < LINE_WIDTH) { 85 | drawAnnulus(0, ANNULUS_COLOR_1, ANNULUS_RADIUS - LINE_WIDTH / 2 + currentLineWidth, canvas); 86 | } 87 | } 88 | 89 | private void drawAnnulus(float currentAngle, int color, float right, Canvas canvas) { 90 | canvas.rotate(currentAngle); 91 | loadingPaint.setStrokeWidth(0); 92 | loadingPaint.setStyle(Paint.Style.FILL); 93 | loadingPaint.setColor(color); 94 | canvas.drawRect(ANNULUS_RADIUS - LINE_WIDTH / 2, -ANNULUS_LENGTH / 2, right, ANNULUS_LENGTH / 2, loadingPaint); 95 | canvas.rotate(-currentAngle); 96 | } 97 | 98 | @Override 99 | protected void initLoading() { 100 | setBackgroundColor(BACKGROUND_COLOR); 101 | 102 | AnimatorSet animatorSet = new AnimatorSet(); 103 | Animator closeAnim = getCloseAnim(); 104 | Animator openAnim = getOpenAnim(); 105 | animatorSet.playSequentially(closeAnim, openAnim); 106 | animatorSet.setInterpolator(new LinearInterpolator()); 107 | animatorSet.addListener(new AnimatorListenerAdapter() { 108 | @Override 109 | public void onAnimationEnd(Animator animation) { 110 | super.onAnimationEnd(animation); 111 | animation.start(); 112 | } 113 | }); 114 | animatorSet.start(); 115 | } 116 | 117 | private Animator getCloseAnim() { 118 | AnimatorSet animatorSet = new AnimatorSet(); 119 | 120 | ValueAnimator leftAnim = ValueAnimator.ofFloat(270, -90); 121 | leftAnim.setDuration(2000); 122 | //左边弧度的变化 123 | leftAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 124 | @Override 125 | public void onAnimationUpdate(ValueAnimator animation) { 126 | currentAnnulusLeftAngle = (float) animation.getAnimatedValue(); 127 | invalidate(); 128 | } 129 | }); 130 | ValueAnimator rightAnim = ValueAnimator.ofFloat(0, -90); 131 | rightAnim.setDuration(2000); 132 | //右边弧度的变化 133 | rightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 134 | @Override 135 | public void onAnimationUpdate(ValueAnimator animation) { 136 | currentAnnulusRightAngle = (float) animation.getAnimatedValue(); 137 | invalidate(); 138 | } 139 | }); 140 | 141 | animatorSet.playTogether(leftAnim, rightAnim); 142 | animatorSet.setInterpolator(new LinearInterpolator()); 143 | return animatorSet; 144 | } 145 | 146 | private Animator getOpenAnim() { 147 | AnimatorSet animatorSet = new AnimatorSet(); 148 | ValueAnimator startAnim = ValueAnimator.ofFloat(0, LINE_WIDTH); 149 | startAnim.setDuration(500); 150 | //打开时的起始动画 151 | startAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 152 | @Override 153 | public void onAnimationUpdate(ValueAnimator animation) { 154 | currentLineWidth = (float) animation.getAnimatedValue(); 155 | invalidate(); 156 | } 157 | }); 158 | 159 | ValueAnimator whirlAnim = ValueAnimator.ofFloat(0, 270); 160 | whirlAnim.setDuration(500); 161 | whirlAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 162 | @Override 163 | public void onAnimationUpdate(ValueAnimator animation) { 164 | currentAnnulusLeftAngle = (float) animation.getAnimatedValue(); 165 | invalidate(); 166 | } 167 | }); 168 | whirlAnim.addListener(new AnimatorListenerAdapter() { 169 | @Override 170 | public void onAnimationStart(Animator animation) { 171 | super.onAnimationStart(animation); 172 | currentAnnulusRightAngle = 0; 173 | currentAnnulusAngle1 = Float.MAX_VALUE; 174 | currentAnnulusAngle2 = Float.MAX_VALUE; 175 | currentAnnulusAngle3 = Float.MAX_VALUE; 176 | } 177 | }); 178 | 179 | ValueAnimator annulusAnim1 = getAnnulusAnim(360 + ANNULUS_ANGLE_1, 1); 180 | ValueAnimator annulusAnim2 = getAnnulusAnim(360 + ANNULUS_ANGLE_2, 2); 181 | ValueAnimator annulusAnim3 = getAnnulusAnim(360 + ANNULUS_ANGLE_3, 3); 182 | 183 | animatorSet.setInterpolator(new LinearInterpolator()); 184 | animatorSet.playSequentially(startAnim, whirlAnim, annulusAnim3, annulusAnim2, annulusAnim1); 185 | return animatorSet; 186 | } 187 | 188 | private ValueAnimator getAnnulusAnim(float endAngle, final int index) { 189 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(270, endAngle); 190 | valueAnimator.setDuration(200); 191 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 192 | @Override 193 | public void onAnimationUpdate(ValueAnimator animation) { 194 | if (index == 1) { 195 | currentAnnulusAngle1 = (float) animation.getAnimatedValue() - 360; 196 | } else if (index == 2) { 197 | currentAnnulusAngle2 = (float) animation.getAnimatedValue() - 360; 198 | } else if (index == 3) { 199 | currentAnnulusAngle3 = (float) animation.getAnimatedValue() - 360; 200 | } 201 | invalidate(); 202 | } 203 | }); 204 | return valueAnimator; 205 | } 206 | 207 | @Override 208 | protected void initLoadingPaint() { 209 | loadingPaint.setAntiAlias(true); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/BaseLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.content.Context; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | 10 | /** 11 | * Created by zhaoli on 2016/6/17. 12 | */ 13 | public abstract class BaseLoading extends View { 14 | 15 | protected Context context = null; 16 | protected Paint loadingPaint = new Paint(); 17 | 18 | public BaseLoading(Context context) { 19 | super(context); 20 | this.context = context; 21 | } 22 | 23 | public BaseLoading(Context context, AttributeSet attrs) { 24 | super(context, attrs); 25 | this.context = context; 26 | } 27 | 28 | public BaseLoading(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | this.context = context; 31 | } 32 | 33 | /** 34 | * 重载必须调用 (构造结束后调用) 35 | */ 36 | protected void initAnim() { 37 | initLoadingPaint(); 38 | initLoading(); 39 | } 40 | 41 | protected abstract void initLoading(); 42 | protected abstract void initLoadingPaint(); 43 | } 44 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/ConvertBallLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.util.AttributeSet; 9 | import android.view.animation.LinearInterpolator; 10 | 11 | /** 12 | * Created by zhaoli on 2016/6/21. 13 | * 变换球体加载 14 | */ 15 | public class ConvertBallLoading extends BaseLoading { 16 | 17 | private final static int BALL_RADIUS = 20; 18 | 19 | private final static int[] BALL_COLOR = new int[] { 20 | Color.parseColor("#3E8EFF"), //蓝 21 | Color.parseColor("#FF4B4B"), //红 22 | Color.parseColor("#FFD91E") //黄 23 | }; 24 | 25 | private final static int BACK_GROUND = Color.parseColor("#FBF8F0"); 26 | 27 | //left 28 | private int[] offset_radius; 29 | private int[] offset; 30 | private int leftStep = 0;//步骤 0, 1, 2, 3 31 | 32 | //right 33 | private int[] center; //中心点(1:左 2:右) 34 | private int[] whirl_radius; 35 | private final static int BALL_RADIUS_2 = 2 * BALL_RADIUS; 36 | private final static int BALL_RADIUS_3 = 3 * BALL_RADIUS; 37 | 38 | 39 | public ConvertBallLoading(Context context) { 40 | super(context); 41 | initAnim(); 42 | } 43 | 44 | public ConvertBallLoading(Context context, AttributeSet attrs) { 45 | super(context, attrs); 46 | initAnim(); 47 | } 48 | 49 | public ConvertBallLoading(Context context, AttributeSet attrs, int defStyleAttr) { 50 | super(context, attrs, defStyleAttr); 51 | initAnim(); 52 | } 53 | 54 | @Override 55 | protected void onDraw(Canvas canvas) { 56 | super.onDraw(canvas); 57 | 58 | //绘制第一幅 59 | if (leftStep == 0) { 60 | drawLeft(2, 0, 1, canvas); //0-1交1 0-2重1 61 | } else if (leftStep == 1) { 62 | drawLeft(1, 2, 0, canvas); //1-2交2 63 | } else if (leftStep == 2) { 64 | drawLeft(2, 0, 1, canvas); //0-1交1 0-2重0 65 | } else { 66 | drawLeft(1, 2, 0, canvas); //1-2交2 67 | } 68 | 69 | //绘制第二幅 70 | canvas.translate(getWidth() / 2, 0); 71 | drawRight(0, canvas); 72 | drawRight(1, canvas); 73 | drawRight(2, canvas); 74 | } 75 | 76 | private void drawLeft(int i, int j, int k, Canvas canvas) { 77 | canvas.translate(getWidth() / 4, getHeight() / 2); 78 | loadingPaint.setColor(BALL_COLOR[i]); 79 | canvas.drawCircle(offset[i], 0, BALL_RADIUS + offset_radius[i], loadingPaint); 80 | 81 | loadingPaint.setColor(BALL_COLOR[j]); 82 | canvas.drawCircle(offset[j], 0, BALL_RADIUS + offset_radius[j], loadingPaint); 83 | 84 | loadingPaint.setColor(BALL_COLOR[k]); 85 | canvas.drawCircle(offset[k], 0, BALL_RADIUS + offset_radius[k], loadingPaint); 86 | } 87 | 88 | private void drawRight(int index, Canvas canvas) { 89 | 90 | float x, y; 91 | 92 | if (whirl_radius[index] >= 0 && whirl_radius[index] < 90) { 93 | y = -BALL_RADIUS_3 * sin(whirl_radius[index]); 94 | } else if (whirl_radius[index] >= 90 && whirl_radius[index] < 180) { 95 | y = -BALL_RADIUS_3 * sin(180 - whirl_radius[index]); 96 | } else if (whirl_radius[index] >= 180 && whirl_radius[index] < 270) { 97 | y = BALL_RADIUS_3 * cos(270 - whirl_radius[index]); 98 | } else { 99 | y = BALL_RADIUS_3 * sin(360 - whirl_radius[index]); 100 | } 101 | 102 | if (whirl_radius[index] >= 0 && whirl_radius[index] < 90) { 103 | x = (center[index] == 1) ? (-BALL_RADIUS_2 - BALL_RADIUS_2 * cos(whirl_radius[index])) : 104 | (BALL_RADIUS_2 - BALL_RADIUS_2 * cos(whirl_radius[index])); 105 | } else if (whirl_radius[index] >= 90 && whirl_radius[index] < 180) { 106 | x = (center[index] == 1) ? (-BALL_RADIUS_2 + BALL_RADIUS_2 * cos(180 - whirl_radius[index])) : 107 | (BALL_RADIUS_2 + BALL_RADIUS_2 * cos(180 - whirl_radius[index])); 108 | } else if (whirl_radius[index] >= 180 && whirl_radius[index] < 270) { 109 | x = (center[index] == 1) ? (-BALL_RADIUS_2 + BALL_RADIUS_2 * sin(270 - whirl_radius[index])) : 110 | (BALL_RADIUS_2 + BALL_RADIUS_2 * sin(270 - whirl_radius[index])); 111 | } else { 112 | x = (center[index] == 1) ? (-BALL_RADIUS_2 - BALL_RADIUS_2 * cos(360 - whirl_radius[index])) : 113 | (BALL_RADIUS_2 - BALL_RADIUS_2 * cos(360 - whirl_radius[index])); 114 | } 115 | 116 | loadingPaint.setColor(BALL_COLOR[index]); 117 | canvas.drawCircle(x, y, BALL_RADIUS, loadingPaint); 118 | } 119 | 120 | private float sin(int angle) { 121 | return (float) Math.sin(Math.toRadians(angle)); 122 | } 123 | 124 | private float cos(int angle) { 125 | return (float) Math.cos(Math.toRadians(angle)); 126 | } 127 | 128 | @Override 129 | protected void initLoading() { 130 | setBackgroundColor(BACK_GROUND); 131 | 132 | AnimatorSet animatorSet = new AnimatorSet(); 133 | 134 | offset = new int[3]; 135 | offset_radius = new int[3]; 136 | ValueAnimator leftMoveAnim = getLeftMoveAnim(); 137 | 138 | center = new int[3]; 139 | whirl_radius = new int[3]; 140 | ValueAnimator rightAnim = getRightAnim(); 141 | 142 | animatorSet.playTogether(leftMoveAnim, rightAnim); 143 | animatorSet.start(); 144 | } 145 | 146 | private ValueAnimator getLeftMoveAnim() { 147 | ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 6 * BALL_RADIUS, 12 * BALL_RADIUS, 148 | 18 * BALL_RADIUS, 24 * BALL_RADIUS); 149 | valueAnimator.setDuration(2000); 150 | valueAnimator.setRepeatCount(-1); 151 | valueAnimator.setInterpolator(new LinearInterpolator()); 152 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 153 | @Override 154 | public void onAnimationUpdate(ValueAnimator animation) { 155 | int value = (int) animation.getAnimatedValue(); 156 | if (value >= 0 && value < 6 * BALL_RADIUS) { 157 | offset[0] = value - 6 * BALL_RADIUS; 158 | offset[1] = -value; 159 | offset[2] = 6 * BALL_RADIUS - value; 160 | 161 | offset_radius[0] = offset_radius[2] = value / 6; //[0, 0.5] 162 | offset_radius[1] = BALL_RADIUS - value / 6; //[0.5, 0] 163 | 164 | leftStep = 0; 165 | } else if (value >= 6 * BALL_RADIUS && value < 12 * BALL_RADIUS) { 166 | offset[0] = value - 6 * BALL_RADIUS; 167 | offset[1] = -(12 * BALL_RADIUS - value); 168 | offset[2] = 6 * BALL_RADIUS - value; 169 | 170 | offset_radius[0] = offset_radius[2] = BALL_RADIUS - (value - 6 * BALL_RADIUS) / 6; //[0.5, 0] 171 | offset_radius[1] = (value - 6 * BALL_RADIUS) / 6; //[0, 0.5] 172 | 173 | leftStep = 1; 174 | } else if (value >= 12 * BALL_RADIUS && value < 18 * BALL_RADIUS) { 175 | offset[0] = 18 * BALL_RADIUS - value; 176 | offset[1] = value - 12 * BALL_RADIUS; 177 | offset[2] = -(18 * BALL_RADIUS - value); 178 | 179 | offset_radius[0] = offset_radius[2] = (value - 12 * BALL_RADIUS) / 6; //[0, 0.5] 180 | offset_radius[1] = BALL_RADIUS - (value - 12 * BALL_RADIUS) / 6; //[0.5, 0] 181 | 182 | leftStep = 2; 183 | } else if (value >= 18 * BALL_RADIUS && value < 24 * BALL_RADIUS) { 184 | offset[0] = -(value - 18 * BALL_RADIUS); 185 | offset[1] = 24 * BALL_RADIUS - value; 186 | offset[2] = value - 18 * BALL_RADIUS; 187 | 188 | offset_radius[0] = offset_radius[2] = BALL_RADIUS - (value - 18 * BALL_RADIUS) / 6; //[0.5, 0] 189 | offset_radius[1] = (value - 18 * BALL_RADIUS) / 6; //[0, 0.5] 190 | 191 | leftStep = 3; 192 | } 193 | invalidate(); 194 | } 195 | }); 196 | return valueAnimator; 197 | } 198 | 199 | private ValueAnimator getRightAnim() { 200 | ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 90, 180, 270, 360, 450, 540, 630, 720); 201 | valueAnimator.setDuration(1500); 202 | valueAnimator.setRepeatCount(-1); 203 | valueAnimator.setInterpolator(new LinearInterpolator()); 204 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 205 | @Override 206 | public void onAnimationUpdate(ValueAnimator animation) { 207 | int value = (int) animation.getAnimatedValue(); 208 | //计算每个 209 | if (value >= 90 && value < 450) { 210 | center[0] = 2; 211 | whirl_radius[0] = value - 90; 212 | } else { 213 | center[0] = 1; 214 | if (value >= 450 && value < 630) { 215 | whirl_radius[0] = 180 - (value - 450); 216 | } else if (value >= 0 && value < 90) { 217 | whirl_radius[0] = 270 - value; 218 | } else { 219 | whirl_radius[0] = 360 - (value - 630); 220 | } 221 | } 222 | 223 | if (value >= 0 && value < 360) { 224 | center[1] = 1; 225 | if (value >= 0 && value < 180) { 226 | whirl_radius[1] = 180 - value; 227 | } else { 228 | whirl_radius[1] = 360 - (value - 180); 229 | } 230 | } else { 231 | center[1] = 2; 232 | whirl_radius[1] = value - 360; 233 | } 234 | 235 | if (value >= 0 && value < 270) { 236 | center[2] = 2; 237 | whirl_radius[2] = 90 + value; 238 | } else if (value >= 270 && value < 630) { 239 | center[2] = 1; 240 | if (value >= 270 && value < 450) { 241 | whirl_radius[2] = 180 - (value - 270); 242 | } else { 243 | whirl_radius[2] = 360 - (value - 450); 244 | } 245 | } else { 246 | center[2] = 2; 247 | whirl_radius[2] = value - 630; 248 | } 249 | } 250 | }); 251 | return valueAnimator; 252 | } 253 | 254 | @Override 255 | protected void initLoadingPaint() { 256 | loadingPaint.setAntiAlias(true); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/DownloadLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.graphics.Path; 12 | import android.graphics.RectF; 13 | import android.util.AttributeSet; 14 | import android.view.animation.AccelerateInterpolator; 15 | import android.view.animation.LinearInterpolator; 16 | 17 | import com.zhaoli.loadings.interpolator.DecelerateAccelerateInterpolator; 18 | 19 | /** 20 | * Created by zhaoli on 2016/6/22. 21 | * 下载加载动画 22 | */ 23 | public class DownloadLoading extends BaseLoading { 24 | private final static int BACK_GROUND_COLOR = Color.parseColor("#1C9CF2"); 25 | private final static int UN_DO_LINE_COLOR = Color.parseColor("#2FA4F2"); 26 | private final static int DO_LINE_COLOR = Color.parseColor("#FFFFFF"); 27 | 28 | private final static int CIRCULAR_RADIUS = 150; 29 | private final static int CIRCULAR_WIDTH = 12; 30 | 31 | private final static float P_CIRCULAR = (float) (2 * Math.PI * CIRCULAR_RADIUS); 32 | private final static RectF RECTF = new RectF(-CIRCULAR_RADIUS, -CIRCULAR_RADIUS, CIRCULAR_RADIUS, CIRCULAR_RADIUS); 33 | 34 | private final static float ARROW_LENGTH = (float) (CIRCULAR_RADIUS / 2 / Math.cos(Math.toRadians(45)));//箭头长度 35 | private final static float CENTER_TEXT_SIZE = 36; //中心文字大小 36 | 37 | 38 | private final static float WAVE_MAX = 4; //波浪数量 39 | private final static float WAVE_RADIUS = ARROW_LENGTH / (2 * WAVE_MAX); //波浪半径 40 | 41 | //-----数据区 42 | private final static int PROGRESS_MAX = 100; 43 | 44 | private int currentProgress = 0; 45 | private int maxProgress = PROGRESS_MAX; 46 | 47 | private String centerText = ""; 48 | 49 | //-----动画区 50 | private int currentLineLength = CIRCULAR_RADIUS; //当前线条长度 51 | private int currentLineAngle = 90; //当前箭头夹角 52 | private int currentLineHeight = 0; //当前线条高度 53 | 54 | private float currentTextSize = 0; //当前文字大小 55 | private float currentWavePosition = ARROW_LENGTH; //当前波浪位置 56 | private float currentWaveOffset = 0; //当前波浪偏移 57 | private RectF waveBeginRectF = new RectF(); 58 | 59 | //对勾两个点的Y变化 60 | private float currentArrowMidHeight = 0; 61 | private float currentArrowEndHeight = 0; 62 | 63 | private ValueAnimator waveAnim; //波浪动画 64 | 65 | private int animState = -1; //0 开始动画 1 中间动画 2 结束动画 66 | 67 | private Path path = new Path(); 68 | 69 | public DownloadLoading(Context context) { 70 | super(context); 71 | initAnim(); 72 | } 73 | 74 | public DownloadLoading(Context context, AttributeSet attrs) { 75 | super(context, attrs); 76 | initAnim(); 77 | } 78 | 79 | public DownloadLoading(Context context, AttributeSet attrs, int defStyleAttr) { 80 | super(context, attrs, defStyleAttr); 81 | initAnim(); 82 | } 83 | 84 | public void setCurrentProgress(int currentProgress) { 85 | this.currentProgress = currentProgress; 86 | centerText = "" + currentProgress; 87 | if (currentProgress >= maxProgress) { 88 | onLoadingEnd(); 89 | onStartAnimEnd(); 90 | } 91 | } 92 | 93 | @Override 94 | protected void onDraw(Canvas canvas) { 95 | super.onDraw(canvas); 96 | 97 | canvas.translate(getWidth() / 2, getHeight() / 2); 98 | 99 | //绘制初始圈 100 | loadingPaint.setColor(UN_DO_LINE_COLOR); 101 | canvas.drawCircle(0, 0, CIRCULAR_RADIUS, loadingPaint); 102 | 103 | //绘制进度 104 | canvas.rotate(-90); 105 | float angle = ((float) currentProgress / maxProgress) * 360; 106 | loadingPaint.setColor(DO_LINE_COLOR); 107 | canvas.drawArc(RECTF, 0, -angle, true, loadingPaint); 108 | 109 | //绘制中心圈 110 | loadingPaint.setColor(BACK_GROUND_COLOR); 111 | canvas.drawCircle(0, 0, CIRCULAR_RADIUS - CIRCULAR_WIDTH, loadingPaint); 112 | 113 | if (animState == 0) { 114 | drawStart(canvas); 115 | } else if (animState == 1) { 116 | drawLoading(canvas); 117 | } else { 118 | drawEnd(canvas); 119 | } 120 | 121 | loadingPaint.setStyle(Paint.Style.FILL); 122 | } 123 | 124 | private void drawStart(Canvas canvas) { 125 | //绘制开始动画 126 | //1.绘制竖线 127 | loadingPaint.setColor(DO_LINE_COLOR); 128 | 129 | //复原画布 130 | canvas.rotate(90); 131 | loadingPaint.setStrokeWidth(CIRCULAR_WIDTH); 132 | canvas.drawLine(0, -currentLineLength / 2 - currentLineHeight, 0, currentLineLength / 2 - currentLineHeight, loadingPaint); 133 | //2.绘制箭头 134 | loadingPaint.setStrokeWidth(CIRCULAR_WIDTH / 2); 135 | float lineX = (float) (-ARROW_LENGTH * Math.sin(Math.toRadians(currentLineAngle / 2))); 136 | float lineY = (float) (ARROW_LENGTH * Math.cos(Math.toRadians(currentLineAngle / 2))) + CIRCULAR_WIDTH / 2; 137 | canvas.drawLine(lineX, CIRCULAR_WIDTH / 2, 0, lineY, loadingPaint); 138 | canvas.drawLine(0, lineY, -lineX, CIRCULAR_WIDTH / 2, loadingPaint); 139 | } 140 | 141 | private void drawLoading(Canvas canvas) { 142 | //绘制中间动画 143 | //1. 绘制文字 144 | 145 | //复原画布 146 | canvas.rotate(90); 147 | loadingPaint.setColor(DO_LINE_COLOR); 148 | if (currentTextSize > 0) { 149 | loadingPaint.setTextSize(currentTextSize); 150 | loadingPaint.setTextAlign(Paint.Align.CENTER); 151 | canvas.drawText(centerText, 0, CIRCULAR_RADIUS / 2, loadingPaint); 152 | } 153 | 154 | //2. 绘制波浪 155 | canvas.translate(-ARROW_LENGTH, 0); 156 | //水平线 157 | path.reset(); 158 | path.moveTo(0, 0); 159 | path.lineTo(currentWavePosition, 0); 160 | 161 | if (currentWaveOffset > 0) { 162 | path.reset(); 163 | float waveBeginAngle; //波浪开始部分的角度 164 | float waveBeginRadius = WAVE_RADIUS * 2 / 3; //波浪开始部分的半径 165 | if (currentWaveOffset > 0 && currentWaveOffset <= WAVE_RADIUS) { 166 | waveBeginAngle = (float) Math.toDegrees(Math.acos((WAVE_RADIUS - currentWaveOffset) / WAVE_RADIUS)); 167 | waveBeginRectF.set(-currentWaveOffset, -waveBeginRadius, 2 * WAVE_RADIUS - currentWaveOffset, waveBeginRadius); 168 | path.addArc(waveBeginRectF, 180 + waveBeginAngle, 180 - waveBeginAngle); 169 | waveBeginRectF.set(waveBeginRectF.right, waveBeginRectF.top, waveBeginRectF.right + 2 * WAVE_RADIUS, waveBeginRectF.bottom); 170 | path.addArc(waveBeginRectF, 0, 180); 171 | } else if (currentWaveOffset > WAVE_RADIUS && currentWaveOffset <= 2 * WAVE_RADIUS) { 172 | waveBeginAngle = (float) (180 - Math.toDegrees(Math.acos((currentWaveOffset - WAVE_RADIUS) / WAVE_RADIUS))); 173 | waveBeginRectF.set(-currentWaveOffset, -waveBeginRadius, 2 * WAVE_RADIUS - currentWaveOffset, waveBeginRadius); 174 | path.addArc(waveBeginRectF, 180 + waveBeginAngle, 180 - waveBeginAngle); 175 | waveBeginRectF.set(waveBeginRectF.right, waveBeginRectF.top, waveBeginRectF.right + 2 * WAVE_RADIUS, waveBeginRectF.bottom); 176 | path.addArc(waveBeginRectF, 0, 180); 177 | } else if (currentWaveOffset > 2 * WAVE_RADIUS && currentWaveOffset <= 3 * WAVE_RADIUS) { 178 | waveBeginAngle = (float) Math.toDegrees(Math.acos((3 * WAVE_RADIUS - currentWaveOffset) / WAVE_RADIUS)); 179 | waveBeginRectF.set(-currentWaveOffset + 2 * WAVE_RADIUS, -waveBeginRadius, 4 * WAVE_RADIUS - currentWaveOffset, waveBeginRadius); 180 | path.addArc(waveBeginRectF, 0, 180 - waveBeginAngle); 181 | } else { 182 | waveBeginAngle = (float) (180 - Math.toDegrees(Math.acos((currentWaveOffset - 3 * WAVE_RADIUS) / WAVE_RADIUS))); 183 | waveBeginRectF.set(-currentWaveOffset + 2 * WAVE_RADIUS, -waveBeginRadius, 4 * WAVE_RADIUS - currentWaveOffset, waveBeginRadius); 184 | path.addArc(waveBeginRectF, 0, 180 - waveBeginAngle); 185 | } 186 | path.moveTo(waveBeginRectF.right, 0); 187 | } 188 | 189 | float laveLength = 2 * ARROW_LENGTH - currentWavePosition - (4 * WAVE_RADIUS - currentWaveOffset); 190 | while (laveLength >= 4 * WAVE_RADIUS) { 191 | path.rQuadTo(WAVE_RADIUS, -WAVE_RADIUS, 2 * WAVE_RADIUS, 0); 192 | path.rQuadTo(WAVE_RADIUS, WAVE_RADIUS, 2 * WAVE_RADIUS, 0); 193 | laveLength -= 4 * WAVE_RADIUS; 194 | } 195 | 196 | if (laveLength >= 2 * WAVE_RADIUS) { 197 | path.rQuadTo(WAVE_RADIUS, -WAVE_RADIUS, 2 * WAVE_RADIUS, 0); 198 | laveLength -= 2 * WAVE_RADIUS; 199 | path.rQuadTo(WAVE_RADIUS, WAVE_RADIUS, laveLength, 0); 200 | } 201 | 202 | loadingPaint.setStyle(Paint.Style.STROKE); 203 | canvas.drawPath(path, loadingPaint); 204 | } 205 | 206 | private void drawEnd(Canvas canvas) { 207 | //绘制结束动画 208 | 209 | //复原画布 210 | canvas.rotate(90); 211 | loadingPaint.setColor(DO_LINE_COLOR); 212 | if (currentTextSize > 0) { 213 | loadingPaint.setTextSize(currentTextSize); 214 | loadingPaint.setTextAlign(Paint.Align.CENTER); 215 | canvas.drawText(centerText, 0, CIRCULAR_RADIUS / 2, loadingPaint); 216 | } 217 | 218 | //2. 绘制直线 219 | path.reset(); 220 | float startX, startY, midX, midY, endX, endY; 221 | 222 | midX = -(float) (ARROW_LENGTH / 3 - currentArrowMidHeight / Math.tan(Math.toRadians(ARROW_MID_ANGLE))); 223 | midY = currentArrowMidHeight; 224 | 225 | startX = -(float) (Math.sqrt((ARROW_LENGTH * 2 / 3) * (ARROW_LENGTH * 2 / 3) - currentArrowMidHeight * currentArrowMidHeight)) + midX; 226 | startY = 0; 227 | 228 | endX = (float) (ARROW_LENGTH - currentArrowEndHeight / Math.tan(Math.toRadians(ARROW_END_ANGLE))); 229 | endY = -currentArrowEndHeight; 230 | 231 | path.moveTo(startX, startY); 232 | path.lineTo(midX, midY); 233 | path.lineTo(endX, endY); 234 | loadingPaint.setStyle(Paint.Style.STROKE); 235 | loadingPaint.setStrokeWidth(CIRCULAR_WIDTH / 2); 236 | canvas.drawPath(path, loadingPaint); 237 | } 238 | 239 | @Override 240 | protected void initLoading() { 241 | setBackgroundColor(BACK_GROUND_COLOR); 242 | 243 | startStartAnim(); 244 | } 245 | 246 | @Override 247 | protected void initLoadingPaint() { 248 | loadingPaint.setAntiAlias(true); 249 | } 250 | 251 | private AnimatorSet getDownloadStartAnim() { 252 | AnimatorSet animatorSet = new AnimatorSet(); 253 | AnimatorSet animatorSet1 = new AnimatorSet(); 254 | AnimatorSet animatorSet2 = new AnimatorSet(); 255 | 256 | ValueAnimator valueAnimator1 = ValueAnimator.ofInt(CIRCULAR_RADIUS, CIRCULAR_WIDTH); 257 | valueAnimator1.setInterpolator(new AccelerateInterpolator()); 258 | valueAnimator1.setDuration(500); 259 | valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 260 | @Override 261 | public void onAnimationUpdate(ValueAnimator animation) { 262 | currentLineLength = (int) animation.getAnimatedValue(); 263 | animState = 0; 264 | invalidate(); 265 | } 266 | }); 267 | 268 | ValueAnimator valueAnimator2 = ValueAnimator.ofInt(90, 180); 269 | valueAnimator2.setInterpolator(new AccelerateInterpolator()); 270 | valueAnimator2.setDuration(500); 271 | valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 272 | @Override 273 | public void onAnimationUpdate(ValueAnimator animation) { 274 | currentLineAngle = (int) animation.getAnimatedValue(); 275 | animState = 0; 276 | invalidate(); 277 | } 278 | }); 279 | 280 | ValueAnimator valueAnimator3 = ValueAnimator.ofInt(0, CIRCULAR_RADIUS + CIRCULAR_WIDTH, CIRCULAR_WIDTH); 281 | valueAnimator3.setDuration(500); 282 | valueAnimator3.setInterpolator(new DecelerateAccelerateInterpolator()); 283 | valueAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 284 | @Override 285 | public void onAnimationUpdate(ValueAnimator animation) { 286 | currentLineHeight = (int) animation.getAnimatedValue(); 287 | animState = 0; 288 | invalidate(); 289 | } 290 | }); 291 | 292 | ValueAnimator valueAnimator4 = ValueAnimator.ofInt(180, 190, 170, 180); 293 | valueAnimator4.setDuration(500); 294 | valueAnimator4.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 295 | @Override 296 | public void onAnimationUpdate(ValueAnimator animation) { 297 | currentLineAngle = (int) animation.getAnimatedValue(); 298 | animState = 0; 299 | invalidate(); 300 | } 301 | }); 302 | 303 | animatorSet1.playTogether(valueAnimator1, valueAnimator2); 304 | animatorSet2.playTogether(valueAnimator3, valueAnimator4); 305 | animatorSet.playSequentially(animatorSet1, animatorSet2); 306 | return animatorSet; 307 | } 308 | 309 | private AnimatorSet getDownloadingAnim() { 310 | AnimatorSet animatorSet = new AnimatorSet(); 311 | 312 | AnimatorSet animatorSet1 = new AnimatorSet(); 313 | 314 | ValueAnimator textSizeAnim = ValueAnimator.ofFloat(0, CENTER_TEXT_SIZE); 315 | textSizeAnim.setDuration(500); 316 | textSizeAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 317 | @Override 318 | public void onAnimationUpdate(ValueAnimator animation) { 319 | currentTextSize = (float) animation.getAnimatedValue(); 320 | animState = 1; 321 | invalidate(); 322 | } 323 | }); 324 | 325 | ValueAnimator lineAnim = ValueAnimator.ofFloat(2 * ARROW_LENGTH, 0); 326 | lineAnim.setDuration(500); 327 | lineAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 328 | @Override 329 | public void onAnimationUpdate(ValueAnimator animation) { 330 | currentWavePosition = (float) animation.getAnimatedValue(); 331 | animState = 1; 332 | invalidate(); 333 | } 334 | }); 335 | 336 | animatorSet1.playTogether(textSizeAnim, lineAnim); 337 | 338 | waveAnim = ValueAnimator.ofFloat(0, 4 * WAVE_RADIUS); 339 | waveAnim.setDuration(300); 340 | waveAnim.setRepeatCount(-1); 341 | waveAnim.setInterpolator(new LinearInterpolator()); 342 | waveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 343 | @Override 344 | public void onAnimationUpdate(ValueAnimator animation) { 345 | currentWaveOffset = (float) animation.getAnimatedValue(); 346 | animState = 1; 347 | invalidate(); 348 | } 349 | }); 350 | 351 | animatorSet.playSequentially(animatorSet1, waveAnim); 352 | 353 | return animatorSet; 354 | } 355 | 356 | private final static float ARROW_MID_Y_MAX = (float) (ARROW_LENGTH * 2 / 3 * Math.cos(Math.toRadians(45))); // 最终30度 357 | private final static float ARROW_END_Y_MAX = -(float) (ARROW_LENGTH * 4 / 3 * Math.cos(Math.toRadians(30)) - ARROW_MID_Y_MAX); 358 | private final static float ARROW_END_X_MAX = (float) (ARROW_LENGTH * 4 / 3 * Math.sin(Math.toRadians(30))); 359 | private final static float ARROW_MID_ANGLE = (float) Math.toDegrees(Math.atan(ARROW_MID_Y_MAX / (ARROW_LENGTH / 3))); 360 | private final static float ARROW_END_ANGLE = (float) Math.toDegrees(Math.atan(-ARROW_END_Y_MAX / (ARROW_LENGTH - ARROW_END_X_MAX))); 361 | private AnimatorSet getDownloadEndAnim() { 362 | AnimatorSet animatorSet = new AnimatorSet(); 363 | 364 | ValueAnimator textSizeAnim = ValueAnimator.ofFloat(CENTER_TEXT_SIZE, 0); 365 | textSizeAnim.setDuration(500); 366 | textSizeAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 367 | @Override 368 | public void onAnimationUpdate(ValueAnimator animation) { 369 | currentTextSize = (float) animation.getAnimatedValue(); 370 | animState = 2; 371 | invalidate(); 372 | } 373 | }); 374 | 375 | AnimatorSet animatorSet1 = new AnimatorSet(); 376 | ValueAnimator arrowAnim1 = ValueAnimator.ofFloat(0, ARROW_MID_Y_MAX); 377 | arrowAnim1.setDuration(1000); 378 | arrowAnim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 379 | @Override 380 | public void onAnimationUpdate(ValueAnimator animation) { 381 | currentArrowMidHeight = (float) animation.getAnimatedValue(); 382 | animState = 2; 383 | invalidate(); 384 | } 385 | }); 386 | 387 | ValueAnimator arrowAnim2 = ValueAnimator.ofFloat(0, -ARROW_END_Y_MAX); 388 | arrowAnim2.setDuration(1000); 389 | arrowAnim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 390 | @Override 391 | public void onAnimationUpdate(ValueAnimator animation) { 392 | currentArrowEndHeight = (float) animation.getAnimatedValue(); 393 | animState = 2; 394 | invalidate(); 395 | } 396 | }); 397 | 398 | animatorSet1.playTogether(arrowAnim1, arrowAnim2); 399 | 400 | animatorSet.playSequentially(textSizeAnim, animatorSet1); 401 | 402 | return animatorSet; 403 | } 404 | 405 | /** 406 | * 进度测试 407 | * @return 408 | */ 409 | public DownloadLoading test() { 410 | new Thread(new Runnable() { 411 | @Override 412 | public void run() { 413 | currentProgress = 0; 414 | while (true) { 415 | post(new Runnable() { 416 | @Override 417 | public void run() { 418 | setCurrentProgress(++currentProgress); 419 | } 420 | }); 421 | post(new Runnable() { 422 | @Override 423 | public void run() { 424 | invalidate(); 425 | } 426 | }); 427 | try { 428 | Thread.sleep(100); 429 | } catch (Exception e) { 430 | e.printStackTrace(); 431 | } 432 | } 433 | } 434 | }).start(); 435 | return this; 436 | } 437 | 438 | /** 439 | * 启动加载中动画 440 | */ 441 | private void startLoadingAnim() { 442 | AnimatorSet animatorSet = getDownloadingAnim(); 443 | animatorSet.start(); 444 | } 445 | 446 | /** 447 | * 启动开始动画 448 | */ 449 | private void startStartAnim() { 450 | AnimatorSet animatorSet = getDownloadStartAnim(); 451 | animatorSet.addListener(new AnimatorListenerAdapter() { 452 | @Override 453 | public void onAnimationEnd(Animator animation) { 454 | super.onAnimationEnd(animation); 455 | animState = -1; 456 | onStartAnimEnd(); 457 | startLoadingAnim(); 458 | } 459 | }); 460 | animatorSet.start(); 461 | } 462 | 463 | private void startEndAnim() { 464 | waveAnim.cancel(); 465 | AnimatorSet animatorSet = getDownloadEndAnim(); 466 | animatorSet.addListener(new AnimatorListenerAdapter() { 467 | @Override 468 | public void onAnimationEnd(Animator animation) { 469 | super.onAnimationEnd(animation); 470 | animState = -1; 471 | onEndAnimEnd(); 472 | } 473 | }); 474 | animatorSet.start(); 475 | } 476 | 477 | /** 478 | * 起始动画结束 回调 479 | */ 480 | protected void onStartAnimEnd() { 481 | test(); 482 | } 483 | 484 | /** 485 | * 加载结束 回调 486 | */ 487 | protected void onLoadingEnd() { 488 | 489 | } 490 | 491 | /** 492 | * 结束动画结束 回调 493 | */ 494 | protected void onEndAnimEnd() { 495 | //TODO 496 | //2秒后 重新开始 497 | new Thread(new Runnable() { 498 | @Override 499 | public void run() { 500 | try { 501 | Thread.sleep(2000); 502 | } catch (Exception e) { 503 | } 504 | post(new Runnable() { 505 | @Override 506 | public void run() { 507 | currentProgress = 0; 508 | invalidate(); 509 | startStartAnim(); 510 | } 511 | }); 512 | } 513 | }).start(); 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/JumpWhirlGraphLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Path; 11 | import android.graphics.RectF; 12 | import android.util.AttributeSet; 13 | 14 | import com.zhaoli.loadings.interpolator.DecelerateAccelerateInterpolator; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * Created by zhaoli on 2016/6/20. 21 | * 跳跃旋转图形加载 22 | */ 23 | public class JumpWhirlGraphLoading extends BaseLoading { 24 | 25 | private final static int BACK_GROUND_COLOR = Color.parseColor("#2B2C30"); 26 | private final static int SQUARE_COLOR = Color.parseColor("#E69D31"); 27 | private final static int ROUND_COLOR = Color.parseColor("#E83131"); 28 | private final static int TRIANGLE_COLOR = Color.parseColor("#318CEA"); 29 | 30 | private final static int LINE_COLOR = Color.parseColor("#999DAC"); 31 | 32 | private final static int ROUND_RADIUS = 90; //圆的半径 33 | private final static int SQUARE_LENGTH = 2 * ROUND_RADIUS; //正方形边长 34 | private final static int SQUARE_RADIUS = 30; //正方形圆角 35 | private final static int TRIANGLE_LENGTH = (int) (SQUARE_LENGTH / Math.cos(Math.toRadians(45))); //三角形边长 36 | private final static int TRIANGLE_IN_LENGTH = (int) (TRIANGLE_LENGTH * Math.cos(Math.toRadians(30)) - 37 | (TRIANGLE_LENGTH / 2 * Math.sin(Math.toRadians(30)))); //三角形内边长 38 | 39 | private final static int LINE_LENGTH = TRIANGLE_LENGTH; 40 | private final static int LINE_LENGTH_MIN = TRIANGLE_LENGTH / 4; 41 | private final static int LINE_HEIGHT = 6; //px 42 | private final static float LINE_M_T_SCALE = ((float) 170) / 225; //margin_top/height 43 | 44 | private final static int JUMP_HEIGHT = 2 * SQUARE_LENGTH; //2倍的正方形边长 45 | 46 | private final static float sin_30 = (float) Math.sin(Math.toRadians(30)); 47 | private final static float cos_30 = (float) Math.cos(Math.toRadians(30)); 48 | 49 | private final static int DURATION = 1000; 50 | 51 | private int currentWhirlAngle = 0; //当前旋转角度 52 | private int currentJumpHeight = 0; //当前跳转高度 53 | private int currentLineLength = LINE_LENGTH; //当前线长度 54 | 55 | private int graphIndex = 0; //图形index = 0 圆 1 正方形 2 三角形 56 | 57 | private Path path = new Path(); 58 | private List animatorList; 59 | 60 | private RectF rectF = new RectF(); 61 | 62 | public JumpWhirlGraphLoading(Context context) { 63 | super(context); 64 | initAnim(); 65 | } 66 | 67 | public JumpWhirlGraphLoading(Context context, AttributeSet attrs) { 68 | super(context, attrs); 69 | initAnim(); 70 | } 71 | 72 | public JumpWhirlGraphLoading(Context context, AttributeSet attrs, int defStyleAttr) { 73 | super(context, attrs, defStyleAttr); 74 | initAnim(); 75 | } 76 | 77 | @Override 78 | protected void onDraw(Canvas canvas) { 79 | super.onDraw(canvas); 80 | 81 | float startY = getHeight() * LINE_M_T_SCALE; 82 | canvas.translate(getWidth() / 2, startY); 83 | //画水平线 84 | loadingPaint.setStrokeWidth(LINE_HEIGHT); 85 | loadingPaint.setColor(LINE_COLOR); 86 | canvas.drawLine(-currentLineLength / 2, 0, currentLineLength / 2, 0, loadingPaint); 87 | 88 | //画跳转图形(圆/正方形/三角形) 89 | if (graphIndex == 0) { 90 | canvas.translate(0, -currentJumpHeight - ROUND_RADIUS); 91 | } else if (graphIndex == 1) { 92 | canvas.translate(0, -currentJumpHeight - SQUARE_LENGTH / 2); 93 | canvas.rotate(currentWhirlAngle); //旋转画布 94 | } else { 95 | canvas.translate(0, -currentJumpHeight - (TRIANGLE_LENGTH * cos_30 - TRIANGLE_IN_LENGTH)); 96 | canvas.rotate(-currentWhirlAngle); //旋转画布 97 | } 98 | switch (graphIndex) { 99 | case 0: 100 | loadingPaint.setColor(ROUND_COLOR); 101 | canvas.drawCircle(0, 0, ROUND_RADIUS, loadingPaint); 102 | return; 103 | case 1: 104 | loadingPaint.setColor(SQUARE_COLOR); 105 | rectF.left = -SQUARE_LENGTH / 2; 106 | rectF.top = -SQUARE_LENGTH / 2; 107 | rectF.right = SQUARE_LENGTH / 2; 108 | rectF.bottom = SQUARE_LENGTH / 2; 109 | canvas.drawRoundRect(rectF, SQUARE_RADIUS, SQUARE_RADIUS, loadingPaint); 110 | break; 111 | case 2: 112 | loadingPaint.setColor(TRIANGLE_COLOR); 113 | path.reset(); 114 | path.moveTo((int) (-TRIANGLE_LENGTH * sin_30), 115 | (int) (TRIANGLE_LENGTH * cos_30 - TRIANGLE_IN_LENGTH)); 116 | path.lineTo(0, -TRIANGLE_IN_LENGTH); 117 | path.lineTo((int) (TRIANGLE_LENGTH * sin_30), 118 | (int) (TRIANGLE_LENGTH * cos_30 - TRIANGLE_IN_LENGTH)); 119 | path.close(); 120 | canvas.drawPath(path, loadingPaint); 121 | break; 122 | } 123 | } 124 | 125 | @Override 126 | protected void initLoading() { 127 | setBackgroundColor(BACK_GROUND_COLOR); 128 | 129 | animatorList = new ArrayList<>(); 130 | 131 | AnimatorSet animatorSet = new AnimatorSet(); 132 | 133 | animatorList.add(getRoundAnimator()); 134 | animatorList.add(getSquareAnimator()); 135 | animatorList.add(getTriangleAnimator()); 136 | 137 | for (int i = 0; i < animatorList.size(); i ++) { 138 | animatorList.get(i).addListener(new AnimatorListenerAdapter() { 139 | @Override 140 | public void onAnimationEnd(Animator animation) { 141 | super.onAnimationEnd(animation); 142 | graphIndex ++; 143 | if (graphIndex == 3) { 144 | graphIndex = 0; 145 | } 146 | } 147 | }); 148 | } 149 | 150 | animatorSet.playSequentially(animatorList); 151 | animatorSet.addListener(new AnimatorListenerAdapter() { 152 | @Override 153 | public void onAnimationEnd(Animator animation) { 154 | super.onAnimationEnd(animation); 155 | animation.start(); 156 | } 157 | }); 158 | animatorSet.start(); 159 | } 160 | 161 | @Override 162 | protected void initLoadingPaint() { 163 | loadingPaint.setAntiAlias(true); 164 | } 165 | 166 | private AnimatorSet getRoundAnimator() { 167 | AnimatorSet animatorSet = new AnimatorSet(); 168 | 169 | ValueAnimator whirlAnim = getWhirlAnim(90); 170 | ValueAnimator lineAnim = getLineAnim(); 171 | ValueAnimator jumpAnim = getJumpAnim(); 172 | 173 | animatorSet.playTogether(whirlAnim, lineAnim, jumpAnim); 174 | animatorSet.setDuration(DURATION); 175 | return animatorSet; 176 | } 177 | 178 | private AnimatorSet getSquareAnimator() { 179 | AnimatorSet animatorSet = new AnimatorSet(); 180 | 181 | ValueAnimator whirlAnim = getWhirlAnim(90); 182 | ValueAnimator lineAnim = getLineAnim(); 183 | ValueAnimator jumpAnim = getJumpAnim(); 184 | 185 | animatorSet.playTogether(whirlAnim, lineAnim, jumpAnim); 186 | animatorSet.setDuration(DURATION); 187 | return animatorSet; 188 | } 189 | 190 | private AnimatorSet getTriangleAnimator() { 191 | AnimatorSet animatorSet = new AnimatorSet(); 192 | 193 | ValueAnimator whirlAnim = getWhirlAnim(120); 194 | ValueAnimator lineAnim = getLineAnim(); 195 | ValueAnimator jumpAnim = getJumpAnim(); 196 | 197 | animatorSet.playTogether(whirlAnim, lineAnim, jumpAnim); 198 | animatorSet.setDuration(DURATION); 199 | return animatorSet; 200 | } 201 | 202 | private ValueAnimator getLineAnim() { 203 | ValueAnimator lineAnim = ValueAnimator.ofInt(LINE_LENGTH 204 | , LINE_LENGTH_MIN, LINE_LENGTH); 205 | lineAnim.setDuration(DURATION); 206 | lineAnim.setInterpolator(new DecelerateAccelerateInterpolator()); 207 | lineAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 208 | @Override 209 | public void onAnimationUpdate(ValueAnimator animation) { 210 | currentLineLength = (int) animation.getAnimatedValue(); 211 | invalidate(); 212 | } 213 | }); 214 | return lineAnim; 215 | } 216 | 217 | private ValueAnimator getJumpAnim() { 218 | ValueAnimator jumpAnim = ValueAnimator.ofInt(0, JUMP_HEIGHT, 0); 219 | jumpAnim.setDuration(DURATION); 220 | jumpAnim.setInterpolator(new DecelerateAccelerateInterpolator()); 221 | jumpAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 222 | @Override 223 | public void onAnimationUpdate(ValueAnimator animation) { 224 | currentJumpHeight = (int) animation.getAnimatedValue(); 225 | invalidate(); 226 | } 227 | }); 228 | return jumpAnim; 229 | } 230 | 231 | private ValueAnimator getWhirlAnim(int angle) { 232 | ValueAnimator whirlAnim = ValueAnimator.ofInt(0, angle, 2 * angle); 233 | whirlAnim.setDuration(DURATION); 234 | whirlAnim.setInterpolator(new DecelerateAccelerateInterpolator()); 235 | whirlAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 236 | @Override 237 | public void onAnimationUpdate(ValueAnimator animation) { 238 | currentWhirlAngle = (int) animation.getAnimatedValue(); 239 | invalidate(); 240 | } 241 | }); 242 | return whirlAnim; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/LineWhirlLoading1.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.RectF; 11 | import android.util.AttributeSet; 12 | import android.view.animation.AccelerateInterpolator; 13 | import android.view.animation.DecelerateInterpolator; 14 | import android.view.animation.LinearInterpolator; 15 | 16 | 17 | /** 18 | * Created by zhaoli on 2016/6/22. 19 | * 线条旋转1 20 | */ 21 | public class LineWhirlLoading1 extends BaseLoading { 22 | 23 | private final static int BACK_GROUND_COLOR = Color.parseColor("#836AFF"); 24 | private final static int LINE_COLOR = Color.parseColor("#FEFEFF"); 25 | 26 | private final static int CIRCULAR_RADIUS = 100; 27 | private final static int LINE_WIDTH = 10; 28 | private final static float P_CIRCULAR = (float) (2 * Math.PI * CIRCULAR_RADIUS); //周长 29 | private final static float MIN_OFFSET = LINE_WIDTH; 30 | private final static float MAX_OFFSET = P_CIRCULAR / 2 - 2 * MIN_OFFSET; 31 | 32 | private int currentAngle = 0; 33 | private float currentLength = MAX_OFFSET; 34 | private int currentRadius = 0; 35 | 36 | private ValueAnimator.AnimatorUpdateListener angleUpdateListener; 37 | private ValueAnimator.AnimatorUpdateListener lengthUpdateListener; 38 | private ValueAnimator.AnimatorUpdateListener scaleUpdateListener; 39 | 40 | private RectF rectF = new RectF(); 41 | 42 | public LineWhirlLoading1(Context context) { 43 | super(context); 44 | initAnim(); 45 | } 46 | 47 | public LineWhirlLoading1(Context context, AttributeSet attrs) { 48 | super(context, attrs); 49 | initAnim(); 50 | } 51 | 52 | public LineWhirlLoading1(Context context, AttributeSet attrs, int defStyleAttr) { 53 | super(context, attrs, defStyleAttr); 54 | initAnim(); 55 | } 56 | 57 | @Override 58 | protected void onDraw(Canvas canvas) { 59 | super.onDraw(canvas); 60 | 61 | canvas.translate(getWidth() / 2, getHeight() / 2); 62 | 63 | canvas.rotate(currentAngle); 64 | int lengthToAngle = (int) (currentLength / P_CIRCULAR * 360); //弧长角度 65 | 66 | rectF.set(-CIRCULAR_RADIUS + currentRadius, 67 | -CIRCULAR_RADIUS + currentRadius, 68 | CIRCULAR_RADIUS - currentRadius, 69 | CIRCULAR_RADIUS - currentRadius); 70 | loadingPaint.setColor(LINE_COLOR); 71 | canvas.drawArc(rectF, lengthToAngle / 2, lengthToAngle, true, loadingPaint); 72 | canvas.rotate(180); 73 | canvas.drawArc(rectF, lengthToAngle / 2, lengthToAngle, true, loadingPaint); 74 | 75 | loadingPaint.setColor(BACK_GROUND_COLOR); 76 | canvas.drawCircle(0, 0, CIRCULAR_RADIUS - LINE_WIDTH - currentRadius, loadingPaint); 77 | } 78 | 79 | @Override 80 | protected void initLoading() { 81 | setBackgroundColor(BACK_GROUND_COLOR); 82 | 83 | AnimatorSet allAnim = new AnimatorSet(); 84 | 85 | AnimatorSet animatorSet1 = getWhirlAnim(); 86 | animatorSet1.setDuration(800); 87 | AnimatorSet animatorSet2 = getWhirlAnim(); 88 | animatorSet2.setDuration(800); 89 | 90 | AnimatorSet animatorSet3 = new AnimatorSet(); 91 | 92 | AnimatorSet animatorSet4 = getWhirlAnim(800); 93 | //TODO 此处有坑,慎踩 94 | /** 95 | * 例如:animatorSet1.playSequentially(A1, A2, A3); 96 | * animatorSet2.playSequentially(animatorSet1, A4); 97 | * animatorSet2.setDuration(time); 98 | * 运行起来:A1和A4同时执行,之后执行A2,A3 99 | * 100 | * 因此,必须在A1,A2,A3,A4中设置时间 101 | */ 102 | animatorSet3.playTogether(animatorSet4, getScaleAnim()); 103 | 104 | allAnim.playSequentially(animatorSet1, animatorSet2, animatorSet3); 105 | allAnim.setInterpolator(new LinearInterpolator()); 106 | allAnim.addListener(new AnimatorListenerAdapter() { 107 | @Override 108 | public void onAnimationEnd(Animator animation) { 109 | super.onAnimationEnd(animation); 110 | animation.start(); 111 | } 112 | }); 113 | 114 | allAnim.start(); 115 | } 116 | 117 | @Override 118 | protected void initLoadingPaint() { 119 | loadingPaint.setAntiAlias(true); 120 | } 121 | 122 | private AnimatorSet getWhirlAnim() { 123 | return getWhirlAnim(0); 124 | } 125 | 126 | private AnimatorSet getWhirlAnim(int time) { 127 | AnimatorSet animatorSet = new AnimatorSet(); 128 | ValueAnimator constantSpeedWhirlAnim = getConstantSpeedWhirlAnim(); 129 | AnimatorSet whirlToMinAnim = getWhirlToMinAnim(); 130 | AnimatorSet whirlToMaxAnim = getWhirlToMaxAnim(); 131 | animatorSet.playSequentially(constantSpeedWhirlAnim, whirlToMinAnim, whirlToMaxAnim); 132 | 133 | if (time != 0) { 134 | constantSpeedWhirlAnim.setDuration(time); 135 | whirlToMinAnim.setDuration(time); 136 | whirlToMaxAnim.setDuration(time); 137 | } 138 | return animatorSet; 139 | } 140 | 141 | private ValueAnimator getConstantSpeedWhirlAnim() { 142 | ValueAnimator valueAnimator = ValueAnimator.ofInt(135, 225); 143 | valueAnimator.setInterpolator(new LinearInterpolator()); 144 | valueAnimator.addUpdateListener(getAngleUpdateListener()); 145 | return valueAnimator; 146 | } 147 | 148 | private AnimatorSet getWhirlToMinAnim() { 149 | AnimatorSet animatorSet = new AnimatorSet(); 150 | 151 | ValueAnimator whirlAnim = ValueAnimator.ofInt(225, 540); 152 | whirlAnim.setInterpolator(new AccelerateInterpolator()); 153 | whirlAnim.addUpdateListener(getAngleUpdateListener()); 154 | 155 | ValueAnimator lengthAnim = ValueAnimator.ofFloat(MAX_OFFSET, MIN_OFFSET); 156 | lengthAnim.setInterpolator(new AccelerateInterpolator()); 157 | lengthAnim.addUpdateListener(getLengthUpdateListener()); 158 | 159 | animatorSet.playTogether(whirlAnim, lengthAnim); 160 | return animatorSet; 161 | } 162 | 163 | private AnimatorSet getWhirlToMaxAnim() { 164 | AnimatorSet animatorSet = new AnimatorSet(); 165 | 166 | ValueAnimator whirlAnim = ValueAnimator.ofInt(180, 495); 167 | whirlAnim.setInterpolator(new DecelerateInterpolator()); 168 | whirlAnim.addUpdateListener(getAngleUpdateListener()); 169 | 170 | ValueAnimator lengthAnim = ValueAnimator.ofFloat(MIN_OFFSET, MAX_OFFSET); 171 | lengthAnim.setInterpolator(new DecelerateInterpolator()); 172 | lengthAnim.addUpdateListener(getLengthUpdateListener()); 173 | 174 | animatorSet.playTogether(whirlAnim, lengthAnim); 175 | return animatorSet; 176 | } 177 | 178 | private ValueAnimator getScaleAnim() { 179 | ValueAnimator valueAnimator = ValueAnimator.ofInt(CIRCULAR_RADIUS, 0, CIRCULAR_RADIUS); 180 | valueAnimator.setInterpolator(new LinearInterpolator()); 181 | valueAnimator.addUpdateListener(getScaleUpdateListener()); 182 | valueAnimator.setDuration(2400); 183 | return valueAnimator; 184 | } 185 | 186 | private ValueAnimator.AnimatorUpdateListener getAngleUpdateListener() { 187 | if (angleUpdateListener == null) { 188 | angleUpdateListener = new ValueAnimator.AnimatorUpdateListener() { 189 | @Override 190 | public void onAnimationUpdate(ValueAnimator animation) { 191 | currentAngle = (int) animation.getAnimatedValue(); 192 | if (currentAngle >= 360) { 193 | currentAngle -= 360; 194 | } 195 | invalidate(); 196 | } 197 | }; 198 | } 199 | return angleUpdateListener; 200 | } 201 | 202 | private ValueAnimator.AnimatorUpdateListener getLengthUpdateListener() { 203 | if (lengthUpdateListener == null) { 204 | lengthUpdateListener = new ValueAnimator.AnimatorUpdateListener() { 205 | @Override 206 | public void onAnimationUpdate(ValueAnimator animation) { 207 | currentLength = (float) animation.getAnimatedValue(); 208 | invalidate(); 209 | } 210 | }; 211 | } 212 | return lengthUpdateListener; 213 | } 214 | 215 | private ValueAnimator.AnimatorUpdateListener getScaleUpdateListener() { 216 | if (scaleUpdateListener == null) { 217 | scaleUpdateListener = new ValueAnimator.AnimatorUpdateListener() { 218 | @Override 219 | public void onAnimationUpdate(ValueAnimator animation) { 220 | currentRadius =CIRCULAR_RADIUS - (int) animation.getAnimatedValue(); 221 | invalidate(); 222 | } 223 | }; 224 | } 225 | return scaleUpdateListener; 226 | } 227 | 228 | 229 | } 230 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/LineWhirlLoading2.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.RectF; 11 | import android.util.AttributeSet; 12 | import android.view.animation.AccelerateInterpolator; 13 | import android.view.animation.DecelerateInterpolator; 14 | 15 | /** 16 | * Created by zhaoli on 2016/6/22. 17 | * 线条旋转2 18 | */ 19 | public class LineWhirlLoading2 extends BaseLoading{ 20 | 21 | private final static int BACK_GROUND_COLOR = Color.parseColor("#2C3E50"); 22 | private final static int LINE_COLOR = Color.parseColor("#FAFBFC"); 23 | 24 | private final static int CIRCULAR_RADIUS = 100; 25 | private final static int LINE_WIDTH = 10; 26 | 27 | private int currentAngle1 = 0; 28 | private int currentAngle2 = 0; 29 | 30 | private RectF rectF = new RectF(); 31 | 32 | public LineWhirlLoading2(Context context) { 33 | super(context); 34 | initAnim(); 35 | } 36 | 37 | public LineWhirlLoading2(Context context, AttributeSet attrs) { 38 | super(context, attrs); 39 | initAnim(); 40 | } 41 | 42 | public LineWhirlLoading2(Context context, AttributeSet attrs, int defStyleAttr) { 43 | super(context, attrs, defStyleAttr); 44 | initAnim(); 45 | } 46 | 47 | @Override 48 | protected void onDraw(Canvas canvas) { 49 | super.onDraw(canvas); 50 | canvas.translate(getWidth() / 2, getHeight() / 2); 51 | canvas.rotate(-90); 52 | 53 | rectF.set(-CIRCULAR_RADIUS, -CIRCULAR_RADIUS, CIRCULAR_RADIUS, CIRCULAR_RADIUS); 54 | loadingPaint.setColor(LINE_COLOR); 55 | canvas.drawArc(rectF, currentAngle2, currentAngle1 - currentAngle2, true, loadingPaint); 56 | 57 | loadingPaint.setColor(BACK_GROUND_COLOR); 58 | canvas.drawCircle(0, 0, CIRCULAR_RADIUS - LINE_WIDTH, loadingPaint); 59 | } 60 | 61 | @Override 62 | protected void initLoading() { 63 | setBackgroundColor(BACK_GROUND_COLOR); 64 | 65 | AnimatorSet animatorSet = new AnimatorSet(); 66 | 67 | ValueAnimator whirlAnim1 = ValueAnimator.ofInt(0, 405); 68 | whirlAnim1.setDuration(1000); 69 | whirlAnim1.setInterpolator(new AccelerateInterpolator()); 70 | whirlAnim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 71 | @Override 72 | public void onAnimationUpdate(ValueAnimator animation) { 73 | currentAngle1 = (int) animation.getAnimatedValue(); 74 | if (currentAngle1 >= 360) { 75 | currentAngle1 = 360; 76 | } 77 | invalidate(); 78 | } 79 | }); 80 | 81 | ValueAnimator whirlAnim2 = ValueAnimator.ofInt(0, 360); 82 | whirlAnim2.setDuration(1200); 83 | whirlAnim2.setStartDelay(600); 84 | whirlAnim2.setInterpolator(new DecelerateInterpolator()); 85 | whirlAnim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 86 | @Override 87 | public void onAnimationUpdate(ValueAnimator animation) { 88 | currentAngle2 = (int) animation.getAnimatedValue(); 89 | invalidate(); 90 | } 91 | }); 92 | whirlAnim2.addListener(new AnimatorListenerAdapter() { 93 | @Override 94 | public void onAnimationEnd(Animator animation) { 95 | super.onAnimationEnd(animation); 96 | currentAngle2 = 0; 97 | } 98 | }); 99 | 100 | animatorSet.playTogether(whirlAnim1, whirlAnim2); 101 | animatorSet.addListener(new AnimatorListenerAdapter() { 102 | @Override 103 | public void onAnimationEnd(Animator animation) { 104 | super.onAnimationEnd(animation); 105 | animation.start(); 106 | } 107 | }); 108 | animatorSet.start(); 109 | } 110 | 111 | @Override 112 | protected void initLoadingPaint() { 113 | loadingPaint.setAntiAlias(true); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/RotateSquareLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Path; 8 | import android.util.AttributeSet; 9 | import android.view.animation.LinearInterpolator; 10 | 11 | /** 12 | * Created by zhaoli on 2016/6/18. 13 | * 旋转方块加载 14 | */ 15 | public class RotateSquareLoading extends BaseLoading { 16 | 17 | private final static int SQUARE_UP_COLOR = Color.parseColor("#FED74C"); 18 | private final static int SQUARE_LEFT_COLOR = Color.parseColor("#DC9633"); 19 | private final static int SQUARE_RIGHT_COLOR = Color.parseColor("#C77532"); 20 | private final static int SQUARE_SHADOW_COLOR = Color.parseColor("#DADADA"); 21 | 22 | private final static int SQUARE_SIDE_LEGTH = 40; //px 23 | 24 | private Path path = null; 25 | private int offset = 0; //[0, SQUARE_SIDE_LEGTH] 26 | private int orientation = 0; //0 左右 1 上下 27 | 28 | private final static int angle = 60; //顶部正方形夹角 29 | private final static float sin_angle = (float) Math.sin(Math.toRadians(angle / 2)); 30 | private final static float cos_angle = (float) Math.cos(Math.toRadians(angle / 2)); 31 | 32 | private int[] xList = new int[7]; 33 | private int[] yList = new int[7]; 34 | 35 | private ValueAnimator valueAnimator; 36 | 37 | public RotateSquareLoading(Context context) { 38 | super(context); 39 | initAnim(); 40 | } 41 | 42 | public RotateSquareLoading(Context context, AttributeSet attrs) { 43 | super(context, attrs); 44 | initAnim(); 45 | } 46 | 47 | public RotateSquareLoading(Context context, AttributeSet attrs, int defStyleAttr) { 48 | super(context, attrs, defStyleAttr); 49 | initAnim(); 50 | } 51 | 52 | @Override 53 | protected void onDraw(Canvas canvas) { 54 | super.onDraw(canvas); 55 | 56 | //移动坐标,旋转 57 | canvas.translate(getWidth() / 2, getHeight() / 2); 58 | 59 | //绘制正方体(总共有4个) 60 | if (path == null) { 61 | path = new Path(); 62 | } 63 | 64 | //需要改变绘制顺序 65 | if (orientation == 0) { 66 | drawSquare(0, canvas); 67 | drawSquare(1, canvas); 68 | drawSquare(2, canvas); 69 | drawSquare(3, canvas); 70 | } else { 71 | drawSquare(2, canvas); 72 | drawSquare(0, canvas); 73 | drawSquare(3, canvas); 74 | drawSquare(1, canvas); 75 | } 76 | } 77 | 78 | @Override 79 | protected void initLoading() { 80 | valueAnimator = ValueAnimator.ofInt(SQUARE_SIDE_LEGTH, 0, -SQUARE_SIDE_LEGTH); 81 | valueAnimator.setRepeatCount(-1); 82 | valueAnimator.setDuration(1000); 83 | valueAnimator.setInterpolator(new LinearInterpolator()); 84 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 85 | @Override 86 | public void onAnimationUpdate(ValueAnimator animation) { 87 | int value = (int) animation.getAnimatedValue(); 88 | if (value > 0) { 89 | offset = SQUARE_SIDE_LEGTH - value; 90 | orientation = 0; 91 | } else { 92 | offset = -value; 93 | orientation = 1; 94 | } 95 | invalidate(); 96 | } 97 | }); 98 | valueAnimator.start(); 99 | } 100 | 101 | @Override 102 | protected void initLoadingPaint() { 103 | loadingPaint.setAntiAlias(true); 104 | } 105 | 106 | private void drawPath(int point1, int point2, int point3, int point4, int color, Canvas canvas, boolean isShadow) { 107 | int shadowLength = 0; 108 | if (isShadow) { 109 | shadowLength = 7 * SQUARE_SIDE_LEGTH / 2; 110 | } 111 | path.reset(); 112 | path.moveTo(xList[point1], yList[point1] + shadowLength); 113 | path.lineTo(xList[point2], yList[point2] + shadowLength); 114 | path.lineTo(xList[point3], yList[point3] + shadowLength); 115 | path.lineTo(xList[point4], yList[point4] + shadowLength); 116 | path.close(); 117 | loadingPaint.setColor(color); 118 | canvas.drawPath(path, loadingPaint); 119 | } 120 | 121 | private void drawSquare(int index, Canvas canvas) { 122 | 123 | //从左角开始,顺时针 124 | switch (index) { 125 | case 0: 126 | xList[0] = (int) (((orientation == 0) ? 127 | (-3 * SQUARE_SIDE_LEGTH / 2 + offset) : 128 | (-SQUARE_SIDE_LEGTH / 2)) * cos_angle); 129 | yList[0] = (int) (((orientation == 0) ? 130 | (-3 * SQUARE_SIDE_LEGTH / 2 + offset) : 131 | (-SQUARE_SIDE_LEGTH / 2)) * sin_angle); 132 | break; 133 | case 1: 134 | xList[0] = (int) (((orientation == 0) ? 135 | (-SQUARE_SIDE_LEGTH / 2 + offset) : 136 | (SQUARE_SIDE_LEGTH / 2 - offset)) * cos_angle); 137 | yList[0] = (int) (((orientation == 0) ? 138 | (-SQUARE_SIDE_LEGTH / 2 + offset) : 139 | (SQUARE_SIDE_LEGTH / 2 + offset)) * sin_angle); 140 | break; 141 | case 2: 142 | xList[0] = (int) (((orientation == 0) ? 143 | (-3 * SQUARE_SIDE_LEGTH / 2 - offset) : 144 | (-5 * SQUARE_SIDE_LEGTH / 2 + offset)) * cos_angle); 145 | yList[0] = (int) (((orientation == 0) ? 146 | (SQUARE_SIDE_LEGTH / 2 - offset) : 147 | (-SQUARE_SIDE_LEGTH / 2 - offset)) * sin_angle); 148 | break; 149 | case 3: 150 | xList[0] = (int) (((orientation == 0) ? 151 | (-SQUARE_SIDE_LEGTH / 2 - offset) : 152 | (-3 * SQUARE_SIDE_LEGTH / 2)) * cos_angle); 153 | yList[0] = (int) (((orientation == 0) ? 154 | (3 * SQUARE_SIDE_LEGTH / 2 - offset) : 155 | (SQUARE_SIDE_LEGTH / 2)) * sin_angle); 156 | break; 157 | } 158 | 159 | xList[6] = xList[0]; 160 | xList[3] = xList[2] = xList[0] + (int) (2 * SQUARE_SIDE_LEGTH * cos_angle); 161 | xList[4] = xList[5] = xList[1] = xList[0] + (int) (SQUARE_SIDE_LEGTH * cos_angle); 162 | 163 | yList[2] = yList[0]; 164 | yList[1] = yList[0] - SQUARE_SIDE_LEGTH / 2; 165 | yList[5] = yList[1] + SQUARE_SIDE_LEGTH; 166 | yList[6] = yList[3] = yList[0] + SQUARE_SIDE_LEGTH; 167 | yList[4] = yList[5] + SQUARE_SIDE_LEGTH; 168 | 169 | //绘制顶部 170 | drawPath(0, 1, 2, 5, SQUARE_UP_COLOR, canvas, false); 171 | //绘制左边 172 | drawPath(0, 5, 4, 6, SQUARE_LEFT_COLOR, canvas, false); 173 | //绘制右边 174 | drawPath(2, 3, 4, 5, SQUARE_RIGHT_COLOR, canvas, false); 175 | //绘制阴影 176 | drawPath(0, 1, 2, 5, SQUARE_SHADOW_COLOR, canvas, true); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/SegmentSquareLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Path; 8 | import android.util.AttributeSet; 9 | import android.view.animation.LinearInterpolator; 10 | 11 | /** 12 | * Created by zhaoli on 2016/6/19. 13 | * 分割方体加载 14 | */ 15 | public class SegmentSquareLoading extends BaseLoading { 16 | 17 | private final static int SQUARE_UP_COLOR = Color.parseColor("#1E909A"); 18 | private final static int SQUARE_LEFT_COLOR = Color.parseColor("#D53B33"); 19 | private final static int SQUARE_RIGHT_COLOR = Color.parseColor("#E79C0F"); 20 | private final static int BACKGROUND_COLOR = Color.parseColor("#262626"); 21 | 22 | private final static int SQUARE_LENGTH = 40; //px 23 | 24 | private final static int angle = 60; //顶部正方形夹角 25 | private final static float sin_angle = (float) Math.sin(Math.toRadians(angle / 2)); 26 | private final static float cos_angle = (float) Math.cos(Math.toRadians(angle / 2)); 27 | 28 | private Path path = null; 29 | 30 | private int orientation = 0; //方向 0 左右 1 前后 2 上下 31 | private int offset = 0; //距离 [0, SQUARE_LENGTH / 3] 32 | 33 | //中心方体的坐标(是不变的) 34 | private int[] centerXList; 35 | private int[] centerYList; 36 | 37 | private int[] xList = new int[7]; 38 | private int[] yList = new int[7]; 39 | 40 | private ValueAnimator valueAnimator; 41 | 42 | public SegmentSquareLoading(Context context) { 43 | super(context); 44 | initAnim(); 45 | } 46 | 47 | public SegmentSquareLoading(Context context, AttributeSet attrs) { 48 | super(context, attrs); 49 | initAnim(); 50 | } 51 | 52 | public SegmentSquareLoading(Context context, AttributeSet attrs, int defStyleAttr) { 53 | super(context, attrs, defStyleAttr); 54 | initAnim(); 55 | } 56 | 57 | @Override 58 | protected void onDraw(Canvas canvas) { 59 | super.onDraw(canvas); 60 | 61 | if (path == null) { 62 | path = new Path(); 63 | } 64 | 65 | canvas.translate(getWidth() / 2, getHeight() / 2); 66 | 67 | for (int i = 0; i < 9; i ++) { 68 | for (int j = 2; j >= 0; j --) { 69 | drawSquare(i, j, canvas); 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * 获取偏移量 76 | * @param index [0,9] 77 | * @return 偏移量 78 | */ 79 | private int getOffsetX(int index) { 80 | if (orientation == 0) { 81 | if (index % 3 == 1) { 82 | return 0; 83 | } else if (index % 3 == 0) { 84 | return (int) (-offset * cos_angle); 85 | } else { 86 | return (int) (offset * cos_angle); 87 | } 88 | } else if (orientation == 1) { 89 | if (index / 3 == 1) { 90 | return 0; 91 | } else if (index / 3 == 0){ 92 | return (int) (offset * cos_angle); 93 | } else { 94 | return (int) (-offset * cos_angle); 95 | } 96 | } else { 97 | return 0; 98 | } 99 | } 100 | 101 | private int getOffsetY(int index, int layout) { 102 | if (orientation == 0) { 103 | if (index % 3 == 1) { 104 | return 0; 105 | } else if (index % 3 == 0) { 106 | return (int) (-offset * sin_angle); 107 | } else { 108 | return (int) (offset * sin_angle); 109 | } 110 | } else if (orientation == 1) { 111 | if (index / 3 == 1) { 112 | return 0; 113 | } else if (index / 3 == 0){ 114 | return (int) (-offset * sin_angle); 115 | } else { 116 | return (int) (offset * sin_angle); 117 | } 118 | } else { 119 | if (layout % 3 == 1) { 120 | return 0; 121 | } else if (layout % 3 == 0) { 122 | return -offset; 123 | } else { 124 | return offset; 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * 获取起始点X坐标 131 | * @param index 顺序 132 | * @return X坐标 133 | */ 134 | private int getStartX(int index) { 135 | int index_3 = index % 3; 136 | int index3_ = index / 3; 137 | if (orientation == 0) { 138 | if (offset >= 0) { 139 | //正向 140 | return (int) ((index_3 - (index3_ + 1)) * SQUARE_LENGTH * cos_angle); 141 | } else { 142 | //反向 143 | return (int) ((index_3 - (index3_ + 1) + 144 | (index_3 == 0 ? -1 : (index_3 == 1 ? 0 : 1)) + 145 | (index3_ == 0 ? 1 : (index3_ == 1) ? 0 : -1)) * SQUARE_LENGTH * cos_angle); 146 | } 147 | } else if (orientation == 1) { 148 | if (offset >= 0) { 149 | return (int) ((index_3 - (index3_ + 1) + 150 | (index_3 == 0 ? -1 : (index_3 == 1 ? 0 : 1))) * SQUARE_LENGTH * cos_angle); 151 | } else { 152 | return (int) ((index_3 - (index3_ + 1) + 153 | (index3_ == 0 ? 1 : (index3_ == 1) ? 0 : -1)) * SQUARE_LENGTH * cos_angle); 154 | } 155 | } else { 156 | if (offset >= 0) { 157 | return (int) ((index_3 - (index3_ + 1) + 158 | (index_3 == 0 ? -1 : (index_3 == 1 ? 0 : 1)) + 159 | (index3_ == 0 ? 1 : (index3_ == 1) ? 0 : -1)) * SQUARE_LENGTH * cos_angle); 160 | } else { 161 | return (int) ((index_3 - (index3_ + 1)) * SQUARE_LENGTH * cos_angle); 162 | } 163 | } 164 | } 165 | 166 | /** 167 | * 获取起始点Y坐标 168 | * @param index 顺序 169 | * @param layout 层 170 | * @return Y坐标 171 | */ 172 | private int getStartY(int index, int layout) { 173 | int index_3 = index % 3; 174 | int index3_ = index / 3; 175 | int y; 176 | if (orientation == 0) { 177 | if (offset >= 0) { 178 | y = (int) ((index3_ + index_3 - 2) * SQUARE_LENGTH * sin_angle); 179 | } else { 180 | y = (int) ((index3_ + index_3 - 2 + 181 | (index_3 == 0 ? -1 : (index_3 == 1 ? 0 : 1)) + 182 | (index3_ == 0 ? -1 : (index3_ == 1) ? 0 : 1)) * SQUARE_LENGTH * sin_angle) + 183 | (layout == 0 ? -1 : (layout == 1) ? 0 : 1) * SQUARE_LENGTH; 184 | } 185 | } else if (orientation == 1) { 186 | if (offset >= 0) { 187 | y = (int) ((index3_ + index_3 - 2 + 188 | (index_3 == 0 ? -1 : (index_3 == 1 ? 0 : 1))) * SQUARE_LENGTH * sin_angle); 189 | } else { 190 | y = (int) ((index3_ + index_3 - 2 + 191 | (index3_ == 0 ? -1 : (index3_ == 1) ? 0 : 1)) * SQUARE_LENGTH * sin_angle) + 192 | (layout == 0 ? -1 : (layout == 1) ? 0 : 1) * SQUARE_LENGTH; 193 | } 194 | } else { 195 | if (offset >= 0) { 196 | y = (int) ((index3_ + index_3 - 2 + 197 | (index_3 == 0 ? -1 : (index_3 == 1 ? 0 : 1)) + 198 | (index3_ == 0 ? -1 : (index3_ == 1) ? 0 : 1)) * SQUARE_LENGTH * sin_angle); 199 | } else { 200 | y = (int) ((index3_ + index_3 - 2) * SQUARE_LENGTH * sin_angle) + 201 | (layout == 0 ? -1 : (layout == 1) ? 0 : 1) * SQUARE_LENGTH; 202 | } 203 | } 204 | y += (layout == 0 ? -1 : (layout == 1) ? 0 : 1) * SQUARE_LENGTH; 205 | return y; 206 | } 207 | 208 | private void drawSquare(int index, int layer, Canvas canvas) { 209 | //先计算最中心的方体坐标,是一直不变的 210 | if (index == 4 && layer == 1) { 211 | drawSquare(centerXList, centerYList, canvas); 212 | return; 213 | } 214 | 215 | xList[0] = getStartX(index) + getOffsetX(index); 216 | yList[0] = getStartY(index, layer) + getOffsetY(index, layer); 217 | 218 | xList[6] = xList[0]; 219 | xList[5] = xList[4] = xList[1] = xList[0] + (int) (SQUARE_LENGTH * cos_angle); 220 | xList[3] = xList[2] = xList[0] + (int) (SQUARE_LENGTH * 2 * cos_angle); 221 | 222 | yList[2] = yList[0]; 223 | yList[1] = yList[0] - SQUARE_LENGTH / 2; 224 | yList[5] = yList[0] + SQUARE_LENGTH / 2; 225 | yList[4] = yList[5] + SQUARE_LENGTH; 226 | yList[6] = yList[3] = yList[0] + SQUARE_LENGTH; 227 | 228 | drawSquare(xList, yList, canvas); 229 | } 230 | 231 | private void drawSquare(int x[], int y[], Canvas canvas) { 232 | //绘制顶部 233 | drawPath(x, y, 0, 1, 2, 5, SQUARE_UP_COLOR, canvas); 234 | //绘制左边 235 | drawPath(x, y, 0, 5, 4, 6, SQUARE_LEFT_COLOR, canvas); 236 | //绘制右边 237 | drawPath(x, y, 2, 3, 4, 5, SQUARE_RIGHT_COLOR, canvas); 238 | } 239 | 240 | private void drawPath(int x[], int y[], int point1, int point2, int point3, int point4, int color, Canvas canvas) { 241 | path.reset(); 242 | path.moveTo(x[point1], y[point1]); 243 | path.lineTo(x[point2], y[point2]); 244 | path.lineTo(x[point3], y[point3]); 245 | path.lineTo(x[point4], y[point4]); 246 | path.close(); 247 | loadingPaint.setColor(color); 248 | canvas.drawPath(path, loadingPaint); 249 | } 250 | 251 | @Override 252 | protected void initLoading() { 253 | setBackgroundColor(BACKGROUND_COLOR); 254 | 255 | centerXList = new int[7]; 256 | centerYList = new int[7]; 257 | 258 | //计算中心方体的坐标 259 | centerXList[6] = centerXList[0] = (int) (-SQUARE_LENGTH * cos_angle); 260 | centerXList[5] = centerXList[4] = centerXList[1] = 0; 261 | centerXList[3] = centerXList[2] = -centerXList[0]; 262 | 263 | centerYList[2] = centerYList[0] = 0; 264 | centerYList[1] = (int) (centerYList[0] - SQUARE_LENGTH * sin_angle); 265 | centerYList[5] = SQUARE_LENGTH / 2; 266 | centerYList[4] = SQUARE_LENGTH; 267 | centerYList[6] = centerYList[3] = SQUARE_LENGTH / 2; 268 | 269 | 270 | valueAnimator = ValueAnimator.ofInt(3 * SQUARE_LENGTH, 2 * SQUARE_LENGTH, SQUARE_LENGTH, 0, 271 | - SQUARE_LENGTH, -2 * SQUARE_LENGTH, -3 * SQUARE_LENGTH); 272 | valueAnimator.setDuration(3000); 273 | valueAnimator.setRepeatCount(-1); 274 | valueAnimator.setInterpolator(new LinearInterpolator()); 275 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 276 | @Override 277 | public void onAnimationUpdate(ValueAnimator animation) { 278 | int value = (int) valueAnimator.getAnimatedValue(); 279 | if (value >= 2 * SQUARE_LENGTH || (value < 0 && value >= -SQUARE_LENGTH)) { 280 | orientation = 0; 281 | offset = (value > 0) ? (3 * SQUARE_LENGTH - value) : value; 282 | } else if ((value >= SQUARE_LENGTH && value < 2 * SQUARE_LENGTH) || 283 | (value < -SQUARE_LENGTH & value >= -2 * SQUARE_LENGTH)) { 284 | orientation = 1; 285 | offset = (value > 0) ? (2 * SQUARE_LENGTH - value) : (value + SQUARE_LENGTH); 286 | } else { 287 | orientation = 2; 288 | offset = (value >= 0 ) ? (SQUARE_LENGTH - value) : (value + 2 * SQUARE_LENGTH); 289 | } 290 | invalidate(); 291 | } 292 | }); 293 | valueAnimator.start(); 294 | } 295 | 296 | @Override 297 | protected void initLoadingPaint() { 298 | loadingPaint.setAntiAlias(true); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/SwingCollisionLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.LinearGradient; 11 | import android.graphics.RectF; 12 | import android.graphics.Shader; 13 | import android.util.AttributeSet; 14 | import android.view.animation.AccelerateInterpolator; 15 | import android.view.animation.DecelerateInterpolator; 16 | 17 | /** 18 | * Created by zhaoli on 2016/6/17. 19 | * 20 | * 摇摆碰撞加载 21 | */ 22 | public class SwingCollisionLoading extends BaseLoading { 23 | 24 | private final static int BACK_GROUND_COLOR = Color.parseColor("#ECE8DE"); 25 | private final static int BALL_START_COLOR = Color.parseColor("#355773"); 26 | private final static int BALL_END_COLOR = Color.parseColor("#D73B27"); 27 | 28 | private final static int BALL_START_SHADOW_COLOR = Color.parseColor("#33355773"); 29 | private final static int BALL_END_SHADOW_COLOR = Color.parseColor("#33D73B27"); 30 | 31 | private final static int BALL_RADIUS = 16; //px 32 | private final static int BALL_MAX = 7; 33 | 34 | private LinearGradient linearGradient = null; 35 | private LinearGradient shadowLinearGradient = null; 36 | private float currentAngle = 0; 37 | 38 | private RectF shadowRectF = new RectF(); 39 | 40 | public SwingCollisionLoading(Context context) { 41 | super(context); 42 | initAnim(); 43 | } 44 | 45 | public SwingCollisionLoading(Context context, AttributeSet attrs) { 46 | super(context, attrs); 47 | initAnim(); 48 | } 49 | 50 | public SwingCollisionLoading(Context context, AttributeSet attrs, int defStyleAttr) { 51 | super(context, attrs, defStyleAttr); 52 | initAnim(); 53 | } 54 | 55 | @Override 56 | protected void onDraw(Canvas canvas) { 57 | super.onDraw(canvas); 58 | 59 | //线条长度 60 | int lineLength = BALL_RADIUS * 10; 61 | 62 | //第一步:绘制球 63 | int startX = (getWidth() - (BALL_MAX * BALL_RADIUS * 2)) / 2; 64 | int startY = (getHeight() - BALL_RADIUS * 2) / 2; 65 | 66 | if (linearGradient == null) { 67 | linearGradient = new LinearGradient(startX, startY, 68 | startX + BALL_MAX * BALL_RADIUS * 2, startY, 69 | BALL_START_COLOR, BALL_END_COLOR, Shader.TileMode.MIRROR); 70 | } 71 | 72 | //设置渐变 73 | loadingPaint.setShader(linearGradient); 74 | 75 | int index = 0; 76 | if (currentAngle > 0) { 77 | index = 1; 78 | } 79 | 80 | //绘制固定的球 81 | for (; index < ((currentAngle >= 0) ? BALL_MAX : (BALL_MAX - 1)); index ++) { 82 | canvas.drawCircle(startX + index * BALL_RADIUS * 2 + BALL_RADIUS, 83 | startY + BALL_RADIUS, 84 | BALL_RADIUS, 85 | loadingPaint); 86 | } 87 | 88 | //绘制动的球 89 | if (currentAngle > 0) { 90 | //球在左边翘起 91 | int leftX = (int) (startX + BALL_RADIUS * 3 - lineLength * Math.sin(Math.toRadians(currentAngle))); 92 | int leftY = (int) (getHeight() / 2 - lineLength + lineLength * Math.cos(Math.toRadians(currentAngle))); 93 | System.out.println(); 94 | canvas.drawCircle(leftX, 95 | leftY, 96 | BALL_RADIUS, 97 | loadingPaint); 98 | } else if (currentAngle < 0) { 99 | //球在右边翘起 100 | int rightX = (int) (int) (startX + BALL_RADIUS * (BALL_MAX * 2 - 3) + 101 | lineLength * Math.sin(Math.toRadians(-currentAngle))); 102 | int rightY = (int) (getHeight() / 2 - lineLength + 103 | lineLength * Math.cos(Math.toRadians(-currentAngle))); 104 | canvas.drawCircle(rightX, 105 | rightY, 106 | BALL_RADIUS, 107 | loadingPaint); 108 | } 109 | 110 | //第二步:绘制阴影 111 | int shadowStartY = startY + BALL_RADIUS * 4; 112 | 113 | if (shadowLinearGradient == null) { 114 | shadowLinearGradient = new LinearGradient(startX, shadowStartY, 115 | startX + BALL_MAX * BALL_RADIUS * 2, shadowStartY, 116 | BALL_START_SHADOW_COLOR, BALL_END_SHADOW_COLOR, Shader.TileMode.MIRROR); 117 | } 118 | 119 | //设置渐变 120 | loadingPaint.setShader(shadowLinearGradient); 121 | for (index = (currentAngle > 0) ? 1 : 0; index < ((currentAngle > 0) ? BALL_MAX : (BALL_MAX - 1)); index ++) { 122 | shadowRectF.left = startX + index * BALL_RADIUS * 2; 123 | shadowRectF.top = shadowStartY; 124 | shadowRectF.right = startX + BALL_RADIUS * 2 + index * BALL_RADIUS * 2; 125 | shadowRectF.bottom = shadowStartY + BALL_RADIUS / 2; 126 | canvas.drawOval(shadowRectF, loadingPaint); 127 | } 128 | } 129 | 130 | @Override 131 | protected void initLoading() { 132 | //设置背景色 133 | setBackgroundColor(BACK_GROUND_COLOR); 134 | 135 | //初始化动画 136 | AnimatorSet animatorSet = new AnimatorSet(); 137 | //计算初始角度(0.2为2/10 线摆长度为半径的10倍) 138 | float startAngle = (float) Math.toDegrees(Math.asin(0.2)); 139 | 140 | ValueAnimator animator1 = ValueAnimator.ofFloat(startAngle, 45); 141 | animator1.setInterpolator(new DecelerateInterpolator()); 142 | animator1.addUpdateListener(getAnimatorUpdateListener()); 143 | 144 | ValueAnimator animator2 = ValueAnimator.ofFloat(45, startAngle); 145 | animator2.setInterpolator(new AccelerateInterpolator()); 146 | animator2.addUpdateListener(getAnimatorUpdateListener()); 147 | 148 | ValueAnimator animator3 = ValueAnimator.ofFloat(-startAngle, -45); 149 | animator3.setInterpolator(new DecelerateInterpolator()); 150 | animator3.addUpdateListener(getAnimatorUpdateListener()); 151 | 152 | ValueAnimator animator4 = ValueAnimator.ofFloat(-45, -startAngle); 153 | animator4.setInterpolator(new AccelerateInterpolator()); 154 | animator4.addUpdateListener(getAnimatorUpdateListener()); 155 | 156 | animatorSet.playSequentially(animator1, animator2, animator3, animator4); 157 | animatorSet.setDuration(300); 158 | animatorSet.addListener(new AnimatorListenerAdapter() { 159 | @Override 160 | public void onAnimationEnd(Animator animation) { 161 | super.onAnimationEnd(animation); 162 | animation.start(); 163 | } 164 | }); 165 | 166 | animatorSet.start(); 167 | } 168 | 169 | @Override 170 | protected void initLoadingPaint() { 171 | //设置抗锯齿 172 | loadingPaint.setAntiAlias(true); 173 | } 174 | 175 | private ValueAnimator.AnimatorUpdateListener getAnimatorUpdateListener() { 176 | return new ValueAnimator.AnimatorUpdateListener() { 177 | @Override 178 | public void onAnimationUpdate(ValueAnimator animation) { 179 | currentAngle = (float) animation.getAnimatedValue(); 180 | invalidate(); 181 | } 182 | }; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/TestView.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | import android.widget.LinearLayout; 11 | 12 | import com.zhaoli.loadings.R; 13 | 14 | /** 15 | * Created by zhaoli on 2016/6/17. 16 | */ 17 | public class TestView extends View { 18 | 19 | private Path path = new Path(); 20 | private Paint paint = new Paint(); 21 | 22 | public TestView(Context context) { 23 | super(context); 24 | } 25 | 26 | public TestView(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | } 29 | 30 | public TestView(Context context, AttributeSet attrs, int defStyleAttr) { 31 | super(context, attrs, defStyleAttr); 32 | } 33 | 34 | @Override 35 | protected void onDraw(Canvas canvas) { 36 | super.onDraw(canvas); 37 | 38 | canvas.translate(0, getHeight() / 2); 39 | 40 | paint.setStyle(Paint.Style.STROKE); 41 | paint.setStrokeWidth(6); 42 | 43 | paint.setColor(Color.parseColor("#FF0000")); 44 | canvas.drawLine(0, 0, getWidth(), 0, paint); 45 | 46 | canvas.drawLine(200, -20, 200, 0, paint); 47 | canvas.drawLine(400, -20, 400, 0, paint); 48 | canvas.drawLine(600, -20, 600, 0, paint); 49 | 50 | path.reset(); 51 | path.moveTo(0, 0); 52 | path.lineTo(200, 200); //直线 下一个点坐标(x, y) 53 | 54 | paint.setColor(Color.parseColor("#000000")); 55 | canvas.drawPath(path, paint); 56 | 57 | rQuadTo(200, 0, 400, -200, "#0000FF", canvas); 58 | rQuadTo(0, 200, 400, 0, "#00FF00", canvas); 59 | rQuadTo(100, -200, 200, 0, "#00FFFF", canvas); 60 | } 61 | 62 | private void startAnim() { 63 | 64 | } 65 | 66 | /** 67 | * 贝塞尔曲线 68 | * @param dx1 贝塞尔曲线控制点坐标(相差) 69 | * @param dy1 贝塞尔曲线控制点坐标(相差) 70 | * @param dx2 贝塞尔曲线终点坐标(相差) 71 | * @param dy2 贝塞尔曲线终点坐标(相差) 72 | * @param color 颜色 73 | * @param canvas 画布 74 | */ 75 | private void rQuadTo(int dx1, int dy1, int dx2, int dy2, String color, Canvas canvas) { 76 | path.reset(); 77 | path.moveTo(0, 0); //起点坐标 78 | path.rQuadTo(dx1, dy1, dx2, dy2); 79 | paint.setColor(Color.parseColor(color)); 80 | canvas.drawPath(path, paint); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/java/com/zhaoli/loadings/loadingViews/TranslationBallLoading.java: -------------------------------------------------------------------------------- 1 | package com.zhaoli.loadings.loadingViews; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ValueAnimator; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.util.AttributeSet; 12 | import android.view.animation.AccelerateInterpolator; 13 | import android.view.animation.DecelerateInterpolator; 14 | import android.view.animation.Interpolator; 15 | import android.view.animation.LinearInterpolator; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * Created by zhaoli on 2016/6/20. 22 | * 23 | * 平移球体加载 24 | */ 25 | public class TranslationBallLoading extends BaseLoading { 26 | 27 | private final static int[] BALL_COLOR = new int[] { 28 | Color.parseColor("#ED7070"), 29 | Color.parseColor("#E9995A"), 30 | Color.parseColor("#6DE9A5"), 31 | Color.parseColor("#4EB3E8"), 32 | Color.parseColor("#C25CEC") 33 | }; 34 | 35 | private final static int TEXT_COLOR = Color.parseColor("#D5D5D5"); 36 | 37 | private final static int SPACE_PROPORTION = 12; //Width / 13 //起始的间距 38 | 39 | private final static int BALL_MAX = 5; 40 | private final static int BALL_RADIUS = 10; 41 | 42 | private final static int SPACE_MIDDLE = 2 * BALL_RADIUS + 8; //相聚时的间距 43 | 44 | private List animatorList; 45 | 46 | private int[] xList = new int[BALL_MAX]; 47 | 48 | boolean startAnimation = false; 49 | 50 | public TranslationBallLoading(Context context) { 51 | super(context); 52 | initAnim(); 53 | } 54 | 55 | public TranslationBallLoading(Context context, AttributeSet attrs) { 56 | super(context, attrs); 57 | initAnim(); 58 | } 59 | 60 | public TranslationBallLoading(Context context, AttributeSet attrs, int defStyleAttr) { 61 | super(context, attrs, defStyleAttr); 62 | initAnim(); 63 | } 64 | 65 | @Override 66 | protected void onDraw(Canvas canvas) { 67 | super.onDraw(canvas); 68 | 69 | if (! startAnimation) { 70 | startAnimation(); 71 | startAnimation = true; 72 | } 73 | 74 | int y = getHeight() / 2; 75 | 76 | for (int i = 0; i < BALL_MAX; i ++) { 77 | drawBall(i, y, canvas); 78 | } 79 | 80 | //绘制文字 81 | loadingPaint.setColor(TEXT_COLOR); 82 | loadingPaint.setTextSize(48); 83 | loadingPaint.setTextAlign(Paint.Align.CENTER); 84 | canvas.drawText("Loading...", getWidth() / 2, (float) (y * 1.5), loadingPaint); 85 | } 86 | 87 | private void drawBall(int index, int y, Canvas canvas) { 88 | loadingPaint.setColor(BALL_COLOR[index]); 89 | canvas.drawCircle(xList[index], y, BALL_RADIUS, loadingPaint); 90 | } 91 | 92 | @Override 93 | protected void initLoading() { 94 | if (animatorList == null) { 95 | animatorList = new ArrayList<>(); 96 | } 97 | } 98 | 99 | private void startAnimation() { 100 | int width = getWidth(); 101 | 102 | int space = width / SPACE_PROPORTION; 103 | 104 | AnimatorSet animatorSet = new AnimatorSet(); 105 | for (int i = 0; i < BALL_MAX; i ++) { 106 | AnimatorSet animatorSet1 = new AnimatorSet(); 107 | 108 | int middleSpace = width / 2 - i * SPACE_MIDDLE + 109 | (BALL_MAX * BALL_RADIUS * 2 + (BALL_MAX - 1) * (SPACE_MIDDLE - 2 * BALL_RADIUS)) / 2; 110 | 111 | ValueAnimator valueAnimator1 = ValueAnimator.ofInt(- i * space, middleSpace); 112 | valueAnimator1.addUpdateListener(getAnimatorUpdateListener(i)); 113 | valueAnimator1.setInterpolator(getInterpolator(i, false)); 114 | valueAnimator1.setDuration(3000); 115 | 116 | ValueAnimator valueAnimator2 = ValueAnimator.ofInt(middleSpace, width - i * space); 117 | valueAnimator2.addUpdateListener(getAnimatorUpdateListener(i)); 118 | valueAnimator2.setInterpolator(getInterpolator(i, true)); 119 | valueAnimator2.setDuration(1500); 120 | 121 | animatorSet1.addListener(new AnimatorListenerAdapter() { 122 | @Override 123 | public void onAnimationEnd(Animator animation) { 124 | super.onAnimationEnd(animation); 125 | animation.start(); 126 | } 127 | }); 128 | animatorSet1.playSequentially(valueAnimator1, valueAnimator2); 129 | animatorSet1.setInterpolator(new LinearInterpolator()); 130 | animatorSet1.start(); 131 | animatorList.add(animatorSet1); 132 | } 133 | animatorSet.playTogether(animatorList); 134 | animatorSet.start(); 135 | } 136 | 137 | private ValueAnimator.AnimatorUpdateListener getAnimatorUpdateListener(final int index) { 138 | return new ValueAnimator.AnimatorUpdateListener() { 139 | @Override 140 | public void onAnimationUpdate(ValueAnimator animation) { 141 | xList[index] = (int) animation.getAnimatedValue(); 142 | invalidate(); 143 | } 144 | }; 145 | } 146 | 147 | private Interpolator getInterpolator(int index, boolean isAcc) { 148 | if (isAcc) { 149 | return new AccelerateInterpolator(index + 1); 150 | } 151 | if (index < (BALL_MAX / 2 + 1)) { 152 | return new DecelerateInterpolator((BALL_MAX / 2 + 1) - index); 153 | } else if (index > (BALL_MAX / 2 + 1)) { 154 | return new AccelerateInterpolator(index - (BALL_MAX / 2 + 1)); 155 | } else { 156 | return new LinearInterpolator(); 157 | } 158 | } 159 | 160 | @Override 161 | protected void initLoadingPaint() { 162 | loadingPaint.setAntiAlias(true); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/drawable/loading_item_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/layout/loading_item_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/layout/test_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/LoadingApp/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/LoadingApp/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/LoadingApp/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/LoadingApp/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/LoadingApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Loadings/LoadingApp/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Loadings 3 | 4 | -------------------------------------------------------------------------------- /Loadings/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.0.0' 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 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /Loadings/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 -------------------------------------------------------------------------------- /Loadings/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':LoadingApp' 2 | -------------------------------------------------------------------------------- /Loadings/示例/Loading01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading01.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading02.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading03.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading04.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading06.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading07.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading08.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading09.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading09.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading10.gif -------------------------------------------------------------------------------- /Loadings/示例/Loading11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveShadow/PerfectLoading/2f7c87e222e7a267ccd38bdda9dd97991f9fd878/Loadings/示例/Loading11.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #自定义加载动画 2 | [灵感来源,动画设计来源](http://mp.weixin.qq.com/s?__biz=MjM5NTQ5MjIyMA==&mid=400603665&idx=3&sn=4e97192d34de86199ba33cb5d524cc77&scene=2&srcid=1102vbz5Y1K0vzTGKLeDTxdb&from=timeline&isappinstalled=0&uin=MTY5MDI4NDA4Mg%3D%3D&key=04dce534b3b035ef2b9162c22037a6a1f626b043ef93fd5f8630571da1bfb73b806c0c1845be61b02ecf4d7af6a8d652&devicetype=iMac+MacBookPro11%2C3+OSX+OSX+10.11.1+build(15B42)&version=11020201&lang=zh_CN&pass_ticket=Ccw4gTzWdRRgQlUTS3FRKMvcvEW0%2FQ1EVlRdgrv%2BfJRCXJxO2Irjh5hIHMni2E7p) 3 | 4 | ##加载动画介绍 5 | ###1. ConvertBallLoading 6 | 效果图 7 | 8 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading07.gif) 9 | 10 | ###2. JumpWhirlGraphLoading 11 | 效果图 12 | 13 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading06.gif) 14 | 15 | ###3. TranslationBallLoading 16 | 效果图 17 | 18 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading04.gif) 19 | 20 | ###4. SegmentSquareLoading 21 | 效果图 22 | 23 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading03.gif) 24 | 25 | ###5. SwingCollisionLoading 26 | 效果图 27 | 28 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading02.gif) 29 | 30 | ###6. RotateSquareLoading 31 | 效果图 32 | 33 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading01.gif) 34 | 35 | ###7. LineWhirlLoading1 36 | 效果图 37 | 38 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading08.gif) 39 | 40 | ###8. LineWhirlLoading2 41 | 效果图 42 | 43 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading09.gif) 44 | 45 | ###9. DownloadLoading 46 | 效果图 47 | 48 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading10.gif) 49 | 50 | ###10. AnnulusWhirlLoading 51 | 效果图 52 | 53 | ![](https://github.com/loveShadow/PerfectLoading/blob/master/Loadings/%E7%A4%BA%E4%BE%8B/Loading11.gif) 54 | 55 | 56 | ##动画暂时维护到这,之后有时间在增加好看的动画 --------------------------------------------------------------------------------