├── FloatingSettingView ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── sheng │ │ │ └── preferencefloatingview │ │ │ ├── MainActivity.java │ │ │ └── floating │ │ │ ├── BaseDrawer.java │ │ │ ├── ContainerView.java │ │ │ ├── IBaseHolder.java │ │ │ └── circle │ │ │ ├── floatingdrawer │ │ │ ├── CircleHolder.java │ │ │ ├── PreferenceFloatingDrawer.java │ │ │ └── ThirdCircle.java │ │ │ └── splashdrawer │ │ │ ├── BaseSplashDrawer.java │ │ │ ├── IOnAnimEndListener.java │ │ │ ├── RotationDrawer.java │ │ │ ├── RotationSweepView.java │ │ │ ├── SlowInSlowOutInterpolator.java │ │ │ ├── SweepDrawer.java │ │ │ └── TransitionDrawer.java │ │ └── res │ │ ├── drawable │ │ ├── b07.png │ │ ├── b3l.png │ │ ├── b3m.png │ │ ├── b3n.png │ │ ├── b3o.png │ │ ├── b3p.png │ │ ├── b3q.png │ │ ├── b3r.png │ │ ├── b3s.png │ │ └── b3t.png │ │ ├── layout │ │ └── activity_main.xml │ │ └── mipmap-xhdpi │ │ └── ic_launcher.png ├── build.gradle ├── floating.png ├── floating1.jpeg ├── floating2.gif ├── floating2.jpeg ├── floating3.jpeg ├── gradle.properties ├── settings.gradle └── wangyi.png └── README.md /FloatingSettingView/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /FloatingSettingView/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /FloatingSettingView/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | defaultConfig { 6 | applicationId "com.sheng.preferencefloatingview" 7 | minSdkVersion 15 8 | targetSdkVersion 25 9 | versionCode 3 10 | versionName "2.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | } 24 | -------------------------------------------------------------------------------- /FloatingSettingView/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/wangsheng/Documents/developSoftware/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 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 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.util.DisplayMetrics; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.ViewTreeObserver; 12 | import android.view.WindowManager; 13 | 14 | import com.sheng.preferencefloatingview.floating.ContainerView; 15 | import com.sheng.preferencefloatingview.floating.circle.floatingdrawer.PreferenceFloatingDrawer; 16 | import com.sheng.preferencefloatingview.floating.circle.splashdrawer.IOnAnimEndListener; 17 | import com.sheng.preferencefloatingview.floating.circle.splashdrawer.RotationSweepView; 18 | 19 | /** 20 | * @author sheng 21 | */ 22 | public class MainActivity extends Activity implements View.OnClickListener{ 23 | 24 | private ContainerView containerView; 25 | private RotationSweepView rotationView; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 32 | 33 | findViewById(R.id.close).setOnClickListener(this); 34 | findViewById(R.id.save).setOnClickListener(this); 35 | 36 | containerView = (ContainerView) findViewById(R.id.floatingView); 37 | ViewGroup.LayoutParams params = containerView.getLayoutParams(); 38 | int width = params.width=getMetricsWidth(this)*7/5; 39 | containerView.setLayoutParams(params); 40 | containerView.setDrawer(new PreferenceFloatingDrawer(this)); 41 | 42 | rotationView = (RotationSweepView) findViewById(R.id.rotationView); 43 | rotationView.setIOnAnimEndListener(new IOnAnimEndListener() { 44 | @Override 45 | public void onAnimEnd() { 46 | animStart(); 47 | } 48 | }); 49 | rotationView.setDistanceXYR(0,0.35f*width,0.3f*width,0.11f*width); 50 | rotationView.setDistanceXYR(1,0.75f*width,0.32f*width,0.105f*width); 51 | rotationView.setDistanceXYR(2,0.25f*width,0.57f*width,0.14f*width); 52 | rotationView.setDistanceXYR(3,0.68f*width,0.75f*width,0.12f*width); 53 | rotationView.setDistanceXYR(4,0.42f*width,0.8f*width,0.1f*width); 54 | rotationView.setDistanceXYR(5,0.57f*width,0.5f*width,0.13f*width); 55 | rotationView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 56 | @Override 57 | public void onGlobalLayout() { 58 | int width = rotationView.getWidth(); 59 | int height = rotationView.getHeight(); 60 | rotationView.setCenterXY(width/2,height/2); 61 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 62 | rotationView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 63 | } 64 | containerView.start(); 65 | } 66 | }); 67 | } 68 | 69 | private void animStart(){ 70 | ValueAnimator animator = ValueAnimator.ofFloat(255,0); 71 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 72 | @Override 73 | public void onAnimationUpdate(ValueAnimator animation) { 74 | rotationView.setAlpha((Float) animation.getAnimatedValue()); 75 | if(((Float) animation.getAnimatedValue())==0){ 76 | rotationView.setVisibility(View.GONE); 77 | containerView.setDrawerStop(false); 78 | } 79 | } 80 | }); 81 | animator.setDuration(500); 82 | animator.start(); 83 | } 84 | 85 | @Override 86 | protected void onResume() { 87 | super.onResume(); 88 | containerView.onResume(); 89 | } 90 | @Override 91 | protected void onPause() { 92 | super.onPause(); 93 | containerView.onPause(); 94 | } 95 | @Override 96 | protected void onDestroy() { 97 | super.onDestroy(); 98 | containerView.onDestroy(); 99 | } 100 | 101 | public static int getMetricsWidth(Context context) 102 | { 103 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 104 | return dm.widthPixels; 105 | } 106 | 107 | @Override 108 | public void onClick(View v) { 109 | switch (v.getId()){ 110 | case R.id.close: 111 | finish(); 112 | break; 113 | case R.id.save: 114 | finish(); 115 | break; 116 | default: 117 | break; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/BaseDrawer.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.view.MotionEvent; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Random; 11 | 12 | /** 13 | * 抽象的基础绘制类 需要定制浮动的图像请继承之 14 | * @author sheng 15 | */ 16 | public abstract class BaseDrawer { 17 | 18 | protected ArrayList holders; 19 | protected Paint paint; 20 | /** 21 | * 暂停 22 | */ 23 | private boolean isStop; 24 | 25 | public static final class FloatingBackground { 26 | public static final int[] WHITE = new int[] { 0xFFFFFFFF, 0xFFFFFFFF }; 27 | public static final int[] TRANS = new int[] { 0x00000000, 0x00000000 }; 28 | } 29 | 30 | protected Context context; 31 | protected int width, height; 32 | private GradientDrawable gradientDrawable; 33 | 34 | public BaseDrawer(Context context) { 35 | super(); 36 | this.context = context; 37 | this.holders = new ArrayList<>(); 38 | this.paint = new Paint(Paint.ANTI_ALIAS_FLAG); 39 | } 40 | 41 | public GradientDrawable makeFloatingBackground(){ 42 | return new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, getFloatingBackgroundGradient()); 43 | } 44 | 45 | protected void drawFloatingBackground(Canvas canvas, float alpha) { 46 | if (gradientDrawable == null) { 47 | gradientDrawable = makeFloatingBackground(); 48 | gradientDrawable.setBounds(0, 0, width, height); 49 | } 50 | gradientDrawable.setAlpha(Math.round(alpha * 255f)); 51 | gradientDrawable.draw(canvas); 52 | } 53 | 54 | /** 55 | * @param canvas 56 | * @return needDrawNextFrame 57 | */ 58 | public boolean draw(Canvas canvas, float alpha) { 59 | drawFloatingBackground(canvas, alpha); 60 | boolean needDrawNextFrame = drawGraphics(canvas, alpha); 61 | return needDrawNextFrame; 62 | } 63 | 64 | /** 65 | * 绘制 66 | * @param canvas 画布 67 | * @param alpha 透明度 68 | * @return 是否需要绘制下一帧 69 | */ 70 | public boolean drawGraphics(Canvas canvas, float alpha){ 71 | for (IBaseHolder holder : this.holders) { 72 | holder.updateAndDraw(canvas, paint); 73 | } 74 | return true; 75 | } 76 | 77 | public void addHolder(IBaseHolder holder){ 78 | if (holders!=null) { 79 | holders.add(holder); 80 | } 81 | } 82 | 83 | public ArrayList getIHolders(){ 84 | return holders; 85 | } 86 | 87 | protected int[] getFloatingBackgroundGradient() { 88 | return FloatingBackground.TRANS; 89 | } 90 | 91 | protected void setSize(int width, int height) { 92 | if(this.width != width && this.height != height){ 93 | this.width = width; 94 | this.height = height; 95 | if (this.gradientDrawable != null) { 96 | gradientDrawable.setBounds(0, 0, width, height); 97 | } 98 | } 99 | 100 | } 101 | 102 | /** 103 | * 处理触摸事件 默认为空 子类覆写 104 | * 105 | * @param event 由view传递过来的事件 106 | */ 107 | protected void onTouch(MotionEvent event) { 108 | 109 | } 110 | 111 | /** 112 | * 获得0--n之内的不等概率随机整数,0概率最大,1次之,以此递减,n最小 113 | */ 114 | public static int getAnyRandInt(int n) { 115 | int max = n + 1; 116 | int bigend = ((1 + max) * max) / 2; 117 | Random rd = new Random(); 118 | int x = Math.abs(rd.nextInt() % bigend); 119 | int sum = 0; 120 | for (int i = 0; i < max; i++) { 121 | sum += (max - i); 122 | if (sum > x) { 123 | return i; 124 | } 125 | } 126 | return 0; 127 | } 128 | 129 | /** 130 | * 获取[min, max)内的随机数,越大的数概率越小 131 | * 132 | * @param min 133 | * @param max 134 | * @return 135 | */ 136 | public static float getDownRandFloat(float min, float max) { 137 | float bigend = ((min + max) * max) / 2f; 138 | // Random rd = new Random(); 139 | // Math.abs(rd.nextInt() % bigend) 140 | float x = getRandom(min, bigend); 141 | int sum = 0; 142 | for (int i = 0; i < max; i++) { 143 | sum += (max - i); 144 | if (sum > x) { 145 | return i; 146 | } 147 | } 148 | return min; 149 | } 150 | 151 | /** 152 | * 获取在最大最小区间中的随机数 153 | * [min, max) 154 | * 155 | * @param min 156 | * @param max 157 | * @return 158 | */ 159 | public static float getRandom(float min, float max) { 160 | if (max < min) { 161 | throw new IllegalArgumentException("max should bigger than min!!!!"); 162 | } 163 | return (float) (min + Math.random() * (max - min)); 164 | } 165 | 166 | public boolean setStop(boolean stop) { 167 | isStop = stop; 168 | for (IBaseHolder holder : holders) { 169 | holder.stop(stop); 170 | } 171 | return isStop; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/ContainerView.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.PixelFormat; 7 | import android.graphics.PorterDuff; 8 | import android.util.AttributeSet; 9 | import android.util.Log; 10 | import android.view.MotionEvent; 11 | import android.view.SurfaceHolder; 12 | import android.view.SurfaceView; 13 | import android.view.animation.AnimationUtils; 14 | 15 | /** 16 | * 提取公共代码封装的Drawer容器 17 | * @author sheng 18 | */ 19 | public class ContainerView extends SurfaceView implements SurfaceHolder.Callback { 20 | 21 | static final String TAG = ContainerView.class.getSimpleName(); 22 | private DrawThread mDrawThread; 23 | private BaseDrawer mDrawer; 24 | 25 | public ContainerView(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | init(context); 28 | } 29 | 30 | private BaseDrawer preDrawer, curDrawer; 31 | private float curDrawerAlpha = 0.5f; 32 | private int mWidth, mHeight; 33 | 34 | private void init(Context context) { 35 | curDrawerAlpha = 0f; 36 | mDrawThread = new DrawThread(); 37 | final SurfaceHolder surfaceHolder = getHolder(); 38 | surfaceHolder.addCallback(this); 39 | setZOrderOnTop(true); 40 | getHolder().setFormat(PixelFormat.TRANSLUCENT); 41 | 42 | } 43 | 44 | /** 45 | * 暂停绘制 46 | * @param stop 47 | */ 48 | public void setDrawerStop(boolean stop) { 49 | mDrawer.setStop(stop); 50 | } 51 | 52 | public void start(){ 53 | mDrawThread.start(); 54 | } 55 | 56 | private void initDrawer(BaseDrawer baseDrawer) { 57 | if (baseDrawer == null) { 58 | return; 59 | } 60 | curDrawerAlpha = 0f; 61 | if (this.curDrawer != null) { 62 | this.preDrawer = curDrawer; 63 | } 64 | this.curDrawer = baseDrawer; 65 | mDrawer = curDrawer; 66 | updateDrawerSize(getWidth(), getHeight()); 67 | invalidate(); 68 | } 69 | 70 | public void setDrawer(BaseDrawer drawer) { 71 | if (drawer == null) { 72 | return; 73 | } 74 | if (drawer != curDrawer) { 75 | curDrawer = drawer; 76 | initDrawer(drawer); 77 | } 78 | } 79 | 80 | @Override 81 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 82 | super.onSizeChanged(w, h, oldw, oldh); 83 | updateDrawerSize(w, h); 84 | mWidth = w; 85 | mHeight = h; 86 | } 87 | 88 | public void updateDrawerSize(int w, int h) { 89 | if (w == 0 || h == 0) { 90 | return; 91 | }// 必须加锁,因为在DrawThread.drawSurface的时候调用的是各种Drawer的绘制方法 92 | // 绘制的时候会遍历内部的各种holder 93 | if (this.curDrawer != null) { 94 | synchronized (curDrawer) { 95 | if (this.curDrawer != null) { 96 | curDrawer.setSize(w, h); 97 | } 98 | } 99 | } 100 | if (this.preDrawer != null) { 101 | synchronized (preDrawer) { 102 | if (this.preDrawer != null) { 103 | preDrawer.setSize(w, h); 104 | } 105 | } 106 | } 107 | 108 | } 109 | 110 | private boolean drawSurface(Canvas canvas) { 111 | final int w = mWidth; 112 | final int h = mHeight; 113 | if (w == 0 || h == 0) { 114 | return true; 115 | } 116 | boolean needDrawNextFrame = false; 117 | if (curDrawer != null) { 118 | curDrawer.setSize(w, h); 119 | needDrawNextFrame = curDrawer.draw(canvas, curDrawerAlpha); 120 | } 121 | if (preDrawer != null && curDrawerAlpha < 1f) { 122 | needDrawNextFrame = true; 123 | preDrawer.setSize(w, h); 124 | preDrawer.draw(canvas, 1f - curDrawerAlpha); 125 | } 126 | if (curDrawerAlpha < 1f) { 127 | curDrawerAlpha += 0.04f; 128 | 129 | if (curDrawerAlpha > 1) { 130 | curDrawerAlpha = 1f; 131 | preDrawer = null; 132 | } 133 | } 134 | return needDrawNextFrame; 135 | } 136 | 137 | public void onResume() { 138 | // Let the drawing thread resume running. 139 | synchronized (mDrawThread) { 140 | mDrawThread.mRunning = true; 141 | mDrawThread.notify(); 142 | } 143 | Log.i(TAG, "onResume"); 144 | } 145 | 146 | public void onPause() { 147 | // Make sure the drawing thread is not running while we are paused. 148 | synchronized (mDrawThread) { 149 | mDrawThread.mRunning = false; 150 | mDrawThread.notify(); 151 | } 152 | Log.i(TAG, "onPause"); 153 | } 154 | 155 | public void onDestroy() { 156 | // Make sure the drawing thread goes away. 157 | synchronized (mDrawThread) { 158 | mDrawThread.mQuit = true; 159 | mDrawThread.notify(); 160 | } 161 | Log.i(TAG, "onDestroy"); 162 | } 163 | 164 | @Override 165 | public boolean onTouchEvent(MotionEvent e) { 166 | mDrawer.onTouch(e); 167 | //继续执行后面的代码 168 | return super.onTouchEvent(e); 169 | } 170 | 171 | @Override 172 | public void surfaceCreated(SurfaceHolder holder) { 173 | // Tell the drawing thread that a surface is available. 174 | synchronized (mDrawThread) { 175 | mDrawThread.mSurface = holder; 176 | mDrawThread.notify(); 177 | } 178 | 179 | Log.i(TAG, "surfaceCreated"); 180 | } 181 | 182 | @Override 183 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 184 | } 185 | 186 | @Override 187 | public void surfaceDestroyed(SurfaceHolder holder) { 188 | // We need to tell the drawing thread to stop, and block until 189 | // it has done so. 190 | synchronized (mDrawThread) { 191 | mDrawThread.mSurface = holder; 192 | mDrawThread.notify(); 193 | while (mDrawThread.mActive) { 194 | try { 195 | mDrawThread.wait(); 196 | } catch (InterruptedException e) { 197 | e.printStackTrace(); 198 | } 199 | } 200 | } 201 | holder.removeCallback(this); 202 | Log.i(TAG, "surfaceDestroyed"); 203 | } 204 | 205 | private class DrawThread extends Thread { 206 | /** 207 | * These are protected by the Thread's lock. 208 | */ 209 | SurfaceHolder mSurface; 210 | boolean mRunning; 211 | boolean mActive; 212 | boolean mQuit; 213 | 214 | @Override 215 | public void run() { 216 | while (true) { 217 | // Log.i(TAG, "DrawThread run.."); 218 | // Synchronize with activity: block until the activity is ready 219 | // and we have a surface; report whether we are active or 220 | // inactive 221 | // at this point; exit thread when asked to quit. 222 | synchronized (this) { 223 | while (mSurface == null || !mRunning) { 224 | if (mActive) { 225 | mActive = false; 226 | notify(); 227 | } 228 | if (mQuit) { 229 | return; 230 | } 231 | try { 232 | wait(); 233 | } catch (InterruptedException e) { 234 | } 235 | } 236 | 237 | if (!mActive) { 238 | mActive = true; 239 | notify(); 240 | } 241 | final long startTime = AnimationUtils.currentAnimationTimeMillis(); 242 | // Lock the canvas for drawing. 243 | Canvas canvas = mSurface.lockCanvas(); 244 | 245 | if (canvas != null) { 246 | canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 247 | // Update graphics. 248 | 249 | drawSurface(canvas); 250 | // All done! 251 | mSurface.unlockCanvasAndPost(canvas); 252 | } else { 253 | Log.i(TAG, "Failure locking canvas"); 254 | } 255 | final long drawTime = AnimationUtils.currentAnimationTimeMillis() - startTime; 256 | final long needSleepTime = 16 - drawTime; 257 | if (needSleepTime > 0) { 258 | try { 259 | Thread.sleep(needSleepTime); 260 | } catch (InterruptedException e) { 261 | e.printStackTrace(); 262 | } 263 | } 264 | 265 | } 266 | } 267 | } 268 | } 269 | 270 | public BaseDrawer getDrawer(){ 271 | return mDrawer; 272 | } 273 | 274 | } 275 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/IBaseHolder.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * 统一的绘制类的接口 8 | * @author sheng 9 | */ 10 | public interface IBaseHolder { 11 | /** 12 | * 子类需要覆写的方法 目的是将View的画布和画笔传递给 实现对象 在对象类中完成具体的绘制逻辑 13 | * @param canvas 画布 14 | * @param paint 画笔 15 | */ 16 | void updateAndDraw(Canvas canvas,Paint paint); 17 | 18 | /** 19 | * 暂停/继续 20 | * @param flag 21 | */ 22 | void stop(boolean flag); 23 | } 24 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/floatingdrawer/CircleHolder.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.floatingdrawer; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ArgbEvaluator; 5 | import android.animation.ValueAnimator; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.DashPathEffect; 9 | import android.graphics.Paint; 10 | import android.graphics.Rect; 11 | import android.view.animation.AccelerateDecelerateInterpolator; 12 | import android.view.animation.AnticipateOvershootInterpolator; 13 | 14 | import com.sheng.preferencefloatingview.floating.BaseDrawer; 15 | import com.sheng.preferencefloatingview.floating.IBaseHolder; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * 浮动的圆形小球具体的绘制 这个是浮动效果的核心类 22 | * 23 | * TODO 浮动效果优化:现在的效果是在一条直线上来回运动,需要优化成在一定范围内运动,在边界弹向其它方向 24 | * H5效果类似 http://blog.csdn.net/chen_gong1992/article/details/53313383 25 | * 读者可以将js关键代码翻译成java 26 | * @author sheng 27 | */ 28 | public class CircleHolder implements IBaseHolder { 29 | /** 30 | * 初始圆心坐标 偏移距离 31 | */ 32 | private final float cx, cy, dx, dy; 33 | /** 34 | * 颜色 35 | */ 36 | private int color; 37 | /** 38 | * 圆的移动是增长还是缩减 39 | */ 40 | private boolean isGrowing = true; 41 | private float curPercent = 0f; 42 | /** 43 | * 速度 44 | */ 45 | private final float percentSpeed; 46 | private String name = "虚位以待"; 47 | private int textSize = 40; 48 | private Rect rect; 49 | 50 | public float curCX,curCY,radius; 51 | 52 | /** 53 | * 选中的小圆相对于大圆的比率 54 | */ 55 | public float rate; 56 | /** 57 | * 选中圆心的坐标 偏移量 58 | */ 59 | private final float smallCx,smallCy,smallDx,smallDy; 60 | /** 61 | * 选中的小圆填充色 62 | */ 63 | private int smallColor; 64 | /** 65 | * 小圆运动速率 66 | */ 67 | private final float smallPercentSpeed; 68 | public float curSmallCX,curSmallCY,smallRadius; 69 | /** 70 | * 当前是否是大圆 71 | */ 72 | private boolean isNowBigCircle = true; 73 | /** 74 | * 小圆文字对应的矩形 75 | */ 76 | private Rect smallRect; 77 | 78 | /** 79 | * 卫星圆中文字对应的矩形 80 | */ 81 | private Rect thirdRect; 82 | /** 83 | * 初始的角度 84 | */ 85 | private int angle; 86 | /** 87 | * 缓存的卫星对象 88 | */ 89 | private List thirdCircles; 90 | 91 | private boolean isAnim = false; 92 | 93 | private boolean isStop = true; 94 | 95 | private CircleHolder circleHolder; 96 | private CircleHolder(Builder builder){ 97 | this.cx = builder.cx; 98 | this.cy = builder.cy; 99 | this.dx = builder.dx; 100 | this.dy = builder.dy; 101 | this.radius = builder.radius; 102 | this.percentSpeed = builder.percentSpeed; 103 | this.color = builder.color; 104 | this.name = builder.name; 105 | this.rect = new Rect(); 106 | 107 | this.rate = builder.rate; 108 | this.smallCx = builder.cx+builder.translateX; 109 | this.smallCy = builder.cy+builder.translateY; 110 | this.smallDx = builder.dx*rate; 111 | this.smallDy = builder.dy*rate; 112 | this.smallRadius = builder.radius*rate; 113 | this.smallPercentSpeed = builder.percentSpeed*rate; 114 | this.smallColor = builder.smallColor; 115 | this.smallRect = new Rect(); 116 | this.thirdRect = new Rect(); 117 | this.angle = builder.angle; 118 | this.thirdCircles = builder.thirdCircles; 119 | 120 | circleHolder = this; 121 | } 122 | 123 | @Override 124 | public void updateAndDraw(Canvas canvas, Paint paint) { 125 | if (isStop) { 126 | curCX = cx; 127 | curCY = cy; 128 | return; 129 | }else { 130 | float randomPercentSpeed = isNowBigCircle ? BaseDrawer.getRandom(percentSpeed * 0.7f, percentSpeed * 1.3f):BaseDrawer.getRandom(smallPercentSpeed * 0.7f, smallPercentSpeed * 1.3f); 131 | if (isGrowing) { 132 | curPercent += randomPercentSpeed; 133 | if (curPercent > 1f) { 134 | curPercent = 1f; 135 | isGrowing = false; 136 | } 137 | } else { 138 | curPercent -= randomPercentSpeed; 139 | if (curPercent < 0f) { 140 | curPercent = 0f; 141 | isGrowing = true; 142 | } 143 | } 144 | curCX = cx + dx * curPercent; 145 | curCY = cy + dy * curPercent; 146 | } 147 | 148 | if (isNowBigCircle) { 149 | paint.setColor(smallColor); 150 | paint.setStyle(Paint.Style.STROKE); 151 | paint.setPathEffect(new DashPathEffect(new float[]{2, 2}, 0)); 152 | paint.setStrokeWidth(1); 153 | canvas.drawCircle(curCX, curCY, radius+1, paint); 154 | paint.setStyle(Paint.Style.FILL); 155 | paint.setPathEffect(null); 156 | paint.setColor(color); 157 | canvas.drawCircle(curCX, curCY, radius, paint); 158 | 159 | paint.setTextSize(textSize); 160 | paint.getTextBounds(name, 0, name.length(), rect); 161 | paint.setColor(smallColor); 162 | canvas.drawText(name, curCX - rect.width() / 2, curCY + rect.height() / 2, paint); 163 | }else{ 164 | if (null==thirdCircles) { 165 | return; 166 | } 167 | paint.setColor(smallColor); 168 | //绘制两个背景圆圈 169 | paint.setStyle(Paint.Style.STROKE); 170 | paint.setStrokeWidth(1.5F); 171 | canvas.drawCircle(cx+10 + dx * curPercent*1.3f, cy+20 + dy * curPercent*1.5f, radius, paint); 172 | canvas.drawCircle(cx+20 + dx * curPercent*1.4f, cy+10 + dy * curPercent*1.1f, radius, paint); 173 | paint.setStyle(Paint.Style.FILL); 174 | 175 | //斜边长度为 小圆的半径+间距+最小圆的半径 176 | //已经斜边和角度 177 | //角度的对边=斜边*sin角度 178 | //角度邻边=斜边*cos角度 179 | for (int i = 0; i < thirdCircles.size(); i++) { 180 | ThirdCircle circle = thirdCircles.get(i); 181 | if (!circle.isAnim()) { 182 | circle.setRadius(radius*rate*circle.getRate()); 183 | } 184 | //斜边 185 | float xieLine = circle.getRadius()+radius*rate+15; 186 | //角度=弧长/半径 187 | //转过的角度 l=nπr/180 n=l*180/πr 188 | int dAngle = (int) ((circle.getRadius()*2+15)*i*180/(Math.PI*xieLine)); 189 | //卫星实际的角度 190 | int realAngle = (angle-dAngle); 191 | 192 | float duiLine = (float) (xieLine*Math.sin(realAngle*Math.PI/180)); 193 | float lingLine= (float) (xieLine*Math.cos(realAngle*Math.PI/180)); 194 | 195 | circle.setXieLine(radius*rate+15); 196 | circle.setRealAngle(realAngle); 197 | if (!circle.isSweepAnim()) { 198 | circle.setCx(curSmallCX-lingLine); 199 | circle.setCy(curSmallCY-duiLine); 200 | } 201 | circle.setDx(smallDx*circle.getRate()); 202 | circle.setDy(smallDy*circle.getRate()); 203 | circle.setCurCX(circle.getCx()+circle.getDx()*curPercent); 204 | circle.setCurCY(circle.getCy()+circle.getDy()*curPercent); 205 | circle.draw(paint, canvas, thirdRect); 206 | 207 | } 208 | 209 | //当前小圆的坐标 210 | curSmallCX = smallCx + smallDx * curPercent; 211 | curSmallCY = smallCy + smallDy * curPercent; 212 | 213 | paint.setColor(smallColor); 214 | canvas.drawCircle(curSmallCX, curSmallCY, smallRadius, paint); 215 | 216 | paint.setTextSize(40*rate); 217 | paint.getTextBounds(name, 0, name.length(), smallRect); 218 | paint.setColor(Color.WHITE); 219 | canvas.drawText(name, curSmallCX - smallRect.width() / 2, curSmallCY + smallRect.height() / 2, paint); 220 | } 221 | } 222 | 223 | @Override 224 | public void stop(boolean flag) { 225 | isStop = flag; 226 | } 227 | 228 | /** 229 | * 构建者模式 设定相关属性 230 | */ 231 | public static class Builder{ 232 | private float cx, cy, dx, dy, radius; 233 | private int color; 234 | private float percentSpeed; 235 | private String name; 236 | 237 | private float rate; 238 | private int smallColor; 239 | /** 240 | * 小圆圆心相对于大圆的圆心移动的距离 241 | */ 242 | private float translateX; 243 | private float translateY; 244 | private int angle; 245 | private List thirdCircles = new ArrayList<>(); 246 | 247 | public Builder setCx(float cx) { 248 | this.cx = cx; 249 | return this; 250 | } 251 | 252 | public Builder setCy(float cy) { 253 | this.cy = cy; 254 | return this; 255 | } 256 | 257 | public Builder setDx(float dx) { 258 | this.dx = dx; 259 | return this; 260 | } 261 | 262 | public Builder setDy(float dy) { 263 | this.dy = dy; 264 | return this; 265 | } 266 | 267 | public Builder setRadius(float radius) { 268 | this.radius = radius; 269 | return this; 270 | } 271 | 272 | public Builder setColor(int color) { 273 | this.color = color; 274 | return this; 275 | } 276 | 277 | public Builder setSmallColor(int color) { 278 | this.smallColor = color; 279 | return this; 280 | } 281 | 282 | public Builder setRate(float rate) { 283 | this.rate = rate; 284 | return this; 285 | } 286 | 287 | public Builder setTranslateX(float x) { 288 | this.translateX = x; 289 | return this; 290 | } 291 | 292 | public Builder setTranslateY(float y) { 293 | this.translateY = y; 294 | return this; 295 | } 296 | 297 | public Builder addThirdCircle(ThirdCircle circle) { 298 | thirdCircles.add(circle); 299 | return this; 300 | } 301 | 302 | public Builder setThirdCircleAngle(int angle) { 303 | this.angle=angle; 304 | return this; 305 | } 306 | 307 | public Builder setPercentSpeed(float percentSpeed) { 308 | this.percentSpeed = percentSpeed; 309 | return this; 310 | } 311 | 312 | public Builder setName(String name) { 313 | this.name = name; 314 | return this; 315 | } 316 | 317 | public CircleHolder build(){ 318 | return new CircleHolder(this); 319 | } 320 | } 321 | 322 | public void circleClick(boolean flag){ 323 | if (isAnim) { 324 | return; 325 | } 326 | isNowBigCircle = flag; 327 | animClick(); 328 | for (ThirdCircle circle: 329 | thirdCircles) { 330 | thirdSweepAnimStart(circle); 331 | } 332 | } 333 | 334 | public boolean isNowBigCircle(){ 335 | return isNowBigCircle; 336 | } 337 | 338 | public List getThirdCircles() { 339 | return thirdCircles; 340 | } 341 | 342 | /** 343 | * 点击之后的动画效果 isNowBigCircle = false 大圆缩小->小圆 isNowBigCircle = true 小圆扩散->大圆 中间配上颜色的渐变 344 | */ 345 | private void animClick(){ 346 | final float ra = radius; 347 | final int startColor = circleHolder.color; 348 | final int endColor = smallColor; 349 | final ArgbEvaluator evaluator = new ArgbEvaluator(); 350 | ValueAnimator animator = ValueAnimator.ofFloat(isNowBigCircle ?rate:1, isNowBigCircle ?1:rate); 351 | animator.setDuration(300); 352 | animator.setInterpolator(new AnticipateOvershootInterpolator()); 353 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 354 | @Override 355 | public void onAnimationUpdate(ValueAnimator animation) { 356 | float rate = (float) animation.getAnimatedValue(); 357 | int color = (int) evaluator.evaluate(rate, isNowBigCircle ?endColor:startColor, isNowBigCircle ?startColor:endColor); 358 | if (isNowBigCircle) { 359 | circleHolder.radius = rate*ra; 360 | circleHolder.color = color; 361 | }else { 362 | smallRadius = rate*ra; 363 | } 364 | } 365 | }); 366 | animator.addListener(new Animator.AnimatorListener() { 367 | @Override 368 | public void onAnimationStart(Animator animation) { 369 | isAnim = true; 370 | } 371 | 372 | @Override 373 | public void onAnimationEnd(Animator animation) { 374 | isAnim = false; 375 | } 376 | 377 | @Override 378 | public void onAnimationCancel(Animator animation) { 379 | 380 | } 381 | 382 | @Override 383 | public void onAnimationRepeat(Animator animation) { 384 | 385 | } 386 | }); 387 | animator.start(); 388 | } 389 | 390 | private void thirdSweepAnimStart(final ThirdCircle circle){ 391 | if (null==thirdCircles) { 392 | return; 393 | } 394 | ValueAnimator animator = ValueAnimator.ofFloat(0, circle.getXieLine()); 395 | animator.setDuration(300); 396 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 397 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 398 | @Override 399 | public void onAnimationUpdate(ValueAnimator animation) { 400 | float xieLine = (float) animation.getAnimatedValue(); 401 | float duiLine = (float) (xieLine*Math.sin(circle.getRealAngle()*Math.PI/180)); 402 | float lingLine= (float) (xieLine*Math.cos(circle.getRealAngle()*Math.PI/180)); 403 | circle.setCx((curSmallCX-lingLine)); 404 | circle.setCy(curSmallCY-duiLine); 405 | } 406 | }); 407 | animator.addListener(new Animator.AnimatorListener() { 408 | @Override 409 | public void onAnimationStart(Animator animation) { 410 | circle.setSweepAnim(true); 411 | } 412 | 413 | @Override 414 | public void onAnimationEnd(Animator animation) { 415 | circle.setSweepAnim(false); 416 | } 417 | 418 | @Override 419 | public void onAnimationCancel(Animator animation) { 420 | 421 | } 422 | 423 | @Override 424 | public void onAnimationRepeat(Animator animation) { 425 | 426 | } 427 | }); 428 | animator.start(); 429 | } 430 | 431 | } -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/floatingdrawer/PreferenceFloatingDrawer.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.floatingdrawer; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.view.MotionEvent; 6 | import android.view.ViewConfiguration; 7 | 8 | import com.sheng.preferencefloatingview.floating.BaseDrawer; 9 | import com.sheng.preferencefloatingview.floating.IBaseHolder; 10 | 11 | /** 12 | * 添加浮动的小"组件" 处理点击事件 13 | * 14 | * @author sheng 15 | */ 16 | public class PreferenceFloatingDrawer extends BaseDrawer { 17 | 18 | private long currentMS; 19 | private float downX; 20 | private float downY; 21 | private float moveX; 22 | private float moveY; 23 | 24 | public PreferenceFloatingDrawer(Context context) { 25 | super(context); 26 | } 27 | 28 | @Override 29 | protected void setSize(int width, int height) { 30 | super.setSize(width, height); 31 | if (holders.size() == 0) { 32 | //这里你可以添加各种自定义的holder 但是如果你需要点击事件的话 你需要在 33 | addHolder(new CircleHolder.Builder() 34 | .setCx(0.35f * width) 35 | .setCy(0.3f * width) 36 | .setDx(0.06f * width) 37 | .setDy(0.022f * width) 38 | .setRadius(0.11f * width) 39 | .setPercentSpeed(0.0019f) 40 | .setColor(Color.parseColor("#FFE2FFF8")) 41 | .setName("情感星座") 42 | .setRate(0.7f) 43 | .setSmallColor(Color.parseColor("#FF59DABC")) 44 | .setTranslateX(-20f) 45 | .setTranslateY(50f) 46 | .setThirdCircleAngle(135) 47 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE2FFF8"), "星座", 0.3f, Color.parseColor("#FF59DABC"))) 48 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE2FFF8"), "情感", 0.4f, Color.parseColor("#FF59DABC"))) 49 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE2FFF8"), "心理", 0.5f, Color.parseColor("#FF59DABC"))) 50 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE2FFF8"), "两性", 0.6f, Color.parseColor("#FF59DABC"))) 51 | .build()); 52 | addHolder(new CircleHolder.Builder() 53 | .setCx(0.75f * width) 54 | .setCy(0.32f * width) 55 | .setDx(0.06f * width) 56 | .setDy(0.022f * width) 57 | .setRadius(0.105f * width) 58 | .setPercentSpeed(0.0017f) 59 | .setColor(Color.parseColor("#FFECFCF3")) 60 | .setName("科技数码") 61 | .setRate(0.7f) 62 | .setSmallColor(Color.parseColor("#FF8BEAB4")) 63 | .setTranslateX(0) 64 | .setTranslateY(50f) 65 | .setThirdCircleAngle(160) 66 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFECFCF3"), "互联网", 0.5f, Color.parseColor("#FF8BEAB4"))) 67 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFECFCF3"), "科技", 0.5f, Color.parseColor("#FF8BEAB4"))) 68 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFECFCF3"), "数码", 0.5f, Color.parseColor("#FF8BEAB4"))) 69 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFECFCF3"), "手机", 0.5f, Color.parseColor("#FF8BEAB4"))) 70 | .build()); 71 | addHolder(new CircleHolder.Builder() 72 | .setCx(0.25f * width) 73 | .setCy(0.57f * width) 74 | .setDx(-0.05f * width) 75 | .setDy(0.05f * width) 76 | .setRadius(0.14f * width) 77 | .setPercentSpeed(0.002f) 78 | .setColor(Color.parseColor("#FFFAF6EE")) 79 | .setName("体育赛事") 80 | .setRate(0.7f) 81 | .setSmallColor(Color.parseColor("#FFFACD74")) 82 | .setTranslateX(0) 83 | .setTranslateY(50f) 84 | .setThirdCircleAngle(60) 85 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFAF6EE"), "羽毛球", 0.5f, Color.parseColor("#FFFACD74"))) 86 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFAF6EE"), "拳击", 0.5f, Color.parseColor("#FFFACD74"))) 87 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFAF6EE"), "足球", 0.5f, Color.parseColor("#FFFACD74"))) 88 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFAF6EE"), "乒乓球", 0.5f, Color.parseColor("#FFFACD74"))) 89 | .build()); 90 | addHolder(new CircleHolder.Builder() 91 | .setCx(0.68f * width) 92 | .setCy(0.75f * width) 93 | .setDx(0.08f * width) 94 | .setDy(-0.035f * width) 95 | .setRadius(0.12f * width) 96 | .setPercentSpeed(0.0025f) 97 | .setColor(Color.parseColor("#FFFDF8F8")) 98 | .setName("生活休闲") 99 | .setRate(0.7f) 100 | .setSmallColor(Color.parseColor("#FFFFB0B0")) 101 | .setTranslateX(0) 102 | .setTranslateY(50f) 103 | .setThirdCircleAngle(270) 104 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFDF8F8"), "生活", 0.5f, Color.parseColor("#FFFFB0B0"))) 105 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFDF8F8"), "菜谱", 0.5f, Color.parseColor("#FFFFB0B0"))) 106 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFDF8F8"), "旅游", 0.5f, Color.parseColor("#FFFFB0B0"))) 107 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFFDF8F8"), "美食", 0.5f, Color.parseColor("#FFFFB0B0"))) 108 | .build()); 109 | addHolder(new CircleHolder.Builder() 110 | .setCx(0.42f * width) 111 | .setCy(0.8f * width) 112 | .setDx(0.06f * width) 113 | .setDy(-0.025f * width) 114 | .setRadius(0.1f * width) 115 | .setPercentSpeed(0.0020f) 116 | .setColor(Color.parseColor("#FFE7F7FA")) 117 | .setName("育儿护理") 118 | .setRate(0.7f) 119 | .setSmallColor(Color.parseColor("#FF6BDEF5")) 120 | .setTranslateX(0) 121 | .setTranslateY(50f) 122 | .setThirdCircleAngle(0) 123 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE7F7FA"), "育儿", 0.5f, Color.parseColor("#FF6BDEF5"))) 124 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE7F7FA"), "亲子", 0.5f, Color.parseColor("#FF6BDEF5"))) 125 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE7F7FA"), "备孕", 0.5f, Color.parseColor("#FF6BDEF5"))) 126 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFE7F7FA"), "孕期", 0.5f, Color.parseColor("#FF6BDEF5"))) 127 | .build()); 128 | addHolder(new CircleHolder.Builder() 129 | .setCx(0.57f * width) 130 | .setCy(0.5f * width) 131 | .setDx(-0.05f * width) 132 | .setDy(0.05f * width) 133 | .setRadius(0.13f * width) 134 | .setPercentSpeed(0.00185f) 135 | .setColor(Color.parseColor("#FFF3E2F7")) 136 | .setName("时政资讯") 137 | .setRate(0.7f) 138 | .setSmallColor(Color.parseColor("#FFCA61E4")) 139 | .setTranslateX(0) 140 | .setTranslateY(50f) 141 | .setThirdCircleAngle(100) 142 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFF3E2F7"), "社会", 0.5f, Color.parseColor("#FFCA61E4"))) 143 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFF3E2F7"), "军事", 0.5f, Color.parseColor("#FFCA61E4"))) 144 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFF3E2F7"), "法制", 0.5f, Color.parseColor("#FFCA61E4"))) 145 | .addThirdCircle(new ThirdCircle(Color.parseColor("#FFF3E2F7"), "国际", 0.5f, Color.parseColor("#FFCA61E4"))) 146 | .build()); 147 | } 148 | } 149 | 150 | @Override 151 | protected int[] getFloatingBackgroundGradient() { 152 | return FloatingBackground.TRANS; 153 | } 154 | 155 | @Override 156 | protected void onTouch(MotionEvent e) { 157 | super.onTouch(e); 158 | switch (e.getAction()) { 159 | case MotionEvent.ACTION_DOWN: 160 | downX = e.getX(); 161 | downY = e.getY(); 162 | moveX = 0; 163 | moveY = 0; 164 | currentMS = System.currentTimeMillis(); 165 | break; 166 | case MotionEvent.ACTION_MOVE: 167 | /** 168 | * X轴距离 169 | */ 170 | moveX += Math.abs(e.getX() - downX); 171 | /** 172 | * y轴距离 173 | */ 174 | moveY += Math.abs(e.getY() - downY); 175 | downX = e.getX(); 176 | downY = e.getY(); 177 | break; 178 | case MotionEvent.ACTION_UP: 179 | /** 180 | * 移动时间 181 | */ 182 | long moveTime = System.currentTimeMillis() - currentMS; 183 | //判断是否继续传递信号 184 | if (isClick(moveTime)) { 185 | //不再执行后面的事件,在这句前可写要执行的触摸相关代码。点击事件是发生在触摸弹起后 186 | break; 187 | } else { 188 | for (IBaseHolder hold : getIHolders()) { 189 | if (hold instanceof CircleHolder) { 190 | CircleHolder holder = ((CircleHolder) hold); 191 | //点击位置x坐标与圆心的x坐标的距离 192 | int distanceX = (int) Math.abs(holder.isNowBigCircle() ? holder.curCX - downX : holder.curSmallCX - downX); 193 | //点击位置y坐标与圆心的y坐标的距离 194 | int distanceY = (int) Math.abs(holder.isNowBigCircle() ? holder.curCY - downY : holder.curSmallCY - downY); 195 | //点击位置与圆心的直线距离 196 | int distanceZ = (int) Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); 197 | //如果点击位置与圆心的距离大于圆的半径,证明点击位置没有在圆内 198 | if (distanceZ <= (holder.isNowBigCircle() ? holder.radius : holder.radius * holder.rate)) { 199 | holder.circleClick(!holder.isNowBigCircle()); 200 | } else { 201 | for (ThirdCircle circle : holder.getThirdCircles()) { 202 | int distanceTX = (int) Math.abs(circle.getCurCX() - downX); 203 | int distanceTY = (int) Math.abs(circle.getCurCY() - downY); 204 | int distanceTZ = (int) Math.sqrt(Math.pow(distanceTX, 2) + Math.pow(distanceTY, 2)); 205 | if (distanceTZ <= circle.getRadius()) { 206 | circle.circleClick(!circle.isSelected()); 207 | } 208 | } 209 | } 210 | } 211 | } 212 | } 213 | break; 214 | default: 215 | break; 216 | } 217 | } 218 | 219 | private boolean isClick(long moveTime) { 220 | return moveTime > ViewConfiguration.getTapTimeout() || (moveX > 20 || moveY > 20); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/floatingdrawer/ThirdCircle.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.floatingdrawer; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.DashPathEffect; 8 | import android.graphics.Paint; 9 | import android.graphics.Rect; 10 | import android.view.animation.BounceInterpolator; 11 | 12 | /** 13 | * 浮动的圆形球我们可以称之为行星,ThirdCircle我们可以称之为卫星圆 14 | * 处理卫星圆球的浮动效果 15 | * @author sheng 16 | */ 17 | public class ThirdCircle { 18 | private float cx, cy, dx, dy, radius; 19 | private int color; 20 | private float percentSpeed; 21 | private String name; 22 | private float rateT; 23 | private float curCX, curCY; 24 | private boolean selected = false; 25 | private int selectedColor = Color.BLUE; 26 | private int realAngle; 27 | private float xieLine; 28 | private boolean isSweepAnim = false; 29 | 30 | private ThirdCircle thirdCircle; 31 | 32 | /** 33 | * 是否正在动画 34 | */ 35 | private boolean isAnim = false; 36 | 37 | public ThirdCircle(int color, String name, float rate, int selectedColor) { 38 | this.color = color; 39 | this.name = name; 40 | this.rateT = rate; 41 | this.selectedColor = selectedColor; 42 | thirdCircle = this; 43 | } 44 | 45 | public float getCx() { 46 | return cx; 47 | } 48 | 49 | public void setCx(float cx) { 50 | this.cx = cx; 51 | } 52 | 53 | public float getCy() { 54 | return cy; 55 | } 56 | 57 | public void setCy(float cy) { 58 | this.cy = cy; 59 | } 60 | 61 | public float getDx() { 62 | return dx; 63 | } 64 | 65 | public void setDx(float dx) { 66 | this.dx = dx; 67 | } 68 | 69 | public float getDy() { 70 | return dy; 71 | } 72 | 73 | public void setDy(float dy) { 74 | this.dy = dy; 75 | } 76 | 77 | public float getRadius() { 78 | return radius; 79 | } 80 | 81 | public void setRadius(float radius) { 82 | this.radius = radius; 83 | } 84 | 85 | public int getColor() { 86 | return selected ? selectedColor : color; 87 | } 88 | 89 | public void setColor(int color) { 90 | this.color = color; 91 | } 92 | 93 | public float getPercentSpeed() { 94 | return percentSpeed; 95 | } 96 | 97 | public void setPercentSpeed(float percentSpeed) { 98 | this.percentSpeed = percentSpeed; 99 | } 100 | 101 | public String getName() { 102 | return name; 103 | } 104 | 105 | public void setName(String name) { 106 | this.name = name; 107 | } 108 | 109 | public float getRate() { 110 | return rateT; 111 | } 112 | 113 | public void setRate(float rate) { 114 | this.rateT = rate; 115 | } 116 | 117 | public float getCurCX() { 118 | return curCX; 119 | } 120 | 121 | public void setCurCX(float curCX) { 122 | this.curCX = curCX; 123 | } 124 | 125 | public float getCurCY() { 126 | return curCY; 127 | } 128 | 129 | public void setCurCY(float curCY) { 130 | this.curCY = curCY; 131 | } 132 | 133 | public boolean isSelected() { 134 | return selected; 135 | } 136 | 137 | public int getRealAngle() { 138 | return realAngle; 139 | } 140 | 141 | public void setRealAngle(int realAngle) { 142 | this.realAngle = realAngle; 143 | } 144 | 145 | public float getXieLine() { 146 | return xieLine; 147 | } 148 | 149 | public void setXieLine(float xieLine) { 150 | this.xieLine = xieLine; 151 | } 152 | 153 | public boolean isSweepAnim() { 154 | return isSweepAnim; 155 | } 156 | 157 | public void setSweepAnim(boolean sweepAnim) { 158 | isSweepAnim = sweepAnim; 159 | } 160 | 161 | public void draw(Paint paint, Canvas canvas, Rect thirdRect) { 162 | paint.setColor(this.selectedColor); 163 | paint.setStyle(Paint.Style.STROKE); 164 | paint.setPathEffect(new DashPathEffect(new float[]{2, 2}, 0)); 165 | paint.setStrokeWidth(1); 166 | canvas.drawCircle(this.getCurCX(), this.getCurCY(), this.getRadius(), paint); 167 | paint.setStyle(Paint.Style.FILL); 168 | paint.setPathEffect(null); 169 | paint.setColor(isSelected()?selectedColor:color); 170 | canvas.drawCircle(this.getCurCX(), this.getCurCY(), this.getRadius() - 1, paint); 171 | 172 | paint.setTextSize(40 * getRate()); 173 | paint.getTextBounds(this.getName(), 0, this.getName().length(), thirdRect); 174 | paint.setColor(isSelected()?Color.WHITE:selectedColor); 175 | canvas.drawText(this.getName(), this.getCurCX() - thirdRect.width() / 2, this.getCurCY() + thirdRect.height() / 2, paint); 176 | } 177 | 178 | public void circleClick(boolean selected) { 179 | this.selected = selected; 180 | animClickThird(); 181 | } 182 | 183 | public void animClickThird(){ 184 | final float rad = thirdCircle.getRadius(); 185 | ValueAnimator animator = ValueAnimator.ofFloat(0.7f,1f); 186 | animator.setDuration(200); 187 | animator.setInterpolator(new BounceInterpolator()); 188 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 189 | @Override 190 | public void onAnimationUpdate(ValueAnimator animation) { 191 | float radius = (float) animation.getAnimatedValue()*rad; 192 | thirdCircle.setRadius(radius); 193 | } 194 | }); 195 | animator.addListener(new Animator.AnimatorListener() { 196 | @Override 197 | public void onAnimationStart(Animator animation) { 198 | isAnim = true; 199 | } 200 | 201 | @Override 202 | public void onAnimationEnd(Animator animation) { 203 | isAnim = false; 204 | } 205 | 206 | @Override 207 | public void onAnimationCancel(Animator animation) { 208 | 209 | } 210 | 211 | @Override 212 | public void onAnimationRepeat(Animator animation) { 213 | 214 | } 215 | }); 216 | animator.start(); 217 | } 218 | 219 | public boolean isAnim() { 220 | return isAnim; 221 | } 222 | } -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/BaseSplashDrawer.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * @author sheng 8 | */ 9 | 10 | public abstract class BaseSplashDrawer { 11 | public static final float START_RADIUS = 20; 12 | 13 | /** 14 | * 绘制 15 | * @param canvas 16 | * @param paint 17 | */ 18 | public abstract void draw(Canvas canvas, Paint paint); 19 | 20 | /** 21 | * 设置中心点 22 | * @param x 23 | * @param y 24 | */ 25 | public abstract void setCenterXY(int x, int y); 26 | } 27 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/IOnAnimEndListener.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 2 | 3 | /** 4 | * @author sheng 5 | */ 6 | 7 | public interface IOnAnimEndListener { 8 | /** 9 | * 动画结束 10 | */ 11 | void onAnimEnd(); 12 | } 13 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/RotationDrawer.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.util.DisplayMetrics; 10 | import android.view.View; 11 | 12 | /** 13 | * 绘制小圆的旋转动画 14 | * @author sheng 15 | */ 16 | public class RotationDrawer extends BaseSplashDrawer { 17 | private int[] mCircleColors; 18 | private ValueAnimator mAnimator; 19 | 20 | private float mCurrentRotationAngle; 21 | 22 | private View view; 23 | 24 | private IOnAnimEndListener listener; 25 | 26 | public RotationDrawer(final View view, final IOnAnimEndListener listener) { 27 | this.view = view; 28 | this.listener = listener; 29 | mCircleColors = new int[]{Color.parseColor("#FF59DABC"),Color.parseColor("#FF8BEAB4"),Color.parseColor("#FFFACD74") 30 | ,Color.parseColor("#FFFFB0B0"),Color.parseColor("#FF6BDEF5"),Color.parseColor("#FFCA61E4")}; 31 | } 32 | 33 | public void startAnim(){ 34 | // 属性动画 35 | mAnimator = ValueAnimator.ofFloat(0, (float) Math.PI * 10); 36 | mAnimator.setDuration(2000); 37 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 38 | @Override 39 | public void onAnimationUpdate(ValueAnimator animation) { 40 | // 不断获取值 当前大圆旋转的角度 41 | mCurrentRotationAngle = (float) animation.getAnimatedValue(); 42 | // 提醒View重新绘制 43 | view.invalidate(); 44 | } 45 | }); 46 | mAnimator.addListener(new Animator.AnimatorListener() { 47 | @Override 48 | public void onAnimationStart(Animator animation) { 49 | 50 | } 51 | 52 | @Override 53 | public void onAnimationEnd(Animator animation) { 54 | listener.onAnimEnd(); 55 | } 56 | 57 | @Override 58 | public void onAnimationCancel(Animator animation) { 59 | 60 | } 61 | 62 | @Override 63 | public void onAnimationRepeat(Animator animation) { 64 | 65 | } 66 | }); 67 | mAnimator.setInterpolator(new SlowInSlowOutInterpolator()); 68 | // 开始计算 69 | mAnimator.start(); 70 | } 71 | 72 | @Override 73 | public void draw(Canvas canvas, Paint paint) { 74 | // 绘制六个小圆 坐标 75 | float preAngle = (float) (2 * Math.PI / mCircleColors.length); 76 | for (int i = 0; i < mCircleColors.length; i++) { 77 | paint.setColor(mCircleColors[i]); 78 | // 初始角度 + 当前旋转的角度 79 | double angle = i * preAngle + mCurrentRotationAngle; 80 | float cx = (float) (getMetricsWidth(view.getContext())/2 + START_RADIUS*3 * Math.cos(angle)); 81 | float cy = (float) (getMetricsHeight(view.getContext())/2 + START_RADIUS*3 * Math.sin(angle)); 82 | canvas.drawCircle(cx, cy, START_RADIUS, paint); 83 | } 84 | } 85 | 86 | @Override 87 | public void setCenterXY(int x, int y) { 88 | 89 | } 90 | 91 | public void cancelAnimator() { 92 | mAnimator.cancel(); 93 | mAnimator = null; 94 | } 95 | 96 | public static int getMetricsWidth(Context context) 97 | { 98 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 99 | return dm.widthPixels; 100 | } 101 | public static int getMetricsHeight(Context context) 102 | { 103 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 104 | return dm.heightPixels; 105 | } 106 | } -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/RotationSweepView.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 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.PorterDuff; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | 11 | /** 12 | * @author sheng 13 | */ 14 | 15 | public class RotationSweepView extends View { 16 | private Paint mPaint; 17 | private RotationDrawer rotationDrawer; 18 | private SweepDrawer sweepDrawer; 19 | private TransitionDrawer transitionDrawer; 20 | 21 | private IOnAnimEndListener IOnAnimEndListener; 22 | 23 | private boolean isStartSweep = false; 24 | private boolean isStartTrans = false; 25 | 26 | public void setIOnAnimEndListener(IOnAnimEndListener IOnAnimEndListener) { 27 | this.IOnAnimEndListener = IOnAnimEndListener; 28 | } 29 | 30 | public RotationSweepView(Context context) { 31 | super(context); 32 | init(); 33 | } 34 | 35 | public RotationSweepView(Context context,AttributeSet attrs) { 36 | super(context, attrs); 37 | init(); 38 | } 39 | 40 | public RotationSweepView(Context context,AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | init(); 43 | } 44 | 45 | private void init(){ 46 | mPaint = new Paint(); 47 | mPaint.setAntiAlias(true); 48 | rotationDrawer = new RotationDrawer(this, new IOnAnimEndListener() { 49 | @Override 50 | public void onAnimEnd() { 51 | sweepDrawer.startAnim(); 52 | isStartSweep = true; 53 | } 54 | }); 55 | 56 | sweepDrawer = new SweepDrawer(this, new IOnAnimEndListener() { 57 | @Override 58 | public void onAnimEnd() { 59 | isStartTrans = true; 60 | transitionDrawer.setCircles(sweepDrawer.getCircles()); 61 | transitionDrawer.startAnim(); 62 | } 63 | }); 64 | transitionDrawer = new TransitionDrawer(this, new IOnAnimEndListener() { 65 | @Override 66 | public void onAnimEnd() { 67 | IOnAnimEndListener.onAnimEnd(); 68 | } 69 | }); 70 | } 71 | 72 | @Override 73 | protected void onDraw(Canvas canvas) { 74 | rotationDrawer.draw(canvas,mPaint); 75 | if (isStartSweep) { 76 | canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR); 77 | canvas.drawColor(Color.WHITE); 78 | sweepDrawer.draw(canvas,mPaint); 79 | } 80 | if (isStartTrans) { 81 | transitionDrawer.draw(canvas,mPaint); 82 | } 83 | } 84 | 85 | public void setDistanceXYR(int i,float x,float y,float r){ 86 | sweepDrawer.setDistanceXYR(i,x,y,r); 87 | } 88 | 89 | public void setCenterXY(int x,int y){ 90 | rotationDrawer.setCenterXY(x,y); 91 | sweepDrawer.setCenterXY(x,y); 92 | transitionDrawer.setCenterXY(x,y); 93 | rotationDrawer.startAnim(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/SlowInSlowOutInterpolator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 18 | 19 | 20 | import android.view.animation.Interpolator; 21 | 22 | /** 23 | * 修改VALUES值 达到慢进慢出的动画效果 24 | * @author sheng 25 | */ 26 | public class SlowInSlowOutInterpolator implements Interpolator { 27 | 28 | /** 29 | * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of 30 | * 201 points. 31 | */ 32 | private static final float[] VALUES = new float[] { 33 | 0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0008f, 0.0013f, 0.0018f, 34 | 0.0024f, 0.0032f, 0.0040f, 0.0049f, 0.0059f, 0.0069f, 0.0081f, 35 | 0.0093f, 0.0106f, 0.0120f, 0.0135f, 0.0151f, 0.0167f, 0.0184f, 36 | 0.0201f, 0.0220f, 0.0239f, 0.0259f, 0.0279f, 0.0300f, 0.0322f, 37 | 0.0345f, 0.0368f, 0.0391f, 0.0416f, 0.0441f, 0.0466f, 0.0492f, 38 | 0.0519f, 0.0547f, 0.0574f, 0.0603f, 0.0632f, 0.0662f, 0.0692f, 39 | 0.0722f, 0.0754f, 0.0785f, 0.0817f, 0.0850f, 0.0884f, 0.0917f, 40 | 0.0952f, 0.0986f, 0.1021f, 0.1057f, 0.1093f, 0.1130f, 0.1167f, 41 | 0.1205f, 0.1243f, 0.1281f, 0.1320f, 0.1359f, 0.1399f, 0.1439f, 42 | 0.1480f, 0.1521f, 0.1562f, 0.1604f, 0.1647f, 0.1689f, 0.1732f, 43 | 0.1776f, 0.1820f, 0.1864f, 0.1909f, 0.1954f, 0.1999f, 0.2045f, 44 | 0.2091f, 0.2138f, 0.2184f, 0.2232f, 0.2279f, 0.2327f, 0.2376f, 45 | 0.2424f, 0.2473f, 0.2523f, 0.2572f, 0.2622f, 0.2673f, 0.2723f, 46 | 0.2774f, 0.2826f, 0.2877f, 0.2929f, 0.2982f, 0.3034f, 0.3087f, 47 | 0.3141f, 0.3194f, 0.3248f, 0.3302f, 0.3357f, 0.3412f, 0.3467f, 48 | 0.3522f, 0.3578f, 0.3634f, 0.3690f, 0.3747f, 0.3804f, 0.3861f, 49 | 0.3918f, 0.3976f, 0.4034f, 0.4092f, 0.4151f, 0.4210f, 0.4269f, 50 | 0.4329f, 0.4388f, 0.4448f, 0.4508f, 0.4569f, 0.4630f, 0.4691f, 51 | 0.4752f, 0.4814f, 0.4876f, 0.4938f, 0.5000f, 0.5063f, 0.5126f, 52 | 0.5189f, 0.5252f, 0.5316f, 0.5380f, 0.5444f, 0.5508f, 0.5573f, 53 | 0.5638f, 0.5703f, 0.5768f, 0.5834f, 0.5900f, 0.5966f, 0.6033f, 54 | 0.6099f, 0.6166f, 0.6233f, 0.6257f, 0.6322f, 0.6387f, 0.6450f, 0.6512f, 0.6574f, 55 | 0.6635f, 0.6695f, 0.6754f, 0.6812f, 0.6870f, 0.6927f, 0.6983f, 56 | 0.7038f, 0.7093f, 0.7147f, 0.7200f, 0.7252f, 0.7304f, 0.7355f, 57 | 0.7406f, 0.7455f, 0.7504f, 0.7553f, 0.7600f, 0.7647f, 0.7694f, 58 | 0.7740f, 0.7785f, 0.7829f, 0.7873f, 0.7917f, 0.7959f, 0.8002f, 59 | 0.8043f, 0.8084f, 0.8125f, 0.8165f, 0.8204f, 0.8243f, 0.8281f, 60 | 0.8319f, 0.8356f, 0.8392f, 0.8429f, 0.8464f, 0.8499f, 0.8534f, 61 | 0.8568f, 0.8601f, 0.8634f, 0.8667f, 0.8699f, 0.8731f, 0.8762f, 62 | 0.8792f, 0.8823f, 0.8852f, 0.8882f, 0.8910f, 0.8939f, 0.8967f, 63 | 0.8994f, 0.9021f, 0.9048f, 0.9074f, 0.9100f, 0.9125f, 0.9150f, 64 | 0.9174f, 0.9198f, 0.9222f, 0.9245f, 0.9268f, 0.9290f, 0.9312f, 65 | 0.9334f, 0.9355f, 0.9376f, 0.9396f, 0.9416f, 0.9436f, 0.9455f, 66 | 0.9474f, 0.9492f, 0.9510f, 0.9528f, 0.9545f, 0.9562f, 0.9579f, 67 | 0.9595f, 0.9611f, 0.9627f, 0.9642f, 0.9657f, 0.9672f, 0.9686f, 68 | 0.9700f, 0.9713f, 0.9726f, 0.9739f, 0.9752f, 0.9764f, 0.9776f, 69 | 0.9787f, 0.9798f, 0.9809f, 0.9820f, 0.9830f, 0.9840f, 0.9849f, 70 | 0.9859f, 0.9868f, 0.9876f, 0.9885f, 0.9893f, 0.9900f, 0.9908f, 71 | 0.9915f, 0.9922f, 0.9928f, 0.9934f, 0.9940f, 0.9946f, 0.9951f, 72 | 0.9956f, 0.9961f, 0.9966f, 0.9970f, 0.9974f, 0.9977f, 0.9981f, 73 | 0.9984f, 0.9987f, 0.9989f, 0.9992f, 0.9994f, 0.9995f, 0.9997f, 74 | 0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f 75 | }; 76 | 77 | private final float[] mValues; 78 | private final float mStepSize; 79 | 80 | public SlowInSlowOutInterpolator() { 81 | mValues = VALUES; 82 | mStepSize = 1f / (mValues.length - 1); 83 | } 84 | 85 | @Override 86 | public float getInterpolation(float input) { 87 | if (input >= 1.0f) { 88 | return 1.0f; 89 | } 90 | if (input <= 0f) { 91 | return 0f; 92 | } 93 | 94 | // Calculate index - We use min with length - 2 to avoid IndexOutOfBoundsException when 95 | // we lerp (linearly interpolate) in the return statement 96 | int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2); 97 | 98 | // Calculate values to account for small offsets as the lookup table has discrete values 99 | float quantized = position * mStepSize; 100 | float diff = input - quantized; 101 | float weight = diff / mStepSize; 102 | 103 | // Linearly interpolate between the table values 104 | return mValues[position] + weight * (mValues[position + 1] - mValues[position]); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/SweepDrawer.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.util.DisplayMetrics; 10 | import android.view.View; 11 | import android.view.animation.AccelerateDecelerateInterpolator; 12 | 13 | /** 14 | * 绘制小圆的扩散动画 15 | * @author sheng 16 | */ 17 | public class SweepDrawer extends BaseSplashDrawer { 18 | private Point[] mCircles; 19 | private ValueAnimator mAnimator; 20 | 21 | private float mCurrentRate; 22 | 23 | private View view; 24 | 25 | private IOnAnimEndListener listener; 26 | 27 | private final float START_RADIUS = 8; 28 | 29 | private int centerX,centerY; 30 | 31 | public SweepDrawer(final View view, final IOnAnimEndListener listener) { 32 | this.view = view; 33 | this.listener = listener; 34 | 35 | mCircles = new Point[]{new Point(getX(0),getY(0),START_RADIUS,Color.parseColor("#FF59DABC"),Color.parseColor("#FFE2FFF8")),new Point(getX(1),getY(1),START_RADIUS,Color.parseColor("#FF8BEAB4"),Color.parseColor("#FFECFCF3")) 36 | ,new Point(getX(2),getY(2),START_RADIUS,Color.parseColor("#FFFACD74"),Color.parseColor("#FFFAF6EE")),new Point(getX(3),getY(3),START_RADIUS,Color.parseColor("#FFFFB0B0"),Color.parseColor("#FFFDF8F8")) 37 | ,new Point(getX(4),getY(4),START_RADIUS,Color.parseColor("#FF6BDEF5"),Color.parseColor("#FFE7F7FA")),new Point(getX(5),getY(5),START_RADIUS,Color.parseColor("#FFCA61E4"),Color.parseColor("#FFF3E2F7"))}; 38 | } 39 | 40 | private float getX(int i){ 41 | double angle = i * (float) (2 * Math.PI / 6); 42 | float cx = (float) (getMetricsWidth(view.getContext())/2 + 32 * Math.cos(angle)); 43 | return cx; 44 | } 45 | private float getY(int i){ 46 | double angle = i * (float) (2 * Math.PI / 6); 47 | float cy = (float) (getMetricsHeight(view.getContext())/2 + 32 * Math.sin(angle)); 48 | return cy; 49 | } 50 | 51 | public void setDistanceXYR(int i,float x,float y,float r){ 52 | for (int j = 0; j < mCircles.length; j++) { 53 | Point p = mCircles[i]; 54 | p.setDistanceX(x-p.x); 55 | p.setDistanceY(y-p.y); 56 | p.setDesR(r); 57 | } 58 | } 59 | 60 | public void startAnim(){ 61 | mAnimator = ValueAnimator.ofFloat(0, 1f); 62 | mAnimator.setDuration(500); 63 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 64 | @Override 65 | public void onAnimationUpdate(ValueAnimator animation) { 66 | // 获取当前的距离百分比 67 | mCurrentRate = (float) animation.getAnimatedValue(); 68 | // 提醒View重新绘制 69 | view.invalidate(); 70 | } 71 | }); 72 | mAnimator.addListener(new Animator.AnimatorListener() { 73 | @Override 74 | public void onAnimationStart(Animator animation) { 75 | 76 | } 77 | 78 | @Override 79 | public void onAnimationEnd(Animator animation) { 80 | listener.onAnimEnd(); 81 | } 82 | 83 | @Override 84 | public void onAnimationCancel(Animator animation) { 85 | 86 | } 87 | 88 | @Override 89 | public void onAnimationRepeat(Animator animation) { 90 | 91 | } 92 | }); 93 | mAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 94 | // 开始计算 95 | mAnimator.start(); 96 | } 97 | 98 | @Override 99 | public void draw(Canvas canvas, Paint paint) { 100 | for (int i = 0; i < mCircles.length; i++) { 101 | Point p = mCircles[i]; 102 | paint.setColor(p.color); 103 | p.x = getX(i)+p.distanceX*mCurrentRate; 104 | p.y = getY(i)+p.distanceY*mCurrentRate; 105 | p.r = START_RADIUS+p.disR*mCurrentRate; 106 | canvas.drawCircle(p.x, p.y, p.r, paint); 107 | } 108 | } 109 | 110 | @Override 111 | public void setCenterXY(int x, int y) { 112 | centerX = x; 113 | centerY = y; 114 | } 115 | 116 | public void cancelAnimator() { 117 | mAnimator.cancel(); 118 | mAnimator = null; 119 | } 120 | 121 | public static int getMetricsWidth(Context context) 122 | { 123 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 124 | return dm.widthPixels; 125 | } 126 | public static int getMetricsHeight(Context context) 127 | { 128 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 129 | return dm.heightPixels; 130 | } 131 | 132 | public class Point{ 133 | public float x; 134 | public float y; 135 | public float r; 136 | public int color; 137 | public float distanceY; 138 | public float distanceX; 139 | public float desR; 140 | public float disR; 141 | public int startColor; 142 | public int toColor; 143 | 144 | public Point(float x, float y, float r,int color,int toColor) { 145 | this.x = x; 146 | this.y = y; 147 | this.r = r; 148 | this.color = this.startColor = color; 149 | this.toColor = toColor; 150 | } 151 | 152 | public void setX(float x) { 153 | this.x = x; 154 | } 155 | 156 | public void setY(float y) { 157 | this.y = y; 158 | } 159 | 160 | public void setR(float r) { 161 | this.r = r; 162 | } 163 | 164 | public void setColor(int color) { 165 | this.color = color; 166 | } 167 | 168 | public void setDistanceY(float distanceY) { 169 | this.distanceY = distanceY; 170 | } 171 | 172 | public void setDistanceX(float distanceX) { 173 | this.distanceX = distanceX; 174 | } 175 | 176 | public void setDesR(float desR) { 177 | this.desR = desR; 178 | disR = desR-START_RADIUS; 179 | } 180 | 181 | public void setToColor(int toColor) { 182 | this.toColor = toColor; 183 | } 184 | } 185 | 186 | public Point[] getCircles() { 187 | return mCircles; 188 | } 189 | } -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/java/com/sheng/preferencefloatingview/floating/circle/splashdrawer/TransitionDrawer.java: -------------------------------------------------------------------------------- 1 | package com.sheng.preferencefloatingview.floating.circle.splashdrawer; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ArgbEvaluator; 5 | import android.animation.ValueAnimator; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | import android.util.Log; 9 | import android.view.View; 10 | import android.view.animation.AccelerateDecelerateInterpolator; 11 | 12 | /** 13 | * 绘制实体圆过渡SurfaceView中的圆 14 | * @author sheng 15 | */ 16 | public class TransitionDrawer extends BaseSplashDrawer { 17 | private SweepDrawer.Point[] mCircles; 18 | private ValueAnimator mAnimator; 19 | private ArgbEvaluator evaluator; 20 | private View view; 21 | 22 | private IOnAnimEndListener listener; 23 | private float mCurrentRate; 24 | 25 | public TransitionDrawer(final View view, final IOnAnimEndListener listener) { 26 | this.view = view; 27 | this.listener = listener; 28 | evaluator = new ArgbEvaluator(); 29 | } 30 | 31 | public void startAnim(){ 32 | mAnimator = ValueAnimator.ofFloat(0.5f,1f); 33 | mAnimator.setDuration(400); 34 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 35 | @Override 36 | public void onAnimationUpdate(ValueAnimator animation) { 37 | // 获取当前的距离百分比 38 | mCurrentRate = (float) animation.getAnimatedValue(); 39 | Log.d("rate",mCurrentRate+""); 40 | // 提醒View重新绘制 41 | view.invalidate(); 42 | } 43 | }); 44 | mAnimator.addListener(new Animator.AnimatorListener() { 45 | @Override 46 | public void onAnimationStart(Animator animation) { 47 | 48 | } 49 | 50 | @Override 51 | public void onAnimationEnd(Animator animation) { 52 | listener.onAnimEnd(); 53 | } 54 | 55 | @Override 56 | public void onAnimationCancel(Animator animation) { 57 | 58 | } 59 | 60 | @Override 61 | public void onAnimationRepeat(Animator animation) { 62 | 63 | } 64 | }); 65 | mAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 66 | // 开始计算 67 | mAnimator.start(); 68 | } 69 | 70 | @Override 71 | public void draw(Canvas canvas, Paint paint) { 72 | for (int i = 0; i < mCircles.length; i++) { 73 | SweepDrawer.Point p = mCircles[i]; 74 | p.color = (int) evaluator.evaluate(mCurrentRate,p.startColor,p.toColor); 75 | paint.setColor(p.color); 76 | canvas.drawCircle(p.x, p.y, p.r, paint); 77 | } 78 | } 79 | 80 | @Override 81 | public void setCenterXY(int x, int y) { 82 | 83 | } 84 | 85 | public void setCircles(SweepDrawer.Point[] mCircles) { 86 | this.mCircles = mCircles; 87 | } 88 | } -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b07.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3l.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3m.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3n.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3o.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3p.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3q.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3q.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3r.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3s.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/drawable/b3t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/drawable/b3t.png -------------------------------------------------------------------------------- /FloatingSettingView/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 22 | 23 | 28 | 29 | 35 | 36 | 44 | 45 | 46 | 47 | 51 | 52 | 55 | 56 | 61 | 62 | 63 | 64 | 68 | 69 | 78 | 79 |