├── 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 |
89 |
90 |
91 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/FloatingSettingView/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/FloatingSettingView/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.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/FloatingSettingView/floating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/floating.png
--------------------------------------------------------------------------------
/FloatingSettingView/floating1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/floating1.jpeg
--------------------------------------------------------------------------------
/FloatingSettingView/floating2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/floating2.gif
--------------------------------------------------------------------------------
/FloatingSettingView/floating2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/floating2.jpeg
--------------------------------------------------------------------------------
/FloatingSettingView/floating3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/floating3.jpeg
--------------------------------------------------------------------------------
/FloatingSettingView/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 |
--------------------------------------------------------------------------------
/FloatingSettingView/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/FloatingSettingView/wangyi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/FloatingSettingView/ad4e27cd47d9965047fc65a766eadb6ea9644019/FloatingSettingView/wangyi.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## FloatingSettingView
2 | 仿网易新闻兴趣选择页面 如果觉得有点意思的话,给个star,谢谢!
3 | 效果如图(实际效果比gif流畅很多):
4 |
5 | ### 扫码体验
6 |
7 | 
8 |
9 | [demo地址](https://fir.im/7t15)
10 |
11 | #### 网易的实现
12 | 
13 |
14 | #### 本项目的实现
15 | 
16 |
17 | 
18 |
19 | 
20 |
21 | 
22 |
23 |
24 | 首先要说明的是,我的这个实现和网易的完全不一样
25 |
26 | 打开开发者选项中的显示布局边界,可以看到网易的是用的组合控件实现的,这种实现其实也不难,自定义组合控件view,排版好每个小模块的布局大小,添加自定义动画,
27 | 位移、缩放、颜色的改变等。
28 |
29 | 这种方式这里就不讨论了,主要说说我的实现,自定义SurfaceView,启用一个线程不断的去改变每个要绘制的对象的位置,然后重新绘制,这种实现的好处是你可以随心所欲的控制你想要绘制的图形,千变万化,但是缺点是,绘制起来相对比较麻烦,如若发现bug或其它问题请提交issue。
30 |
31 |
--------------------------------------------------------------------------------