├── .travis.yml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── osan │ │ └── nebula │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── osan │ │ │ └── nebula │ │ │ ├── App.java │ │ │ ├── AppFlowActivity.java │ │ │ ├── FlashActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── anim │ │ │ └── StarryView.java │ │ │ └── util │ │ │ └── Utils.java │ └── res │ │ ├── anim │ │ ├── abc_fade_in.xml │ │ ├── abc_fade_out.xml │ │ ├── abc_grow_fade_in_from_bottom.xml │ │ ├── abc_popup_enter.xml │ │ ├── abc_popup_exit.xml │ │ ├── abc_shrink_fade_out_from_bottom.xml │ │ ├── abc_slide_in_bottom.xml │ │ ├── abc_slide_in_top.xml │ │ ├── abc_slide_out_bottom.xml │ │ ├── abc_slide_out_top.xml │ │ ├── activity_in.xml │ │ ├── design_bottom_sheet_slide_in.xml │ │ ├── design_bottom_sheet_slide_out.xml │ │ ├── design_fab_in.xml │ │ ├── design_fab_out.xml │ │ ├── design_snackbar_in.xml │ │ ├── design_snackbar_out.xml │ │ ├── i_panel_acc.xml │ │ ├── load_rotate_anim.xml │ │ ├── panel_enter.xml │ │ ├── panel_exit.xml │ │ ├── sub_activity_close_enter.xml │ │ ├── sub_activity_close_exit.xml │ │ ├── sub_activity_open_enter.xml │ │ └── sub_activity_open_exit.xml │ │ ├── layout │ │ ├── activity_app_flow.xml │ │ ├── activity_flash.xml │ │ └── activity_main.xml │ │ ├── mipmap-mdpi │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── bg.png │ │ ├── ic_launcher_round.png │ │ ├── light.png │ │ ├── particallevel1.png │ │ ├── particallevel2.png │ │ ├── particallevel3.png │ │ ├── particallevel4.png │ │ ├── sky_layer1.jpg │ │ ├── sky_layer2.png │ │ └── vortex.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── anims.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── integers.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── osan │ └── nebula │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── nebula.gif └── settings.gradle /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | sudo: false 3 | android: 4 | components: 5 | - build-tools-25.0.2 6 | - android-25 7 | - extra-android-m2repository 8 | - extra-android-support 9 | before_install: 10 | - chmod +x gradlew 11 | script: 12 | - "./gradlew assembleRelease" 13 | deploy: 14 | provider: releases 15 | api_key: 16 | secure: eY1MPVqsgMQs0deLMeHH0hJrkdxgP8VauCc0AW3MEDy+89EkPQiWK+mQgnNVExGL54ajMurYAs3Y3evRZNNxeIUnhXiFLTLTg9sZlbT1lhK+/0wjQ0pQ6B2RbQLeNUq5dXrJbgLEWTpDgvABZomnYvDiUll7iJESX1Eo2NOZ/YKsjkPXILnm63P37UOrxBzOi45ZTZ5hd4L+E1oDq9k0fI0YEh7HKJYR5RfpFK3fvcNqyoAPG2cBlwGS33dfg4LeZN2Yxb+7cNMraEipj7ZjiiaYFXeGOgBz8xWWqcG+k8MjqpxOKJtlSqKjxFYpa0J97yFxxmmMtIEJX+lExc6XMbm9xeYKMqeJJLwSpmNFk6ggkk/vUsTzMy8+LdczyuQhBvCGMRt/IZRSxGIOznZD3KbmK5/Ceex3aXgin2pEhNA4LmwSLf+zMhniJNIZj3b2cF8u18LR1lGTNxTD03w170lDtX1fizmUQYFSiPJHeBiGp3W5HD9uTLbJfnh9tfJnOIhlKMN5C1Nl2HUyeV4KXCnkl4q73DtMuuNBn66JnFUhuzh0diFVpdAUK0KhwYTGew6DNz3GsnxFuPr/6MnrpUKKxSucDjprA2G0bfd7XbrVSw9BYR3wk5mU3ust5HXqh0erTIh6kRkin6NphPm+NLyQTvvbeXc/+M/zkXWAU9w= 17 | file: "app/build/outputs/apk/app-release-unsigned.apk" 18 | skip_cleanup: true 19 | on: 20 | tags: true 21 | repo: wangfuda/nebula 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nebula 2 | [![Build Status](https://travis-ci.org/wangfuda/nebula.svg?branch=master)](https://travis-ci.org/wangfuda/nebula) 3 | >一个关于星空的自定义动画Sample 4 | 5 | 6 | 7 | Sample中自定义动画实现及 8 | CPU运算/GPU渲染性能优化的讲解:[nebula自定义动画及GPU渲染性能优化手记](http://www.jianshu.com/p/4d16ebea8e35) 9 | 10 | 11 | 12 | 自定义动画nebula的Gif图: 13 | 14 | ![](https://github.com/wangfuda/nebula/blob/master/screenshots/nebula.gif) 15 | 16 | 17 | 18 | 自定义动画nebula的截图: 19 | 20 | ![](https://github.com/wangfuda/nebula/blob/master/screenshots/1.png) 21 | ![](https://github.com/wangfuda/nebula/blob/master/screenshots/2.png) 22 | ![](https://github.com/wangfuda/nebula/blob/master/screenshots/3.png) 23 | ![](https://github.com/wangfuda/nebula/blob/master/screenshots/4.png) 24 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.osan.nebula" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 101 11 | versionName "1.0.1" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/osan/nebula/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.osan.discovery", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/osan/nebula/App.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | /** 7 | * Created by osan on 2017/7/5. 8 | */ 9 | 10 | public class App extends Application { 11 | 12 | public static Context context; 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | context = this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/osan/nebula/AppFlowActivity.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | /** 7 | * 8 | * Created by osan on 9/21. 9 | */ 10 | 11 | // TODO: 2017/7/5 点击自定义view上的app图标,可以进入app flow界面 12 | public class AppFlowActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_app_flow); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/osan/nebula/FlashActivity.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | /** 8 | * 9 | * Created by osan on 9/21. 10 | */ 11 | 12 | public class FlashActivity extends Activity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_flash); 18 | 19 | Intent intent = new Intent(); 20 | intent.setClass(this, MainActivity.class); 21 | startActivity(intent); 22 | overridePendingTransition(0, 0); 23 | finish(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/osan/nebula/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import com.osan.nebula.anim.StarryView; 7 | 8 | /** 9 | * 10 | * Created by osan on 9/21. 11 | */ 12 | 13 | public class MainActivity extends Activity { 14 | 15 | private StarryView starryView; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | starryView = (StarryView) findViewById(R.id.starry_view); 22 | 23 | } 24 | 25 | @Override 26 | protected void onPause() { 27 | super.onPause(); 28 | starryView.pauseAnimation(); 29 | } 30 | 31 | @Override 32 | protected void onResume() { 33 | super.onResume(); 34 | starryView.resumeAnimation(); 35 | } 36 | 37 | @Override 38 | protected void onDestroy() { 39 | super.onDestroy(); 40 | starryView.stopAnimation(); 41 | starryView.releaseBitmap(); 42 | starryView.releaseBitmapEx(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/osan/nebula/anim/StarryView.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula.anim; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.graphics.BlurMaskFilter; 9 | import android.graphics.Canvas; 10 | import android.graphics.Color; 11 | import android.graphics.ColorMatrix; 12 | import android.graphics.ColorMatrixColorFilter; 13 | import android.graphics.Paint; 14 | import android.graphics.Rect; 15 | import android.graphics.RectF; 16 | import android.util.AttributeSet; 17 | import android.util.DisplayMetrics; 18 | import android.util.Log; 19 | import android.view.MotionEvent; 20 | import android.view.View; 21 | import android.view.WindowManager; 22 | import android.view.animation.AccelerateInterpolator; 23 | import android.view.animation.LinearInterpolator; 24 | 25 | import com.osan.nebula.R; 26 | import com.osan.nebula.util.Utils; 27 | 28 | /** 29 | * Created by osan on 9/21. 30 | */ 31 | 32 | public class StarryView extends View { 33 | 34 | private static final String TAG = "StarryView"; 35 | 36 | private int scwidth, halfWidth, thirdWidth, vortexHalfWidth; 37 | private int scheight, halfHeight, vortexHalfHeight; 38 | 39 | /** 40 | * 定制画笔 41 | */ 42 | private Paint paintBg = new Paint(); 43 | private Paint paintSky = new Paint(); 44 | private Paint paintVortex = new Paint(); 45 | private Paint paintCircleSolid = new Paint(); 46 | private Paint paintCircleHollow = new Paint(); 47 | private Paint paintCircleStar = new Paint(); 48 | private Paint paintText = new Paint(); 49 | private Paint paintTextTmp = new Paint(); 50 | /** 51 | * app小水波纹画笔 52 | */ 53 | private Paint paintWave1 = new Paint(); 54 | /** 55 | * 默认画笔 56 | */ 57 | private Paint paint = new Paint(); 58 | 59 | private Bitmap vortex, sky_layer1, sky_layer2, light, bg, particallevel1; 60 | private float mValue1, mValue2, mValue3, mValue4, mValue5, mValue6, mValue7, mValue8, mValue9; 61 | private float mValueMax = 1.0f; 62 | private int mIntValueMax = 100; 63 | /** 64 | * 单位:毫秒 65 | */ 66 | private long firstOverlay = 1000; 67 | private long timeFirstStep = 1000; 68 | private long timeSecondStep = 4500; 69 | private long timeThirdStep = 800; 70 | private long timeFourthStep = 2000; 71 | private long timeFifthStep = 10000; 72 | private long timeSixthStep = 1000; 73 | private long timeSeventhStep = 1000; 74 | private long timeEighthStep = 800; 75 | private long timeNinthStep = 3000; 76 | 77 | /** 78 | * ValueAnimator 79 | */ 80 | private ValueAnimator valueAnimator1; 81 | private ValueAnimator valueAnimator2; 82 | private ValueAnimator valueAnimator3; 83 | private ValueAnimator valueAnimator4; 84 | private ValueAnimator valueAnimator5; 85 | private ValueAnimator valueAnimator6; 86 | private ValueAnimator valueAnimator7; 87 | private ValueAnimator valueAnimator8; 88 | private ValueAnimator valueAnimator9; 89 | 90 | /** 91 | * 小星星半径 92 | */ 93 | private float radiusStar = Utils.dip2px(8); 94 | /** 95 | * 水波纹实心圆半径 96 | */ 97 | private float radiusMask = Utils.dip2px(33); 98 | /** 99 | * 水波纹小圆半径 100 | */ 101 | private float radiusSm = Utils.dip2px(66); 102 | /** 103 | * 水波纹大圆半径 104 | */ 105 | private float radiusLg = Utils.dip2px(133); 106 | /** 107 | * app小水波纹半径 108 | */ 109 | private float radiusAppWave = Utils.dip2px(13); 110 | /** 111 | * 波纹线宽 112 | */ 113 | private float circleStroke = Utils.dip2px(1.3f); 114 | /** 115 | * 文本距离小星星的垂直间距 116 | */ 117 | private float textVer = Utils.dip2px(20); 118 | /** 119 | * 文本字体大小 120 | */ 121 | private float textSize = Utils.dip2px(14); 122 | 123 | /** 124 | * app名称 125 | */ 126 | private String[] appNameInner = {"Facebook", "Twitter", "Instagram", "Snapshot", "Whatapp", "Youtube"}; 127 | private String[] appNameOuter = {"Gmail", "Aello", "Photo", "Message", "CleanMaster", "momoda"}; 128 | private String[] appNameInnerTmp, appNameOuterTmp; 129 | /** 130 | * app名称旋转角度 131 | */ 132 | private double a = 0; 133 | /** 134 | * 刷新应用圈 135 | */ 136 | private boolean isRefresh = false; 137 | /** 138 | * 切换内外圈的应用名称 139 | */ 140 | private boolean appSwitch = false; 141 | /** 142 | * 切换app小水波纹 143 | */ 144 | private int waveRandom = 0; 145 | /** 146 | * app小水波纹重复次数 147 | */ 148 | private int waveRepeatMax = 4; 149 | /** 150 | * app小水波纹重复次数计数器 151 | */ 152 | private int waveRepeatInit = 0; 153 | 154 | private int appNameIndex = 0; 155 | 156 | /** 157 | * 动画参数数据 158 | */ 159 | private Rect srcVortex, srcBg, src_sky_layer1, src_sky_layer2, dst_sky_layer2, srcLight, src_vortex, srcparticallevel1; 160 | private RectF dstFVortex, dstBg, dst_sky_layer1, dstLight, dst_vortex, dstparticallevel1; 161 | private RectF dst_bg; 162 | private ColorMatrix cMatrix; 163 | private float sqrtMask; 164 | private float halfMask; 165 | private float coord1, coord2, coord3, coord4; 166 | private float sqrt3; 167 | private double pi; 168 | 169 | 170 | public StarryView(Context context) { 171 | this(context, null); 172 | } 173 | 174 | public StarryView(Context context, AttributeSet attrs) { 175 | this(context, attrs, 0); 176 | } 177 | 178 | public StarryView(Context context, AttributeSet attrs, int defStyleAttr) { 179 | super(context, attrs, defStyleAttr); 180 | 181 | initValue(); 182 | 183 | initPaint(); 184 | 185 | initBitmap(); 186 | 187 | initAnimation(); 188 | 189 | initData(); 190 | } 191 | 192 | /** 193 | * 暂停全部动画 194 | */ 195 | public void pauseAnimation() { 196 | try { 197 | if (valueAnimator1 != null) valueAnimator1.pause(); 198 | if (valueAnimator2 != null) valueAnimator2.pause(); 199 | if (valueAnimator3 != null) valueAnimator3.pause(); 200 | if (valueAnimator4 != null) valueAnimator4.pause(); 201 | if (valueAnimator5 != null) valueAnimator5.pause(); 202 | if (valueAnimator6 != null) valueAnimator6.pause(); 203 | if (valueAnimator7 != null) valueAnimator7.pause(); 204 | if (valueAnimator8 != null) valueAnimator8.pause(); 205 | if (valueAnimator9 != null) valueAnimator9.pause(); 206 | } catch (Exception e) { 207 | e.printStackTrace(); 208 | } 209 | } 210 | 211 | /** 212 | * 恢复全部动画 213 | */ 214 | public void resumeAnimation() { 215 | if (valueAnimator1 != null) valueAnimator1.resume(); 216 | if (valueAnimator2 != null) valueAnimator2.resume(); 217 | if (valueAnimator3 != null) valueAnimator3.resume(); 218 | if (valueAnimator4 != null) valueAnimator4.resume(); 219 | if (valueAnimator5 != null) valueAnimator5.resume(); 220 | if (valueAnimator6 != null) valueAnimator6.resume(); 221 | if (valueAnimator7 != null) valueAnimator7.resume(); 222 | if (valueAnimator8 != null) valueAnimator8.resume(); 223 | if (valueAnimator9 != null) valueAnimator9.resume(); 224 | } 225 | 226 | /** 227 | * 停止全部动画 228 | */ 229 | public void stopAnimation() { 230 | 231 | if (valueAnimator1 != null) valueAnimator1.end(); 232 | if (valueAnimator2 != null) valueAnimator2.end(); 233 | if (valueAnimator3 != null) valueAnimator3.end(); 234 | if (valueAnimator4 != null) valueAnimator4.end(); 235 | if (valueAnimator5 != null) valueAnimator5.end(); 236 | if (valueAnimator6 != null) valueAnimator6.end(); 237 | if (valueAnimator7 != null) valueAnimator7.end(); 238 | if (valueAnimator8 != null) valueAnimator8.end(); 239 | if (valueAnimator9 != null) valueAnimator9.end(); 240 | 241 | } 242 | 243 | /** 244 | * 将获取屏幕宽高数值抽取出来,只需获取一次 245 | */ 246 | private void initValue() { 247 | WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 248 | 249 | DisplayMetrics metric = new DisplayMetrics(); 250 | wm.getDefaultDisplay().getMetrics(metric); 251 | scwidth = metric.widthPixels; // 屏幕宽度(像素) 252 | scheight = metric.heightPixels; // 屏幕高度(像素) 253 | halfWidth = scwidth / 2; 254 | halfHeight = scheight / 2; 255 | thirdWidth = scwidth / 3; 256 | 257 | sqrtMask = (float) Math.sqrt(3) / 2 * radiusMask; 258 | halfMask = radiusMask / 2; 259 | 260 | } 261 | 262 | /** 263 | * 初始化动画参数值 264 | * 可复用的动画参数直接初始化,这可以有效减小onDraw中计算量,降低CPU计算负荷,提高GPU渲染速度 265 | */ 266 | private void initData() { 267 | 268 | vortexHalfWidth = vortex.getWidth() / 2; 269 | vortexHalfHeight = vortex.getHeight() / 2; 270 | 271 | srcVortex = new Rect(0, 0, vortex.getWidth(), vortex.getHeight()); 272 | dstFVortex = new RectF(0, 0, 0, 0); 273 | 274 | srcBg = new Rect(0, 0, bg.getWidth(), bg.getHeight()); 275 | dstBg = new RectF(-halfWidth, -halfHeight, halfWidth, halfHeight); 276 | 277 | src_sky_layer1 = new Rect(0, 0, sky_layer1.getWidth(), sky_layer1.getHeight()); 278 | dst_sky_layer1 = new RectF(-halfWidth, -halfHeight, halfWidth, halfHeight); 279 | src_sky_layer2 = new Rect(0, 0, sky_layer2.getWidth(), sky_layer2.getHeight()); 280 | dst_sky_layer2 = new Rect(-thirdWidth, -thirdWidth, thirdWidth, thirdWidth); 281 | 282 | srcLight = new Rect(0, 0, light.getWidth(), light.getHeight()); 283 | float tempX = light.getWidth() / 2; 284 | float tempY = light.getHeight() / 2; 285 | dstLight = new RectF(-tempX, -tempY, tempX, tempY); 286 | 287 | src_vortex = new Rect(0, 0, vortex.getWidth(), vortex.getHeight()); 288 | dst_vortex = new RectF(0, 0, 0, 0); 289 | 290 | cMatrix = new ColorMatrix(); 291 | 292 | dst_bg = new RectF(0, 0, 0, 0); 293 | 294 | srcparticallevel1 = new Rect(0, 0, particallevel1.getWidth(), particallevel1.getHeight()); 295 | dstparticallevel1 = new RectF(0, 0, 0, 0); 296 | 297 | coord1 = (float) Math.sqrt(3) / 2 * radiusSm; 298 | coord2 = (float) Math.sqrt(3) / 2 * radiusLg; 299 | coord3 = radiusSm / 2; 300 | coord4 = radiusLg / 2; 301 | 302 | sqrt3 = (float) Math.sqrt(3) / 2; 303 | pi = Math.PI / 180; 304 | 305 | 306 | } 307 | 308 | /** 309 | * 初始化画笔 310 | * 可复用画笔放在初始化处理,避免在onDraw中处理,一切为了效率 311 | */ 312 | private void initPaint() { 313 | 314 | paintCircleSolid.setColor(this.getContext().getResources().getColor(R.color.mask_bg_color)); 315 | paintCircleSolid.setAntiAlias(true); 316 | //绘制水波纹(双层:实心半透黑色波纹+加速放大灰白色水波纹) 317 | paintCircleHollow.setColor(this.getContext().getResources().getColor(R.color.circle_color)); 318 | paintCircleHollow.setStrokeWidth(circleStroke); 319 | paintCircleHollow.setStyle(Paint.Style.STROKE); 320 | paintCircleHollow.setAntiAlias(true); 321 | 322 | paintCircleStar.setColor(this.getContext().getResources().getColor(R.color.star_color)); 323 | paintCircleStar.setAntiAlias(true); 324 | //需关闭硬件加速才能生效(矛盾的是,关闭硬件加速后,GPU渲染耗时飙升,二选一,先忽略光晕效果) 325 | paintCircleStar.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID)); 326 | 327 | paintText.setAntiAlias(true); 328 | paintText.setTextSize(textSize); 329 | paintText.setColor(Color.GRAY); 330 | paintText.setTextAlign(Paint.Align.CENTER); 331 | paintTextTmp.setAntiAlias(true); 332 | paintTextTmp.setTextSize(textSize); 333 | paintTextTmp.setColor(Color.GRAY); 334 | paintTextTmp.setTextAlign(Paint.Align.CENTER); 335 | 336 | //app小水波纹画笔 337 | paintWave1.setColor(this.getContext().getResources().getColor(R.color.wave_color)); 338 | paintWave1.setStrokeWidth(circleStroke); 339 | paintWave1.setStyle(Paint.Style.STROKE); 340 | paintWave1.setAntiAlias(true); 341 | 342 | } 343 | 344 | /** 345 | * 图片资源初始化 346 | */ 347 | private void initBitmap() { 348 | BitmapFactory.Options localOptions = new BitmapFactory.Options(); 349 | localOptions.inSampleSize = 1; 350 | 351 | light = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.light, localOptions); 352 | 353 | vortex = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.vortex, localOptions); 354 | sky_layer2 = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.sky_layer2, localOptions); 355 | 356 | particallevel1 = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.particallevel1, localOptions); 357 | 358 | localOptions.inSampleSize = 2; 359 | localOptions.inPreferredConfig = Bitmap.Config.RGB_565; 360 | sky_layer1 = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.sky_layer1, localOptions); 361 | bg = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.bg, localOptions); 362 | } 363 | 364 | /** 365 | * 图片资源释放 366 | */ 367 | public void releaseBitmap() { 368 | if (vortex != null) vortex.recycle(); 369 | vortex = null; 370 | if (sky_layer1 != null) sky_layer1.recycle(); 371 | sky_layer1 = null; 372 | if (sky_layer2 != null) sky_layer2.recycle(); 373 | sky_layer2 = null; 374 | if (light != null) light.recycle(); 375 | light = null; 376 | 377 | } 378 | 379 | /** 380 | * 图片资源释放 381 | */ 382 | public void releaseBitmapEx() { 383 | if (bg != null) bg.recycle(); 384 | bg = null; 385 | if (particallevel1 != null) particallevel1.recycle(); 386 | particallevel1 = null; 387 | 388 | } 389 | 390 | 391 | 392 | /** 393 | * 简化动画, 简化为一个ValueAnimator 394 | * 数值变化范围是 1 - 2 395 | * 规定 0 - 1 为阶段1 396 | * 规定 1 - 2 为阶段2 397 | * 根据数值绘制不同的内容 398 | */ 399 | private void initAnimation() { 400 | 401 | valueAnimator1 = ValueAnimator.ofFloat(0f, mValueMax); 402 | 403 | valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 404 | @Override 405 | public void onAnimationUpdate(ValueAnimator animation) { 406 | mValue1 = (float) animation.getAnimatedValue(); 407 | invalidate(); // 重绘 408 | } 409 | }); 410 | 411 | valueAnimator1.setDuration(timeFirstStep + firstOverlay); 412 | valueAnimator1.start(); 413 | 414 | //2 415 | valueAnimator2 = ValueAnimator.ofFloat(0f, mValueMax); 416 | 417 | valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 418 | @Override 419 | public void onAnimationUpdate(ValueAnimator animation) { 420 | mValue2 = (float) animation.getAnimatedValue(); 421 | Log.d(TAG, "mValue2:" + mValue2); 422 | invalidate(); 423 | } 424 | }); 425 | 426 | valueAnimator2.setDuration(timeSecondStep); 427 | valueAnimator2.setStartDelay(timeFirstStep); 428 | valueAnimator2.setInterpolator(new AccelerateInterpolator()); 429 | valueAnimator2.start(); 430 | 431 | //3 432 | valueAnimator3 = ValueAnimator.ofFloat(0f, mValueMax); 433 | 434 | valueAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 435 | @Override 436 | public void onAnimationUpdate(ValueAnimator animation) { 437 | mValue3 = (float) animation.getAnimatedValue(); 438 | invalidate(); // 重绘 439 | } 440 | }); 441 | 442 | valueAnimator3.setDuration(timeThirdStep); 443 | valueAnimator3.setStartDelay(timeFirstStep + timeSecondStep); 444 | valueAnimator3.setInterpolator(new LinearInterpolator()); 445 | valueAnimator3.start(); 446 | 447 | //4 448 | valueAnimator4 = ValueAnimator.ofFloat(0f, mValueMax); 449 | 450 | valueAnimator4.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 451 | @Override 452 | public void onAnimationUpdate(ValueAnimator animation) { 453 | mValue4 = (float) animation.getAnimatedValue(); 454 | invalidate(); // 重绘 455 | } 456 | }); 457 | 458 | valueAnimator4.setDuration(timeFourthStep); 459 | valueAnimator4.setStartDelay(timeFirstStep + timeSecondStep + timeThirdStep); 460 | valueAnimator4.start(); 461 | 462 | //5 463 | valueAnimator5 = ValueAnimator.ofFloat(0f, mValueMax); 464 | 465 | valueAnimator5.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 466 | @Override 467 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 468 | mValue5 = (float) valueAnimator.getAnimatedValue(); 469 | invalidate(); // 重绘 470 | } 471 | }); 472 | 473 | valueAnimator5.setDuration(timeFifthStep); 474 | valueAnimator5.setStartDelay(timeFirstStep + timeSecondStep + timeFourthStep); 475 | valueAnimator5.setRepeatCount(ValueAnimator.INFINITE); 476 | valueAnimator5.setRepeatMode(ValueAnimator.RESTART); 477 | valueAnimator5.start(); 478 | 479 | //6 480 | valueAnimator6 = ValueAnimator.ofFloat(0f, mValueMax); 481 | 482 | valueAnimator6.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 483 | @Override 484 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 485 | mValue6 = (float) valueAnimator.getAnimatedValue(); 486 | if (mValue6 == 1.f) { 487 | releaseBitmap(); 488 | releaseValueAnimator(); 489 | } 490 | invalidate(); // 重绘 491 | } 492 | }); 493 | 494 | valueAnimator6.setDuration(timeSixthStep); 495 | valueAnimator6.setStartDelay(timeFirstStep + timeSecondStep + timeFourthStep); 496 | valueAnimator6.start(); 497 | 498 | //7 499 | valueAnimator7 = ValueAnimator.ofFloat(0f, mValueMax); 500 | 501 | valueAnimator7.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 502 | @Override 503 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 504 | mValue7 = (float) valueAnimator.getAnimatedValue(); 505 | invalidate(); // 重绘 506 | } 507 | }); 508 | 509 | valueAnimator7.addListener(new ValueAnimator.AnimatorListener() { 510 | @Override 511 | public void onAnimationStart(Animator animator) { 512 | appNameIndex += 5; 513 | } 514 | 515 | @Override 516 | public void onAnimationEnd(Animator animator) { 517 | 518 | } 519 | 520 | @Override 521 | public void onAnimationCancel(Animator animator) { 522 | 523 | } 524 | 525 | @Override 526 | public void onAnimationRepeat(Animator animator) { 527 | 528 | } 529 | }); 530 | 531 | valueAnimator7.setDuration(timeSeventhStep); 532 | valueAnimator7.setStartDelay(timeFirstStep + timeSecondStep + timeFourthStep + timeSixthStep + timeNinthStep); 533 | valueAnimator7.setInterpolator(new AccelerateInterpolator()); 534 | valueAnimator7.start(); 535 | 536 | //8 537 | valueAnimator8 = ValueAnimator.ofFloat(0f, mValueMax); 538 | 539 | valueAnimator8.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 540 | @Override 541 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 542 | mValue8 = (float) valueAnimator.getAnimatedValue(); 543 | invalidate(); // 重绘 544 | } 545 | }); 546 | 547 | valueAnimator8.setDuration(timeEighthStep); 548 | valueAnimator8.setInterpolator(new AccelerateInterpolator()); 549 | 550 | //9 551 | valueAnimator9 = ValueAnimator.ofInt(0, mIntValueMax); 552 | 553 | valueAnimator9.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 554 | @Override 555 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 556 | mValue9 = (int) valueAnimator.getAnimatedValue(); 557 | invalidate(); // 重绘 558 | } 559 | }); 560 | valueAnimator9.addListener(new ValueAnimator.AnimatorListener() { 561 | @Override 562 | public void onAnimationStart(Animator animator) { 563 | if (waveRepeatInit++ % waveRepeatMax == 0) { 564 | waveRandom = (int) (Math.random() * 12); 565 | } 566 | } 567 | 568 | @Override 569 | public void onAnimationEnd(Animator animator) { 570 | if (waveRepeatInit % 8 == 0) { 571 | 572 | if (!valueAnimator7.isRunning()) { 573 | waveRepeatInit = 0; 574 | mValue9 = 0; 575 | mValue7 = 0.f; 576 | valueAnimator7.setStartDelay(0); 577 | valueAnimator7.start(); 578 | isRefresh = true; 579 | valueAnimator8.start(); 580 | appSwitch = !appSwitch; 581 | } 582 | } else { 583 | valueAnimator9.start(); 584 | } 585 | } 586 | 587 | @Override 588 | public void onAnimationCancel(Animator animator) { 589 | 590 | } 591 | 592 | @Override 593 | public void onAnimationRepeat(Animator animator) { 594 | 595 | } 596 | }); 597 | 598 | valueAnimator9.setDuration(timeNinthStep); 599 | } 600 | 601 | /** 602 | * 资源释放 603 | */ 604 | private void releaseValueAnimator() { 605 | valueAnimator1 = null; 606 | valueAnimator2 = null; 607 | valueAnimator3 = null; 608 | valueAnimator4 = null; 609 | valueAnimator5 = null; 610 | } 611 | 612 | /** 613 | * * 绘制时序及动画时序 614 | *

615 | * 阶段一 绘制:漩涡 动画:放大 1-4倍 时长:500ms 时间区间:0-500ms 616 | *

617 | * 阶段二 绘制:背景 动画:透明 0%-100% 时长:800ms 时间区间:700-1500ms 618 | * 阶段二 绘制:星空 动画:透明 0%-100% 时长:200ms 时间区间:500-700ms 619 | * 阶段二 绘制:星空 动画:放大 1-3倍 时长:1000ms 时间区间:500-1500ms 620 | * 阶段二 绘制:星云 动画:放大 1-3倍 旋转 0-100度 时长:1000ms 时间区间:500-1500ms 621 | * 阶段二 绘制:星光 动画:放大 0-4倍 时长:1000ms 时间区间:500-1500ms 622 | *

623 | * 阶段三 绘制:无 动画:透明 100%-0% 时长:100ms 时间区间:1500-1600ms 动画标的:星空 星云 星光 624 | * 阶段三 绘制:无 动画:放大 0-0.7倍 时长:300ms 时间区间:1500-1800ms 动画标的:漩涡 625 | * 阶段三 绘制:无 动画:透明 100%-0% 时长:100ms 时间区间:1800-1900ms 动画标的:漩涡 626 | *

627 | * 阶段四 绘制:无 动画:变暗 100%-45% 放大 1-1.3倍 时长:500ms 时间区间:1800-2300ms 动画标的:背景 628 | *

629 | * 阶段五 绘制:星团 动画:放大 1-2倍 时长:2000ms 时间区间:2300-4300ms 630 | * 阶段五 绘制:水波纹 动画:放大 时长: 时间区间: 631 | *

632 | * 阶段六 初始化绘制内外圈星星及文字 633 | *

634 | * 阶段七 绘制内外圈星星及文字,并负责绘制动态旋转放大 635 | *

636 | * 阶段八 绘制星团(一次性动画) 637 | *

638 | * 阶段九 绘制某个app的动态小水波纹 639 | * 640 | * @param canvas 641 | */ 642 | @Override 643 | protected void onDraw(Canvas canvas) { 644 | super.onDraw(canvas); 645 | 646 | if (mValue1 < mValueMax / 2) { 647 | 648 | drawState1(canvas); 649 | 650 | } else if (mValue1 < mValueMax) { 651 | 652 | drawState2(canvas); 653 | 654 | } else if (mValue2 < mValueMax) { 655 | 656 | drawState2(canvas); 657 | 658 | } else if (mValue3 < mValueMax) { 659 | 660 | drawState3(canvas); 661 | 662 | } else if (mValue4 < mValueMax) { 663 | 664 | drawState4(canvas); 665 | 666 | } else { 667 | 668 | drawState5(canvas); 669 | 670 | if (mValue6 < mValueMax) { 671 | 672 | drawState6(canvas); 673 | 674 | } else if (mValue7 <= mValueMax) { 675 | 676 | if (!valueAnimator9.isStarted()) { 677 | 678 | valueAnimator9.start(); 679 | } 680 | 681 | drawState9(canvas); 682 | 683 | if (isRefresh) { 684 | // drawState8(canvas); 685 | if (mValue7 == mValueMax) { 686 | 687 | isRefresh = false; 688 | } 689 | } 690 | 691 | drawState7(canvas); 692 | } 693 | } 694 | } 695 | 696 | /** 697 | * 阶段一 698 | * 绘制漩涡,主要控制放大 699 | * 700 | * @param canvas 701 | */ 702 | private void drawState1(Canvas canvas) { 703 | canvas.save(); 704 | canvas.translate(halfWidth, halfHeight); 705 | 706 | float scale = (mValue1 * 9) + 1; // 缩放比例 1 - 4 707 | float tempX = vortexHalfWidth * scale; 708 | float tempY = vortexHalfHeight * scale; 709 | dstFVortex.set(-tempX, -tempY, tempX, tempY); 710 | 711 | canvas.drawBitmap(vortex, srcVortex, dstFVortex, paint); 712 | 713 | canvas.restore(); 714 | } 715 | 716 | /** 717 | * 阶段二 绘制星空和亮光 718 | * 绘制星空背景 - 放大 719 | * 绘制星空漩涡 - 放大+旋转 720 | * 绘制亮光 - 放大 721 | * 722 | * @param canvas 723 | */ 724 | private void drawState2(Canvas canvas) { 725 | 726 | // 绘制 bg 727 | if (mValue2 < 200.f / timeSecondStep) { 728 | paintBg.setAlpha((int) (255 * 0.0f)); // 设置了画笔透明度 729 | } else { 730 | paintBg.setAlpha((int) (255 * mValue2)); // 设置了画笔透明度 731 | } 732 | 733 | canvas.save(); 734 | canvas.translate(halfWidth, halfHeight); 735 | canvas.drawBitmap(bg, srcBg, dstBg, paintBg); 736 | 737 | canvas.restore(); 738 | 739 | 740 | // 绘制 sky_layer 741 | paint.setAlpha((int) (255 * 0.6f)); // 设置了画笔透明度 742 | 743 | if (mValue2 < 0.6f) { 744 | paintSky.setAlpha((int) (255 * 0.6f)); // 设置了画笔透明度 745 | } else { 746 | paintSky.setAlpha((int) (255 * mValue2)); // 设置了画笔透明度 747 | } 748 | 749 | float tempValue = mValue2; 750 | float scale = (tempValue * 4) + 1; // 缩放比例 1 - 3 751 | 752 | canvas.save(); 753 | canvas.translate(halfWidth, halfHeight); 754 | canvas.scale(scale, scale); // 放大 755 | 756 | canvas.drawBitmap(sky_layer1, src_sky_layer1, dst_sky_layer1, paintSky); 757 | 758 | canvas.rotate(tempValue * 720f); // 旋转 759 | 760 | canvas.drawBitmap(sky_layer2, src_sky_layer2, dst_sky_layer2, paintSky); 761 | 762 | canvas.restore(); 763 | 764 | // 绘制light 765 | canvas.save(); 766 | float tempScale = (tempValue * 8); // 缩放比例 0 - 4 767 | canvas.translate(halfWidth, halfHeight); 768 | canvas.scale(tempScale, tempScale); // 缩放 769 | 770 | 771 | canvas.drawBitmap(light, srcLight, dstLight, paintSky); 772 | 773 | canvas.restore(); 774 | 775 | 776 | } 777 | 778 | 779 | /** 780 | * 阶段三 781 | * 过渡渐出 782 | * 783 | * @param canvas 784 | */ 785 | private void drawState3(Canvas canvas) { 786 | 787 | // 绘制 bg 788 | canvas.save(); 789 | canvas.translate(halfWidth, halfHeight); 790 | canvas.drawBitmap(bg, srcBg, dstBg, paintBg); 791 | canvas.restore(); 792 | 793 | // 绘制 sky_layer 794 | if (mValue3 < 0.55f) { 795 | paintSky.setAlpha((int) (255 * (1 - mValue3 * 1.3))); // 设置了画笔透明度 796 | } else { 797 | paintSky.setAlpha((int) (255 * (0.0f))); // 设置了画笔透明度 798 | } 799 | 800 | float tempValue = mValue2 - mValue3; 801 | float scale = (tempValue * 4) + 1; // 缩放比例 1 - 3 802 | 803 | canvas.save(); 804 | canvas.translate(halfWidth, halfHeight); 805 | canvas.scale(scale, scale); // 放大 806 | canvas.drawBitmap(sky_layer1, src_sky_layer1, dst_sky_layer1, paintSky); 807 | canvas.rotate((mValue2 + mValue3) * 720f); // 旋转 808 | canvas.drawBitmap(sky_layer2, src_sky_layer2, dst_sky_layer2, paintSky); 809 | canvas.restore(); 810 | 811 | // 绘制light 812 | canvas.save(); 813 | float tempScale = (tempValue * 8); // 缩放比例 0 - 4 814 | canvas.translate(halfWidth, halfHeight); 815 | canvas.scale(tempScale, tempScale); // 缩放 816 | canvas.drawBitmap(light, srcLight, dstLight, paintSky); 817 | canvas.restore(); 818 | 819 | 820 | //绘制最后的漩涡并消失 821 | if (mValue3 < 0.75f) { 822 | paintVortex.setAlpha((int) (255 * (1))); // 设置了画笔透明度 823 | } else { 824 | paintVortex.setAlpha((int) (255 * (1 - mValue3))); // 设置了画笔透明度 825 | } 826 | 827 | canvas.save(); 828 | canvas.translate(halfWidth, halfHeight); 829 | float scaleVortex = (mValue3 * 0.7f); // 缩放比例 0 - 0.7 830 | float tempXVortex = vortexHalfWidth * scaleVortex; 831 | float tempYVortex = vortexHalfHeight * scaleVortex; 832 | dst_vortex.set(-tempXVortex, -tempYVortex, tempXVortex, tempYVortex); 833 | canvas.drawBitmap(vortex, src_vortex, dst_vortex, paint); 834 | canvas.restore(); 835 | 836 | 837 | } 838 | 839 | /** 840 | * 阶段四 841 | * 绘制背景变暗 慢速微放大 842 | * 843 | * @param canvas 844 | */ 845 | private void drawState4(Canvas canvas) { 846 | 847 | // 绘制 bg 848 | float contrast = (1.f - mValue4 > 0.4f) ? (1.f - mValue4) : 0.4f;// 对比度 849 | float brightness = 0;// 亮度 850 | 851 | cMatrix.set(new float[]{contrast, 0, 0, 0, brightness, 852 | 0, contrast, 0, 0, brightness, 853 | 0, 0, contrast, 0, brightness, 854 | 0, 0, 0, 1, 0}); 855 | 856 | paintBg.setColorFilter(new ColorMatrixColorFilter(cMatrix)); 857 | 858 | canvas.save(); 859 | canvas.translate(halfWidth, halfHeight); 860 | float scale = (mValue4 * 0.3f) + 1; // 缩放比例 1 - 1.3 861 | float tempX = halfWidth * scale; 862 | float tempY = halfHeight * scale; 863 | dst_bg.set(-tempX, -tempY, tempX, tempY); 864 | canvas.drawBitmap(bg, srcBg, dst_bg, paintBg); 865 | canvas.restore(); 866 | } 867 | 868 | /** 869 | * 阶段五 870 | * 绘制星团 871 | * 872 | * @param canvas 873 | */ 874 | private void drawState5(Canvas canvas) { 875 | 876 | drawState4(canvas);//背景状态保持 877 | 878 | //绘制星团 879 | canvas.save(); 880 | canvas.translate(halfWidth, halfHeight); 881 | float scale = (mValue5 * 5f) + 1; // 缩放比例 1 - 6 882 | float tempX = halfWidth * scale; 883 | float tempY = halfHeight * scale; 884 | dstparticallevel1.set(-tempX, -tempY, tempX, tempY); 885 | canvas.drawBitmap(particallevel1, srcparticallevel1, dstparticallevel1, paintBg);//共用背景画笔,保持对比度一致 886 | canvas.restore(); 887 | } 888 | 889 | /** 890 | * 阶段六 初始化绘制内外圈星星及文字 891 | * 892 | * @param canvas 893 | */ 894 | private void drawState6(Canvas canvas) { 895 | //绘制水波纹(双层:实心半透黑色波纹+加速放大灰白色水波纹) 896 | float radius2 = mValue6 * radiusSm; 897 | float radius3 = mValue6 * radiusLg; 898 | canvas.save(); 899 | canvas.translate(halfWidth, halfHeight); 900 | canvas.drawCircle(0, 0, radius2, paintCircleHollow); 901 | canvas.drawCircle(0, 0, radius3, paintCircleHollow); 902 | canvas.restore(); 903 | 904 | float radius = mValue6 * radiusMask; 905 | canvas.save(); 906 | canvas.translate(halfWidth, halfHeight); 907 | canvas.drawCircle(0, 0, radius, paintCircleSolid); 908 | canvas.restore(); 909 | 910 | if (mValue6 == mValueMax) { 911 | 912 | canvas.save(); 913 | canvas.translate(halfWidth, halfHeight); 914 | //绘制内圈星星 915 | canvas.drawCircle(-radius2, 0, radiusStar, paintCircleStar); 916 | canvas.drawCircle(radius2, 0, radiusStar, paintCircleStar); 917 | 918 | float x = radius2 / 2; 919 | float y = (float) Math.sqrt(3) / 2 * radius2; 920 | canvas.drawCircle(-x, -y, radiusStar, paintCircleStar); 921 | canvas.drawCircle(-x, y, radiusStar, paintCircleStar); 922 | canvas.drawCircle(x, -y, radiusStar, paintCircleStar); 923 | canvas.drawCircle(x, y, radiusStar, paintCircleStar); 924 | //绘制内圈星星名字 925 | canvas.drawText(appNameInner[0], -radius2, textVer, paintText); 926 | canvas.drawText(appNameInner[1], radius2, textVer, paintText); 927 | canvas.drawText(appNameInner[2], -x, -y + textVer, paintText); 928 | canvas.drawText(appNameInner[3], -x, y + textVer, paintText); 929 | canvas.drawText(appNameInner[4], x, -y + textVer, paintText); 930 | canvas.drawText(appNameInner[5], x, y + textVer, paintText); 931 | 932 | //绘制外圈星星 933 | float xx = (float) Math.sqrt(3) / 2 * radius3; 934 | float yy = radius3 / 2; 935 | canvas.drawCircle(0, -radius3, radiusStar, paintCircleStar); 936 | canvas.drawCircle(0, radius3, radiusStar, paintCircleStar); 937 | canvas.drawCircle(-xx, -yy, radiusStar, paintCircleStar); 938 | canvas.drawCircle(-xx, yy, radiusStar, paintCircleStar); 939 | canvas.drawCircle(xx, -yy, radiusStar, paintCircleStar); 940 | canvas.drawCircle(xx, yy, radiusStar, paintCircleStar); 941 | 942 | //绘制外圈星星名字 943 | canvas.drawText(appNameOuter[0], 0, -radius3 + textVer, paintText); 944 | canvas.drawText(appNameOuter[1], 0, radius3 + textVer, paintText); 945 | canvas.drawText(appNameOuter[2], -xx, -yy + textVer, paintText); 946 | canvas.drawText(appNameOuter[2], -xx, yy + textVer, paintText); 947 | canvas.drawText(appNameOuter[2], xx, -yy + textVer, paintText); 948 | canvas.drawText(appNameOuter[2], xx, yy + textVer, paintText); 949 | 950 | 951 | canvas.restore(); 952 | } 953 | } 954 | 955 | /** 956 | * 阶段七 绘制内外圈星星及文字,并负责绘制动态旋转放大 957 | * 958 | * @param canvas 959 | */ 960 | 961 | private void drawState7(Canvas canvas) { 962 | 963 | float radius2 = mValue6 * radiusSm; 964 | float radius3 = mValue6 * radiusLg; 965 | 966 | //绘制水波纹(双层:实心半透黑色波纹+加速放大灰白色水波纹) 967 | //绘制动态放大的第二三圈 968 | canvas.save(); 969 | canvas.translate(halfWidth, halfHeight); 970 | float scale = (mValue7 * 1.f) + 1; // 缩放比例 1 - 2 971 | canvas.scale(scale, scale); 972 | canvas.rotate(mValue7 * 30f); 973 | canvas.drawCircle(0, 0, radiusMask, paintCircleHollow); 974 | canvas.drawCircle(0, 0, radius2, paintCircleHollow); 975 | canvas.restore(); 976 | 977 | //绘制无限放大并在主屏上不在显示的第三圈 978 | canvas.save(); 979 | canvas.translate(halfWidth, halfHeight); 980 | float scale3 = (mValue7 * 2.f) + 1; // 缩放比例 1 - 2 981 | canvas.scale(scale3, scale3); 982 | canvas.rotate(mValue7 * 30f); 983 | canvas.drawCircle(0, 0, radius3, paintCircleHollow); 984 | canvas.restore(); 985 | 986 | float radius = mValue6 * radiusMask; 987 | //绘制阴影圈 988 | canvas.save(); 989 | canvas.translate(halfWidth, halfHeight); 990 | canvas.drawCircle(0, 0, radius, paintCircleSolid); 991 | canvas.restore(); 992 | 993 | paintTextTmp.setTextSize(textSize * mValue7); 994 | 995 | canvas.save(); 996 | // setLayerType(LAYER_TYPE_SOFTWARE, null);//关闭硬件加速 997 | canvas.translate(halfWidth, halfHeight); 998 | canvas.scale(scale, scale); 999 | canvas.rotate(30f * mValue7); 1000 | 1001 | { 1002 | //绘制内圈星星 1003 | 1004 | float x = radius2 / 2; 1005 | float y = (float) Math.sqrt(3) / 2 * radius2; 1006 | float tmpRadius = radiusStar / scale; 1007 | canvas.drawCircle(-radius2, 0, tmpRadius, paintCircleStar); 1008 | canvas.drawCircle(radius2, 0, tmpRadius, paintCircleStar); 1009 | canvas.drawCircle(-x, -y, tmpRadius, paintCircleStar); 1010 | canvas.drawCircle(-x, y, tmpRadius, paintCircleStar); 1011 | canvas.drawCircle(x, -y, tmpRadius, paintCircleStar); 1012 | canvas.drawCircle(x, y, tmpRadius, paintCircleStar); 1013 | } 1014 | canvas.restore(); 1015 | 1016 | canvas.save(); 1017 | canvas.translate(halfWidth, halfHeight); 1018 | canvas.scale(scale3, scale3); 1019 | canvas.rotate(30f * mValue7); 1020 | { 1021 | //绘制外圈星星 1022 | float x = (float) Math.sqrt(3) / 2 * radius3; 1023 | float y = radius3 / 2; 1024 | float tmpRadius3 = radiusStar / scale; 1025 | canvas.drawCircle(0, -radius3, tmpRadius3, paintCircleStar); 1026 | canvas.drawCircle(0, radius3, tmpRadius3, paintCircleStar); 1027 | canvas.drawCircle(-x, -y, tmpRadius3, paintCircleStar); 1028 | canvas.drawCircle(-x, y, tmpRadius3, paintCircleStar); 1029 | canvas.drawCircle(x, -y, tmpRadius3, paintCircleStar); 1030 | canvas.drawCircle(x, y, tmpRadius3, paintCircleStar); 1031 | } 1032 | canvas.restore(); 1033 | 1034 | if (!appSwitch) { 1035 | appNameInnerTmp = appNameInner; 1036 | appNameOuterTmp = appNameOuter; 1037 | } else { 1038 | appNameInnerTmp = appNameOuter; 1039 | appNameOuterTmp = appNameInner; 1040 | } 1041 | 1042 | a = 30f * mValue7; 1043 | 1044 | /** 1045 | * 绘制新生内圈星星及名字 1046 | */ 1047 | { 1048 | canvas.save(); 1049 | // setLayerType(LAYER_TYPE_SOFTWARE, null);//关闭硬件加速 1050 | canvas.translate(halfWidth, halfHeight); 1051 | canvas.scale(scale, scale); 1052 | canvas.rotate(30f * mValue7); 1053 | 1054 | //绘制新生内圈星星 1055 | float tmpRadius = radiusStar * mValue7 / scale; 1056 | canvas.drawCircle(0, -radiusMask, tmpRadius, paintCircleStar); 1057 | canvas.drawCircle(0, radiusMask, tmpRadius, paintCircleStar); 1058 | canvas.drawCircle(-sqrtMask, -halfMask, tmpRadius, paintCircleStar); 1059 | canvas.drawCircle(-sqrtMask, halfMask, tmpRadius, paintCircleStar); 1060 | canvas.drawCircle(sqrtMask, -halfMask, tmpRadius, paintCircleStar); 1061 | canvas.drawCircle(sqrtMask, halfMask, tmpRadius, paintCircleStar); 1062 | 1063 | canvas.restore(); 1064 | } 1065 | 1066 | //绘制新生内圈星星的名字 1067 | { 1068 | canvas.save(); 1069 | canvas.translate(halfWidth, halfHeight); 1070 | float r = -radiusMask * scale; 1071 | 1072 | for (int i = 0; i < 6; i++) { 1073 | canvas.drawText(appNameOuterTmp[(appNameIndex + i) % 6], (float) (r * Math.cos((30 + 60 * i + a) * pi)), (float) (r * Math.sin((30 + 60 * i + a) * pi) + textVer), paintTextTmp); 1074 | } 1075 | canvas.restore(); 1076 | } 1077 | { 1078 | //绘制内圈星星名字 1079 | canvas.save(); 1080 | canvas.translate(halfWidth, halfHeight); 1081 | float tmpRadius2 = -radius2 * scale; 1082 | float tmpRadius3 = -radius3 * scale3; 1083 | 1084 | for (int i = 0; i < 6; i++) { 1085 | canvas.drawText(appNameInnerTmp[(appNameIndex + i) % 6], (float) (tmpRadius2 * Math.cos((60 * i + a) * pi)), (float) (tmpRadius2 * Math.sin((60 * i + a) * pi) + textVer), paintText); 1086 | } 1087 | 1088 | //绘制外圈星星名字 1089 | 1090 | for (int i = 0; i < 6; i++) { 1091 | canvas.drawText(appNameOuterTmp[(appNameIndex + i) % 6], (float) (tmpRadius3 * Math.cos((30 + 60 * i + a) * pi)), (float) (tmpRadius3 * Math.sin((30 + 60 * i + a) * pi) + textVer), paintText); 1092 | } 1093 | canvas.restore(); 1094 | } 1095 | 1096 | if (mValue7 == mValueMax) { 1097 | 1098 | canvas.save(); 1099 | canvas.translate(halfWidth, halfHeight); 1100 | 1101 | float tmpRadius = radiusStar; 1102 | canvas.drawCircle(-radius2, 0, tmpRadius, paintCircleStar); 1103 | canvas.drawCircle(radius2, 0, tmpRadius, paintCircleStar); 1104 | canvas.drawCircle(-radius2 / 2, -sqrt3 * radius2, tmpRadius, paintCircleStar); 1105 | canvas.drawCircle(-radius2 / 2, sqrt3 * radius2, tmpRadius, paintCircleStar); 1106 | canvas.drawCircle(radius2 / 2, -sqrt3 * radius2, tmpRadius, paintCircleStar); 1107 | canvas.drawCircle(radius2 / 2, sqrt3 * radius2, tmpRadius, paintCircleStar); 1108 | canvas.restore(); 1109 | a = 0; 1110 | } 1111 | 1112 | } 1113 | 1114 | /** 1115 | * 阶段八 1116 | * 绘制星团(一次性动画) 1117 | * 1118 | * @param canvas 1119 | */ 1120 | private void drawState8(Canvas canvas) { 1121 | 1122 | //绘制星团 1123 | canvas.save(); 1124 | canvas.translate(halfWidth, halfHeight); 1125 | 1126 | Rect src_bg = new Rect(0, 0, particallevel1.getWidth(), particallevel1.getHeight()); 1127 | float scale = (mValue8 * 2f) + 4; // 缩放比例 1 - 6 1128 | float tempX = halfWidth * scale; 1129 | float tempY = halfHeight * scale; 1130 | RectF dst_bg = new RectF(-tempX, -tempY, tempX, tempY); 1131 | 1132 | /*canvas.drawBitmap(particallevel2, src_bg, dst_bg, paintBg);//共用背景画笔,保持对比度一致 1133 | canvas.drawBitmap(particallevel3, src_bg, dst_bg, paintBg);//共用背景画笔,保持对比度一致*/ 1134 | canvas.restore(); 1135 | } 1136 | 1137 | /** 1138 | * 阶段九 1139 | * 绘制某个app的动态小水波纹 1140 | * 1141 | * @param canvas 1142 | * @return 1143 | */ 1144 | private void drawState9(Canvas canvas) { 1145 | 1146 | int alpha = 255; 1147 | 1148 | float coord[][] = { 1149 | {-radiusSm, 0}, {radiusSm, 0}, {-coord3, -coord1}, 1150 | {-coord3, coord1}, {coord3, -coord1}, {coord3, coord1}, 1151 | {0, -radiusLg}, {0, radiusLg}, {-coord2, -coord4}, 1152 | {-coord2, coord4}, {coord2, -coord4}, {coord2, coord4}}; 1153 | 1154 | float x = coord[waveRandom][0]; 1155 | float y = coord[waveRandom][1]; 1156 | 1157 | 1158 | if (mValue9 > mIntValueMax / 2) { 1159 | alpha = (int) (255 * 2f * (100 - mValue9) / 100); 1160 | } else { 1161 | alpha = (int) (255 * 2f * mValue9 / 100); 1162 | } 1163 | paintWave1.setAlpha(alpha); 1164 | canvas.save(); 1165 | float scale1 = (mValue9 * 1f / 100) + 1; // 缩放比例 1 - 2 1166 | float scale2 = (mValue9 * 1.6f / 100) + 1; // 缩放比例 1 - 2 1167 | canvas.translate(halfWidth, halfHeight); 1168 | canvas.drawCircle(x, y, radiusAppWave, paintWave1); 1169 | canvas.restore(); 1170 | 1171 | canvas.save(); 1172 | canvas.translate(halfWidth, halfHeight); 1173 | canvas.drawCircle(x, y, (radiusAppWave - 8) * scale2, paintWave1); 1174 | canvas.restore(); 1175 | 1176 | canvas.save(); 1177 | canvas.translate(halfWidth, halfHeight); 1178 | canvas.drawCircle(x, y, (radiusAppWave) * scale1, paintWave1); 1179 | canvas.restore(); 1180 | 1181 | paintWave1.setAlpha(alpha / 2); 1182 | canvas.save(); 1183 | canvas.translate(halfWidth, halfHeight); 1184 | canvas.drawCircle(x, y, (radiusAppWave + 8) * scale1, paintWave1); 1185 | canvas.restore(); 1186 | 1187 | } 1188 | 1189 | 1190 | @Override 1191 | public boolean onTouchEvent(MotionEvent event) { 1192 | 1193 | boolean isCancel = false; 1194 | int action = event.getAction(); 1195 | 1196 | if (!valueAnimator9.isStarted() || valueAnimator7.isRunning()) { 1197 | return false; 1198 | } 1199 | 1200 | switch (action) { 1201 | case MotionEvent.ACTION_UP: 1202 | 1203 | if (!isCancel) { 1204 | 1205 | if (!valueAnimator7.isRunning()) { 1206 | mValue9 = 0; 1207 | valueAnimator9.end(); 1208 | mValue7 = 0.f; 1209 | valueAnimator7.setStartDelay(0); 1210 | valueAnimator7.start(); 1211 | isRefresh = true; 1212 | valueAnimator8.start(); 1213 | appSwitch = !appSwitch; 1214 | } 1215 | 1216 | } 1217 | break; 1218 | case MotionEvent.ACTION_CANCEL: 1219 | 1220 | isCancel = true; 1221 | break; 1222 | } 1223 | 1224 | return true; 1225 | } 1226 | } 1227 | -------------------------------------------------------------------------------- /app/src/main/java/com/osan/nebula/util/Utils.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula.util; 2 | 3 | import android.content.res.Resources; 4 | import android.util.TypedValue; 5 | 6 | import com.osan.nebula.App; 7 | 8 | /** 9 | * Created by osan on 2017/7/5. 10 | */ 11 | 12 | public class Utils { 13 | 14 | /** 15 | * dp2px 16 | */ 17 | public static int dip2px(float dpValue) { 18 | final float scale = App.context.getResources().getDisplayMetrics().density; 19 | return (int) (dpValue * scale + 0.5f); 20 | } 21 | 22 | /** 23 | * px2dp 24 | */ 25 | public static int px2dip(float pxValue) { 26 | final float scale = App.context.getResources().getDisplayMetrics().density; 27 | return (int) (pxValue / scale + 0.5f); 28 | } 29 | /** 30 | *根据设备信息获取当前分辨率下指定单位对应的像素大小; 31 | * px,dip,sp -> px 32 | */ 33 | public float getRawSize(int unit, float size) { 34 | Resources r; 35 | if (App.context == null){ 36 | r = Resources.getSystem(); 37 | }else{ 38 | r = App.context.getResources(); 39 | } 40 | return TypedValue.applyDimension(unit, size, r.getDisplayMetrics()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_grow_fade_in_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_popup_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_popup_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_shrink_fade_out_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_slide_in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_slide_in_top.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_slide_out_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/abc_slide_out_top.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/anim/design_bottom_sheet_slide_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/design_bottom_sheet_slide_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/design_fab_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/design_fab_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/design_snackbar_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/design_snackbar_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/i_panel_acc.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/load_rotate_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/anim/panel_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/anim/panel_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/anim/sub_activity_close_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/anim/sub_activity_close_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/sub_activity_open_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/sub_activity_open_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_app_flow.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_flash.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/bg.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/light.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/particallevel1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/particallevel1.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/particallevel2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/particallevel2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/particallevel3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/particallevel3.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/particallevel4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/particallevel4.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/sky_layer1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/sky_layer1.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/sky_layer2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/sky_layer2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/vortex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/app/src/main/res/mipmap-xxxhdpi/vortex.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 20 | 21 | 35 | 36 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/anims.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @*android:anim/activity_close_enter 4 | @*android:anim/activity_close_exit 5 | @*android:anim/activity_open_enter 6 | @*android:anim/activity_open_exit 7 | @*android:anim/fade_in 8 | @*android:anim/fade_out 9 | @*android:anim/grow_fade_in_from_bottom 10 | @*android:anim/push_down_in 11 | @*android:anim/push_down_out 12 | @*android:anim/push_up_in 13 | @*android:anim/push_up_out 14 | @*android:anim/shrink_fade_out_from_bottom 15 | @*android:anim/task_close_enter 16 | @*android:anim/task_close_exit 17 | @*android:anim/task_open_enter 18 | @*android:anim/task_open_exit 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #50000000 8 | #50888888 9 | #501d557b 10 | #8a58ae 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2 4 | 2 5 | 220 6 | 150 7 | @*android:integer/config_criticalBatteryWarningLevel 8 | @*android:integer/config_lowBatteryWarningLevel 9 | @*android:integer/config_screenBrightnessDim 10 | @*android:integer/preference_fragment_scrollbarStyle 11 | 150 12 | 127 13 | 2000 14 | 4000 15 | 0 16 | 2 17 | 178 18 | 999 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | nebula 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 14 | 15 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/test/java/com/osan/nebula/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.osan.nebula; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /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.3.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | println ">>> rootProject.buildDir: ${rootProject.buildDir}" 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 05 13:59:27 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/nebula.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangfuda/nebula/feb5dad2c222830deccbebcb47c2edf0f7f6f5f7/screenshots/nebula.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------