├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── BeiSaierLearn.iml
├── README.md
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── xulinggang
│ │ └── beisaierlearn
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── xlg
│ │ ├── beisaierlearn
│ │ ├── Log.java
│ │ ├── MainActivity.java
│ │ └── SecondActivity.java
│ │ ├── util
│ │ └── Util.java
│ │ └── weidget
│ │ ├── BeiSaiErView.java
│ │ ├── BitmapMeshView2.java
│ │ ├── DrawDottedCurve.java
│ │ ├── HeartFlowLayout.java
│ │ └── PathMeasureView.java
│ └── res
│ ├── layout
│ ├── activity_main.xml
│ └── activity_second.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ ├── bt.jpg
│ ├── heart1.png
│ ├── heart2.png
│ ├── heart3.png
│ ├── heart4.png
│ ├── ic_launcher.png
│ └── image.jpg
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | BeiSaierLearn
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 1.8
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/BeiSaierLearn.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BeiSaierLearn
2 | 贝塞尔效果,模仿苹果神奇效果
3 |
4 | 主要用到知识点:自定义属性动画(TypeEvaluator 实现贝塞尔效果)、PathMeasure(计算Path长度)、drawBitmapMesh用法、一元3次方程求解。
5 |
6 | 先看下效果:
7 | 
8 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.1"
6 |
7 | defaultConfig {
8 | applicationId "com.xlg.beisaierlearn"
9 | minSdkVersion 15
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | debug{
16 | buildConfigField "boolean", "LOG_DEBUG", "true"
17 | }
18 |
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 | compile 'com.android.support:appcompat-v7:22.2.0'
29 | }
30 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/xulinggang/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/xulinggang/beisaierlearn/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.example.xulinggang.beisaierlearn;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/beisaierlearn/Log.java:
--------------------------------------------------------------------------------
1 | package com.xlg.beisaierlearn;
2 |
3 | /**
4 | * Created by xulinggang on 15/6/29.
5 | */
6 | public class Log {
7 |
8 | public static void i(String tag, String msg) {
9 | if (BuildConfig.LOG_DEBUG) {
10 | android.util.Log.i(tag,msg);
11 | }
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/beisaierlearn/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.xlg.beisaierlearn;
2 |
3 | import android.animation.ObjectAnimator;
4 | import android.app.Activity;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.view.Menu;
8 | import android.view.MenuItem;
9 | import android.view.View;
10 | import android.view.Window;
11 | import android.view.WindowManager;
12 | import android.view.animation.AnticipateOvershootInterpolator;
13 | import android.view.animation.LinearInterpolator;
14 | import android.widget.Button;
15 | import android.widget.TextView;
16 |
17 | import com.xlg.beisaierlearn.R;
18 | import com.xlg.weidget.BeiSaiErView;
19 | import com.xlg.weidget.HeartFlowLayout;
20 |
21 |
22 | public class MainActivity extends Activity {
23 | private BeiSaiErView mBeiSaiErView;
24 | private Button mButton;
25 | private TextView mHello;
26 | private HeartFlowLayout mHeartFlowLayout;
27 |
28 | private Button mButtonNext;
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | /*set it to be no title*/
34 | requestWindowFeature(Window.FEATURE_NO_TITLE);
35 | /*set it to be full screen*/
36 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
37 | WindowManager.LayoutParams.FLAG_FULLSCREEN);
38 |
39 |
40 | setContentView(R.layout.activity_main);
41 |
42 | mBeiSaiErView = (BeiSaiErView) findViewById(R.id.beisai_view);
43 | mButton = (Button) findViewById(R.id.btn_click);
44 | mButtonNext = (Button) findViewById(R.id.btn_next);
45 |
46 | mHello = (TextView) findViewById(R.id.hello_world);
47 |
48 | LinearInterpolator interpolator = new LinearInterpolator();
49 | final ObjectAnimator animator = ObjectAnimator.ofFloat(mHello,"translationY",0,1920);
50 | animator.setInterpolator(interpolator);
51 | animator.setDuration(3000);
52 |
53 | mHeartFlowLayout = (HeartFlowLayout) findViewById(R.id.heart_flow);
54 | mButton.setOnClickListener(new View.OnClickListener() {
55 | @Override
56 | public void onClick(View v) {
57 | mBeiSaiErView.startRun();
58 | animator.start();
59 | }
60 |
61 | });
62 |
63 | mButtonNext.setOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View v) {
66 | Intent intent = new Intent(MainActivity.this, SecondActivity.class);
67 | startActivity(intent);
68 | }
69 | });
70 |
71 |
72 | }
73 |
74 | @Override
75 | public boolean onCreateOptionsMenu(Menu menu) {
76 | // Inflate the menu; this adds items to the action bar if it is present.
77 | getMenuInflater().inflate(R.menu.menu_main, menu);
78 | return true;
79 | }
80 |
81 | @Override
82 | public boolean onOptionsItemSelected(MenuItem item) {
83 | // Handle action bar item clicks here. The action bar will
84 | // automatically handle clicks on the Home/Up button, so long
85 | // as you specify a parent activity in AndroidManifest.xml.
86 | int id = item.getItemId();
87 |
88 | //noinspection SimplifiableIfStatement
89 | if (id == R.id.action_settings) {
90 | return true;
91 | }
92 |
93 | return super.onOptionsItemSelected(item);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/beisaierlearn/SecondActivity.java:
--------------------------------------------------------------------------------
1 | package com.xlg.beisaierlearn;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.widget.RelativeLayout;
6 |
7 | import com.xlg.weidget.DrawDottedCurve;
8 |
9 | /**
10 | * Created by xulinggang on 15/6/26.
11 | */
12 | public class SecondActivity extends Activity {
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_second);
17 |
18 | DrawDottedCurve curve = new DrawDottedCurve(this,0,200,400,400);
19 | RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(-2,-2);
20 | curve.setLayoutParams(params);
21 | RelativeLayout root = (RelativeLayout) findViewById(R.id.root);
22 | root.addView(curve);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/util/Util.java:
--------------------------------------------------------------------------------
1 | package com.xlg.util;
2 |
3 |
4 | import com.xlg.beisaierlearn.Log;
5 |
6 | /**
7 | * Created by xulinggang on 15/6/29.
8 | */
9 | public class Util {
10 | private static final String TAG = "Util";
11 | public static double calculate3X(double a, double b, double c, double d) {
12 | double A = b*b - 3*a*c;
13 | double B = b*c - 9*a*d;
14 | double C = c*c - 3*b*d;
15 | double D = B * B - 4*A*C;
16 | Log.i(TAG, "a = " + a + ", b = " + b + ", c = " + c + ", d = " + d);
17 | Log.i(TAG,"A = "+A+", B = " + b + ", C = "+C +", D = "+D);
18 | // System.out.println("A ==> "+A);
19 | // System.out.println("B ==> "+B);
20 | // System.out.println("C ==> "+C);
21 | // System.out.println("D ==> "+D);
22 | double result = 0;
23 | if (A == 0 && B == 0) {
24 | //盛金公式1
25 | Log.i(TAG,"盛金公式1");
26 | result = -3*d/c;
27 | } else if (D > 0) {
28 | //盛金公式2
29 | Log.i(TAG,"盛金公式2");
30 | //先计算y1,y2
31 | double Y1 = A * b + 3 * a * ((-1*B + Math.sqrt(B * B - 4*A*C))/2);
32 | double Y2 = A * b + 3 * a * ((-1*B - Math.sqrt(B * B - 4*A*C))/2);
33 | Log.i(TAG, "Y1 ==> " + Y1);
34 | Log.i(TAG, "Y2 ==> " + Y2);
35 |
36 | double Y1F = 1d;
37 | double Y2F = 1d;
38 | if (Y1 < 0) {
39 | Y1F = -1d;
40 | }
41 | if (Y2 < 0) {
42 | Y2F = -1d;
43 | }
44 | double bot = 1d/3;
45 |
46 | result = (-b - (Y1F*Math.pow(Y1*Y1F,bot) + Y2F*Math.pow(Y2*Y2F,bot))) / (3*a);
47 | System.out.println("X ==> "+result);
48 | } else if (D == 0) {
49 | Log.i(TAG,"盛金公式3");
50 | if (A == 0) {
51 | Log.i(TAG,"A == 0");
52 | return result;
53 | }
54 | double K = B / A;
55 | double X1 = -b/a + K;
56 | double X2 = -K / 2;
57 | System.out.println("X1 ==>"+X1);
58 | System.out.println("X2 ==>"+X2);
59 | double max = Math.max(X1,X2);
60 | if (max >= 0) {
61 | result = max;
62 | } else {
63 | Log.i(TAG, "Max < 0");
64 | }
65 | } else if (D < 0) {
66 | Log.i(TAG,"盛金公式4");
67 | if (A <= 0) {
68 | Log.i(TAG, "A < 0");
69 | return result;
70 | }
71 | double T = (2*A*b - 3*a*B) / (2*Math.sqrt(A*A*A));
72 | double Z = Math.toDegrees(Math.acos(T));
73 | Log.i(TAG, "Z ==>"+Z);
74 | double cosZ_3 = Math.cos(Math.toRadians(Z / 3));
75 | double sinZ_3 = Math.sin(Math.toRadians(Z / 3));
76 | double X1 = (-b - 2*Math.sqrt(A)*cosZ_3) / (3*a);
77 | double X2 = (-b + Math.sqrt(A)*(cosZ_3 + Math.sqrt(3)*sinZ_3)) / (3*a);
78 | double X3 = (-b + Math.sqrt(A)*(cosZ_3 - Math.sqrt(3)*sinZ_3)) / (3*a);
79 | Log.i(TAG, "X1 ==>" + X1);
80 | Log.i(TAG, "X2 ==>" + X2);
81 | Log.i(TAG, "X3 ==>" + X3);
82 | //因为t是0-1的,所以
83 | if (X1 >= 0 && X1 <= 1) {
84 | result = X1;
85 | } else if (X2 >= 0 && X2 <= 1) {
86 | result = X2;
87 | } else if (X3 >= 0 && X3 <= 1) {
88 | result = X3;
89 | }
90 | }
91 |
92 | return result;
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/weidget/BeiSaiErView.java:
--------------------------------------------------------------------------------
1 | package com.xlg.weidget;
2 |
3 | import android.animation.TypeEvaluator;
4 | import android.animation.ValueAnimator;
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Paint;
11 | import android.graphics.Path;
12 | import android.graphics.PathMeasure;
13 | import android.graphics.PointF;
14 | import android.util.AttributeSet;
15 | import android.util.Log;
16 | import android.view.View;
17 | import android.view.animation.AccelerateInterpolator;
18 | import android.view.animation.LinearInterpolator;
19 |
20 | import com.xlg.beisaierlearn.R;
21 | import com.xlg.util.Util;
22 |
23 | /**
24 | * Created by xulinggang on 15/6/16.
25 | * 主要是模仿苹果的神奇效果,界面往底部缩
26 | * 感觉2种效果都差了点,只能当做是学习贝塞尔曲线绘制,以及drawBitmapMesh 等api得学习了
27 | */
28 | public class BeiSaiErView extends View {
29 | private static final String TAG = BeiSaiErView.class.getSimpleName();
30 | private Paint mPaint;
31 | private Context mContext;
32 | private Path mPathLeft;
33 | private Path mPathRight;
34 |
35 | private PointF mLeftStart = new PointF();
36 | private PointF mLeftControl1 = new PointF();
37 | private PointF mLeftControl2 = new PointF();
38 | private PointF mLeftEnd = new PointF();
39 |
40 | private PointF mRightStart = new PointF();
41 | private PointF mRightControl1 = new PointF();
42 | private PointF mRightControl2 = new PointF();
43 | private PointF mRightEnd = new PointF();
44 |
45 | private int mViewWide;
46 | private int mViewHeight;
47 | private int mMiddle;
48 |
49 | private Point mPointF;
50 | private Paint mPaintPoint;
51 |
52 | private ValueAnimator mAnimator;
53 |
54 | private Bitmap mBitmap;
55 |
56 | /**显示效果,可选*/
57 | private Effect mEffect = Effect.EFFECT_ONE;
58 |
59 | public enum Effect {
60 | EFFECT_ONE, EFFECT_TWO
61 | }
62 |
63 | public BeiSaiErView(Context context) {
64 | super(context);
65 | mContext = context;
66 | init();
67 |
68 | }
69 |
70 | public BeiSaiErView(Context context, AttributeSet attrs) {
71 | super(context, attrs);
72 | init();
73 | }
74 |
75 | //
76 | private static final int WIDTH = 15, HEIGHT = 22;// 分割数
77 | private static final int COUNT = (WIDTH + 1) * (HEIGHT + 1);// 交点数
78 | private float[] matrixOriganal = new float[COUNT * 2];// 基准点坐标数组
79 | private Paint origPaint, movePaint, pointPaint, graphicPaint;// 基准点、变换点和线段的绘制Paint
80 |
81 | private void init() {
82 | mViewWide = getResources().getDisplayMetrics().widthPixels;
83 | mViewHeight = getResources().getDisplayMetrics().heightPixels;
84 | mMiddle = mViewWide / 2;
85 |
86 | mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.bt);
87 | // 初始化坐标数组
88 | int index = 0;
89 | for (int y = 0; y <= HEIGHT; y++) {
90 | float fy = mViewHeight * y / HEIGHT;
91 |
92 | for (int x = 0; x <= WIDTH; x++) {
93 | float fx = mViewWide * x / WIDTH;
94 | setXY(matrixOriganal, index, fx, fy);
95 | index += 1;
96 | }
97 | }
98 | // 实例画笔并设置颜色
99 | origPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
100 | origPaint.setColor(0x660000FF);
101 |
102 | graphicPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
103 | graphicPaint.setStyle(Paint.Style.STROKE);
104 | graphicPaint.setColor(0x660000FF);
105 | graphicPaint.setStrokeWidth(8);
106 |
107 | pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
108 | pointPaint.setStyle(Paint.Style.STROKE);
109 | pointPaint.setColor(Color.YELLOW);
110 | pointPaint.setStrokeWidth(6);
111 |
112 |
113 | mPaint = new Paint();
114 | mPaint.setAntiAlias(true);
115 | mPaint.setStyle(Paint.Style.STROKE);
116 | mPaint.setColor(Color.RED);
117 | mPaint.setStrokeWidth(4);
118 |
119 | mPaintPoint = new Paint();
120 | mPaintPoint.setAntiAlias(true);
121 | mPaintPoint.setStyle(Paint.Style.STROKE);
122 | mPaintPoint.setColor(Color.YELLOW);
123 | mPaintPoint.setStrokeWidth(8);
124 |
125 | mLeftStart.x = 0;
126 | mLeftStart.y = 0;
127 | mLeftControl1.x = 100;
128 | mLeftControl1.y = 700;
129 | mLeftControl2.x = 300;
130 | mLeftControl2.y = 100;
131 | mLeftEnd.x = mMiddle - 50;
132 | mLeftEnd.y = mViewHeight;
133 | // mLeftEnd.x = 281.1968f;
134 | // mLeftEnd.y = 736.6974f ;
135 |
136 | mRightStart.x = mViewWide;
137 | mRightStart.y = 0;
138 | mRightControl1.x = mViewWide - 100;
139 | mRightControl1.y = 700;
140 | mRightControl2.x = mViewWide - 300;
141 | mRightControl2.y = 100;
142 | mRightEnd.x = mMiddle + 50;
143 | mRightEnd.y = mViewHeight;
144 |
145 | mPathLeft = new Path();
146 | mPathLeft.moveTo(mLeftStart.x, mLeftStart.y);
147 | mPathLeft.cubicTo(mLeftControl1.x, mLeftControl1.y, mLeftControl2.x, mLeftControl2.y, mLeftEnd.x, mLeftEnd.y);
148 |
149 | mPathRight = new Path();
150 | mPathRight.moveTo(mRightStart.x, mRightStart.y);
151 | mPathRight.cubicTo(mRightControl1.x, mRightControl1.y, mRightControl2.x, mRightControl2.y, mRightEnd.x, mRightEnd.y);
152 |
153 | initAnimator();
154 | initInPathPoints();
155 | }
156 |
157 | /**
158 | * 绘制参考元素
159 | *
160 | * @param canvas 画布
161 | */
162 | private void drawGuide(Canvas canvas) {
163 | for (int i = 0; i < COUNT * 2; i += 2) {
164 | float x = matrixOriganal[i + 0];
165 | float y = matrixOriganal[i + 1];
166 | canvas.drawCircle(x, y, 4, origPaint);
167 | }
168 | }
169 |
170 | /**
171 | * 设置坐标数组
172 | *
173 | * @param array 坐标数组
174 | * @param index 标识值
175 | * @param x x坐标
176 | * @param y y坐标
177 | */
178 | private void setXY(float[] array, int index, float x, float y) {
179 | array[index * 2 + 0] = x;
180 | array[index * 2 + 1] = y;
181 | }
182 |
183 |
184 | /***
185 | * 初始化动画,曲线效果
186 | */
187 | private void initAnimator() {
188 | //两个控制点
189 | // PointF pointF1 = new PointF(mLeftControl1.x);
190 | // PointF pointF2 = new PointF(300, 100);
191 | BeiSaiErEvaluator beiSaiErEvaluator = new BeiSaiErEvaluator(mLeftControl1, mLeftControl2);
192 |
193 | //起点与终点
194 | // PointF start = new PointF(0, 0);
195 | // PointF end = new PointF(mMiddle, mViewHeight);
196 | mAnimator = ValueAnimator.ofObject(beiSaiErEvaluator, mLeftStart, mLeftEnd);
197 | mAnimator.setDuration(3000);
198 | mAnimator.setInterpolator(new LinearInterpolator());
199 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
200 | @Override
201 | public void onAnimationUpdate(ValueAnimator animation) {
202 | mPointF = (Point) animation.getAnimatedValue();
203 | Log.i(TAG, "mPointF x => " + mPointF.x + ", y => " + mPointF.y + ", t = >" + mPointF.t);
204 | calculateMesh(mPointF);
205 | invalidate();
206 | }
207 | });
208 | }
209 |
210 | //计算左右边每段上的点
211 | Point[] posLeft;
212 | Point[] posRight;
213 | float leftLength;
214 | float rightLength;
215 |
216 | private Path cutPath = new Path();
217 | PathMeasure leftPathMeasure;
218 | PathMeasure rightPathMeasure;
219 |
220 | /**
221 | * 将mesh数组左边界,定位在path上
222 | */
223 | private void initInPathPoints() {
224 | leftPathMeasure = new PathMeasure(mPathLeft, false);
225 | rightPathMeasure = new PathMeasure(mPathRight, false);
226 | leftLength = leftPathMeasure.getLength();
227 | float leftDivider = leftLength / HEIGHT; //每段长度
228 |
229 |
230 | posLeft = new Point[HEIGHT + 1];
231 | posRight = new Point[HEIGHT + 1];
232 |
233 | float[] f = new float[2];
234 | for (int i = 0; i < posLeft.length; i++) {
235 | Point pointF = new Point();
236 | leftPathMeasure.getPosTan(leftDivider * i, f, null);
237 | pointF.x = f[0];
238 | pointF.y = f[1];
239 |
240 | CubeParam cubeParam = new CubeParam();
241 | cubeParam.calculateParams(mLeftStart, mLeftControl1, mLeftControl2, mLeftEnd, pointF);
242 | pointF.t = (float) Util.calculate3X(cubeParam.a, cubeParam.b, cubeParam.c, cubeParam.d);
243 | posLeft[i] = pointF;
244 |
245 | }
246 |
247 | rightLength = leftPathMeasure.getLength();
248 | float rightDivider = rightLength / HEIGHT; //每段长度
249 | for (int i = 0; i < posRight.length; i++) {
250 | Point pointFR = new Point();
251 | rightPathMeasure.getPosTan(rightDivider * i, f, null);
252 | pointFR.x = f[0];
253 | pointFR.y = f[1];
254 | CubeParam cubeParamR = new CubeParam();
255 | cubeParamR.calculateParams(mRightStart, mRightControl1, mRightControl2, mRightEnd, pointFR);
256 | pointFR.t = (float) Util.calculate3X(cubeParamR.a, cubeParamR.b, cubeParamR.c, cubeParamR.d);
257 | posRight[i] = pointFR;
258 | }
259 | }
260 |
261 | /**
262 | * 开始动画
263 | */
264 | public void startRun() {
265 | if (movePath != null) {
266 | movePath.reset();
267 | }
268 | mAnimator.start();
269 | }
270 |
271 |
272 | Path movePath = new Path();
273 | PathMeasure pathMeasure = new PathMeasure();
274 |
275 | /**
276 | * 重新计算mesh
277 | */
278 | public void calculateMesh(Point movePoint) {
279 | float leftDivider = 0;
280 | float moveLength = 0;
281 | if (mEffect == Effect.EFFECT_TWO) {
282 | float height = movePoint.y;
283 | //TODO 其实这里应用的是贝塞尔上两点间的曲线长度,而暂时不知道如何求(微积分?),所以用height来替用下
284 | leftPathMeasure.getSegment(0, height, movePath, false);
285 | pathMeasure.setPath(movePath, false);
286 | moveLength = pathMeasure.getLength();
287 | leftDivider = (leftLength - moveLength) / HEIGHT; //每段长度
288 | } else {
289 | leftDivider = leftLength / HEIGHT; //每段长度
290 | }
291 |
292 |
293 | float[] f = new float[2];
294 | for (int i = 0; i < posLeft.length; i++) {
295 | Point pointF = new Point();
296 | if (mEffect == Effect.EFFECT_ONE) {
297 | leftPathMeasure.getPosTan(leftDivider * i, f, null);
298 | } else {
299 | leftPathMeasure.getPosTan(moveLength + leftDivider * i, f, null);
300 | }
301 | pointF.x = f[0];
302 | pointF.y = f[1];
303 |
304 | if (mEffect == Effect.EFFECT_ONE) {
305 | CubeParam cubeParam = new CubeParam();
306 | cubeParam.calculateParams(mLeftStart, mLeftControl1, mLeftControl2, mLeftEnd, pointF);
307 | pointF.t = (float) Util.calculate3X(cubeParam.a, cubeParam.b, cubeParam.c, cubeParam.d);
308 | }
309 | posLeft[i] = pointF;
310 |
311 | }
312 | for (int i = 0; i < posRight.length; i++) {
313 | Point pointFR = new Point();
314 | rightPathMeasure.getPosTan(moveLength + leftDivider * i, f, null);
315 | pointFR.x = f[0];
316 | pointFR.y = f[1];
317 | if (mEffect == Effect.EFFECT_ONE) {
318 | CubeParam cubeParamR = new CubeParam();
319 | cubeParamR.calculateParams(mRightStart, mRightControl1, mRightControl2, mRightEnd, pointFR);
320 | pointFR.t = (float) Util.calculate3X(cubeParamR.a, cubeParamR.b, cubeParamR.c, cubeParamR.d);
321 |
322 | }
323 | posRight[i] = pointFR;
324 | }
325 |
326 | if (mEffect == Effect.EFFECT_ONE) {
327 | //首先将2条path上的点横坐标移动,根据t
328 | for (int i = 0; i < posLeft.length; i++) {
329 | posLeft[i].x = beiSaiEr3((posLeft[i].t + movePoint.t), mLeftStart.x, mLeftControl1.x, mLeftControl2.x, mLeftEnd.x);
330 | posLeft[i].y = beiSaiEr3((posLeft[i].t + movePoint.t), mLeftStart.y, mLeftControl1.y, mLeftControl2.y, mLeftEnd.y);
331 | }
332 | for (int i = 0; i < posRight.length; i++) {
333 | posRight[i].x = beiSaiEr3((posRight[i].t + movePoint.t), mRightStart.x, mRightControl1.x, mRightControl2.x, mRightEnd.x);
334 | posRight[i].y = beiSaiEr3((posRight[i].t + movePoint.t), mRightStart.y, mRightControl1.y, mRightControl2.y, mRightEnd.y);
335 | }
336 |
337 | }
338 |
339 | int index = 0;
340 | for (int i = 0; i <= HEIGHT; i++) {
341 | float fy = posLeft[i].y;
342 |
343 | float distance = posRight[i].x - posLeft[i].x;
344 |
345 | float pad = distance / WIDTH;
346 | float margin = posLeft[i].x;
347 | for (int x = 0; x <= WIDTH; x++) {
348 | float fx = margin + pad * x;
349 | setXY(matrixOriganal, index, fx, fy);
350 | index += 1;
351 | }
352 | }
353 | }
354 |
355 | /**
356 | * 求3阶贝塞尔值
357 | *
358 | * @param t (0 - 1)
359 | * @param start 起始点
360 | * @param control1 控制点1
361 | * @param control2 控制点2
362 | * @param end 结束点
363 | * @return
364 | */
365 | private float beiSaiEr3(float t, float start, float control1, float control2, float end) {
366 | float result = 0;
367 | if (t >= 1f) {
368 | t = 1f;
369 | }
370 | float timeLeft = 1 - t;
371 | result = start * timeLeft * timeLeft * timeLeft +
372 | 3 * control1 * t * timeLeft * timeLeft +
373 | 3 * control2 * t * t * timeLeft +
374 | end * t * t * t;
375 |
376 | return result;
377 |
378 | }
379 |
380 |
381 | @Override
382 | protected void onDraw(Canvas canvas) {
383 | super.onDraw(canvas);
384 | canvas.drawColor(Color.BLACK);
385 | canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, matrixOriganal, 0, null, 0, null);
386 | drawGuide(canvas);
387 | //
388 | canvas.drawPath(mPathLeft, mPaint);
389 | canvas.drawPath(mPathRight, mPaint);
390 | canvas.drawPoint(0, 700, mPaint);
391 | //
392 | if (mPointF != null) {
393 | canvas.drawLine(mPointF.x, mPointF.y, mViewWide - mPointF.x, mPointF.y, mPaintPoint);
394 | }
395 | //
396 | for (int i = 0; i < posLeft.length; i++) {
397 | canvas.drawPoint(posLeft[i].x, posLeft[i].y, pointPaint);
398 | }
399 |
400 | }
401 |
402 | @Override
403 | protected void onDetachedFromWindow() {
404 | super.onDetachedFromWindow();
405 | }
406 |
407 | /**
408 | * 属性动画差值器
409 | */
410 | class BeiSaiErEvaluator implements TypeEvaluator {
411 | private PointF pointF1; //控制点1
412 | private PointF pointF2; //控制点2
413 |
414 | public BeiSaiErEvaluator(PointF pointF1, PointF pointF2) {
415 | this.pointF1 = pointF1;
416 | this.pointF2 = pointF2;
417 | }
418 |
419 |
420 | @Override
421 | public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
422 | float timeLeft = 1 - fraction;
423 | Point pointF = new Point();
424 | pointF.t = fraction;
425 |
426 | //代入公式
427 | pointF.x = startValue.x * timeLeft * timeLeft * timeLeft +
428 | 3 * pointF1.x * fraction * timeLeft * timeLeft +
429 | 3 * pointF2.x * fraction * fraction * timeLeft +
430 | endValue.x * fraction * fraction * fraction;
431 |
432 | pointF.y = startValue.y * timeLeft * timeLeft * timeLeft +
433 | 3 * pointF1.y * fraction * timeLeft * timeLeft +
434 | 3 * pointF2.y * fraction * fraction * timeLeft +
435 | endValue.y * fraction * fraction * fraction;
436 | return pointF;
437 | }
438 | }
439 |
440 | class Point extends PointF {
441 | /**
442 | * 该点在贝塞尔曲线上的时间
443 | */
444 | public float t;
445 | }
446 |
447 | /**
448 | * 一元3次方程,各参数计算
449 | */
450 | class CubeParam {
451 | public double a;
452 | public double b;
453 | public double c;
454 | public double d;
455 |
456 | //a = -X0 + 3X1 - 3X2 + X3
457 | //b = 3X0 - 6X1 + 3X2
458 | //c = -3X0 + 3X1
459 | //d = X0 - Bx(已知的坐标)
460 |
461 | /**
462 | * @param f0 起始点
463 | * @param f1 控制点1
464 | * @param f2 控制点2
465 | * @param f3 结束点
466 | * @param fp 曲线上的点
467 | */
468 | public void calculateParams(PointF f0, PointF f1, PointF f2, PointF f3, PointF fp) {
469 | a = -1 * f0.x + 3 * f1.x - 3 * f2.x + f3.x;
470 | b = 3 * f0.x - 6 * f1.x + 3 * f2.x;
471 | c = -3 * f0.x + 3 * f1.x;
472 | d = f0.x - fp.x;
473 | }
474 | }
475 |
476 |
477 | }
478 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/weidget/BitmapMeshView2.java:
--------------------------------------------------------------------------------
1 | package com.xlg.weidget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.util.AttributeSet;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 |
12 | import com.xlg.beisaierlearn.R;
13 |
14 |
15 | /**
16 | * Created by xulinggang on 15/6/26.
17 | */
18 | public class BitmapMeshView2 extends View {
19 | private static final int WIDTH = 9, HEIGHT = 9;// 分割数
20 | private static final int COUNT = (WIDTH + 1) * (HEIGHT + 1);// 交点数
21 |
22 | private Bitmap mBitmap;// 位图对象
23 |
24 | private float[] matrixOriganal = new float[COUNT * 2];// 基准点坐标数组
25 | private float[] matrixMoved = new float[COUNT * 2];// 变换后点坐标数组
26 |
27 | private float clickX, clickY;// 触摸屏幕时手指的xy坐标
28 |
29 | private Paint origPaint, movePaint, linePaint;// 基准点、变换点和线段的绘制Paint
30 |
31 | public BitmapMeshView2(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | setFocusable(true);
34 |
35 | // 实例画笔并设置颜色
36 | origPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
37 | origPaint.setColor(0x660000FF);
38 | movePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
39 | movePaint.setColor(0x99FF0000);
40 | linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
41 | linePaint.setColor(0xFFFFFB00);
42 |
43 | // 获取位图资源
44 | mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.bt);
45 |
46 | // 初始化坐标数组
47 | int index = 0;
48 | for (int y = 0; y <= HEIGHT; y++) {
49 | float fy = mBitmap.getHeight() * y / HEIGHT;
50 |
51 | for (int x = 0; x <= WIDTH; x++) {
52 | float fx = mBitmap.getWidth() * x / WIDTH;
53 | setXY(matrixMoved, index, fx, fy);
54 | setXY(matrixOriganal, index, fx, fy);
55 | index += 1;
56 | }
57 | }
58 | }
59 |
60 | /**
61 | * 设置坐标数组
62 | *
63 | * @param array
64 | * 坐标数组
65 | * @param index
66 | * 标识值
67 | * @param x
68 | * x坐标
69 | * @param y
70 | * y坐标
71 | */
72 | private void setXY(float[] array, int index, float x, float y) {
73 | array[index * 2 + 0] = x;
74 | array[index * 2 + 1] = y;
75 | }
76 |
77 | @Override
78 | protected void onDraw(Canvas canvas) {
79 |
80 | // 绘制网格位图
81 | canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, matrixMoved, 0, null, 0, null);
82 |
83 | // 绘制参考元素
84 | drawGuide(canvas);
85 | }
86 |
87 | /**
88 | * 绘制参考元素
89 | *
90 | * @param canvas
91 | * 画布
92 | */
93 | private void drawGuide(Canvas canvas) {
94 | for (int i = 0; i < COUNT * 2; i += 2) {
95 | float x = matrixOriganal[i + 0];
96 | float y = matrixOriganal[i + 1];
97 | canvas.drawCircle(x, y, 4, origPaint);
98 |
99 | float x1 = matrixOriganal[i + 0];
100 | float y1 = matrixOriganal[i + 1];
101 | float x2 = matrixMoved[i + 0];
102 | float y2 = matrixMoved[i + 1];
103 | canvas.drawLine(x1, y1, x2, y2, origPaint);
104 | }
105 |
106 | for (int i = 0; i < COUNT * 2; i += 2) {
107 | float x = matrixMoved[i + 0];
108 | float y = matrixMoved[i + 1];
109 | canvas.drawCircle(x, y, 4, movePaint);
110 | }
111 |
112 | canvas.drawCircle(clickX, clickY, 6, linePaint);
113 | }
114 |
115 | /**
116 | * 计算变换数组坐标
117 | */
118 | private void smudge() {
119 | for (int i = 0; i < COUNT * 2; i += 2) {
120 |
121 | float xOriginal = matrixOriganal[i + 0];
122 | float yOriginal = matrixOriganal[i + 1];
123 |
124 | float dist_click_to_origin_x = clickX - xOriginal;
125 | float dist_click_to_origin_y = clickY - yOriginal;
126 |
127 | float kv_kat = dist_click_to_origin_x * dist_click_to_origin_x + dist_click_to_origin_y * dist_click_to_origin_y;
128 |
129 | float pull = (float) (1000000 / kv_kat / Math.sqrt(kv_kat));
130 |
131 | if (pull >= 1) {
132 | matrixMoved[i + 0] = clickX;
133 | matrixMoved[i + 1] = clickY;
134 | } else {
135 | matrixMoved[i + 0] = xOriginal + dist_click_to_origin_x * pull;
136 | matrixMoved[i + 1] = yOriginal + dist_click_to_origin_y * pull;
137 | }
138 | }
139 | }
140 |
141 | @Override
142 | public boolean onTouchEvent(MotionEvent event) {
143 | clickX = event.getX();
144 | clickY = event.getY();
145 | smudge();
146 | invalidate();
147 | return true;
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/weidget/DrawDottedCurve.java:
--------------------------------------------------------------------------------
1 | package com.xlg.weidget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.DashPathEffect;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.PathMeasure;
10 | import android.os.CountDownTimer;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.View;
14 |
15 | import java.util.Random;
16 |
17 | public class DrawDottedCurve extends View {
18 |
19 | Path[] path = new Path[8];
20 | Path[] drawingPath = new Path[8];
21 | PathMeasure[] measure = new PathMeasure[8];
22 | Path[] segmentPath = new Path[8];
23 | float[] length = new float[8];
24 | float[] start = new float[8];
25 | float[] percent = new float[8];
26 | Paint paint = new Paint();
27 | float x1;
28 | float y1;
29 | float x3;
30 | float y3;
31 | long k = 0;
32 | Canvas canvas;
33 | Random r = new Random();
34 |
35 | // public DrawDottedCurve(Context context, AttributeSet attrs) {
36 | // super(context, attrs);
37 | // }
38 |
39 | public DrawDottedCurve(Context context, int a, int b, int c, int d) {
40 | super(context);
41 | x1 = a;
42 | y1 = b;
43 | x3 = c;
44 | y3 = d;
45 |
46 | paint.setAlpha(255);
47 | paint.setStrokeWidth(2);
48 | paint.setColor(Color.RED);
49 | paint.setStyle(Paint.Style.STROKE);
50 | paint.setPathEffect(new DashPathEffect(new float[] { 2, 4 }, 50));
51 |
52 | for (int i = 0; i < 8; i++) {
53 | k = r.nextInt(100 - 30) + 30;
54 | path[i] = new Path();
55 | path[i].moveTo(x1 + k, y1 + k); //
56 | path[i].quadTo((x3 + k - x1 + k) / 2, (y3 + k - y1 + k) / 2,
57 | x3 + k, y3 + k); // Calculate Bezier Curves
58 | }
59 |
60 | final long DRAW_TIME = 10000;
61 | CountDownTimer timer = new CountDownTimer(DRAW_TIME, 100) {
62 |
63 | @Override
64 | public void onTick(long millisUntilFinished) {
65 | Log.d("Timer", "Inizio");
66 | for (int i = 0; i < 8; i++) {
67 | start[i] = 0;
68 | measure[i] = new PathMeasure();
69 | measure[i].setPath(path[i], false);
70 |
71 | percent[i] = ((float) (DRAW_TIME - millisUntilFinished))
72 | / (float) DRAW_TIME;
73 |
74 | segmentPath[i] = new Path();
75 | drawingPath[i] = new Path();
76 | length[i] = measure[i].getLength() * percent[i];
77 | measure[i].getSegment(start[i], length[i], segmentPath[i],
78 | true);
79 | start[i] = length[i];
80 | drawingPath[i].addPath(segmentPath[i]);
81 |
82 |
83 | }
84 | invalidate();
85 | }
86 | @Override
87 | public void onFinish() {
88 | for (int i = 0; i < 8; i++) {
89 | measure[i].getSegment(start[i], measure[i].getLength(),
90 | segmentPath[i], true);
91 | drawingPath[i].addPath(segmentPath[i]);
92 |
93 |
94 | }
95 |
96 | invalidate();
97 | Log.d("Timer", "Fine");
98 | }
99 |
100 | };
101 | timer.start();
102 | }
103 | @Override
104 | public void onDraw(Canvas canvas) {
105 | super.onDraw(canvas);
106 | for (int i = 0; i < 8; i++) {
107 | canvas.drawPath(drawingPath[i], paint);
108 | invalidate();
109 |
110 | }
111 |
112 | }
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/weidget/HeartFlowLayout.java:
--------------------------------------------------------------------------------
1 | package com.xlg.weidget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.TypeEvaluator;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.graphics.PointF;
8 | import android.util.AttributeSet;
9 | import android.view.ViewGroup;
10 | import android.view.animation.AnimationSet;
11 | import android.view.animation.LinearInterpolator;
12 | import android.widget.ImageView;
13 | import android.widget.RelativeLayout;
14 |
15 |
16 | import com.xlg.beisaierlearn.R;
17 |
18 | import java.util.Random;
19 |
20 | /**
21 | * Created by xulinggang on 15/6/21.
22 | */
23 | public class HeartFlowLayout extends RelativeLayout {
24 | private Context mContext;
25 | private Random mRandom = new Random();
26 | private static final int[] mDrawables = new int[]{
27 | R.mipmap.heart1,
28 | R.mipmap.heart2,
29 | R.mipmap.heart3,
30 | R.mipmap.heart4,
31 | };
32 | public HeartFlowLayout(Context context) {
33 | super(context);
34 | mContext = context;
35 | }
36 |
37 | public HeartFlowLayout(Context context, AttributeSet attrs) {
38 | super(context, attrs);
39 | mContext = context;
40 | }
41 |
42 | private int mWide;
43 | private int mHeight;
44 | private int mMiddle;
45 | @Override
46 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
47 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
48 | mWide = getMeasuredWidth();
49 | mHeight = getMeasuredHeight();
50 | mMiddle = mWide / 2;
51 | }
52 |
53 | private PointF getPointF(int scale) {
54 |
55 | PointF pointF = new PointF();
56 | pointF.x = mRandom.nextInt((mWide - 100));//减去100 是为了控制 x轴活动范围,看效果 随意~~
57 | //再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些 也可以用其他方法
58 | pointF.y = mRandom.nextInt((mHeight - 100))/scale;
59 | return pointF;
60 | }
61 |
62 | public void addHeart(){
63 | final ImageView imageView = new ImageView(mContext);
64 | imageView.setImageResource(mDrawables[mRandom.nextInt(4)]);
65 | LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
66 | params.addRule(ALIGN_PARENT_BOTTOM,TRUE);
67 | params.addRule(CENTER_HORIZONTAL,TRUE);
68 | imageView.setLayoutParams(params);
69 | addView(imageView);
70 |
71 | PointF pointF1 = new PointF(mMiddle - 50,mHeight - 200);
72 | PointF pointF2 = new PointF(mMiddle + 100,mHeight - 320);
73 | //new PointF((mWidth-dWidth)/2,mHeight-dHeight),new PointF(random.nextInt(getWidth()),0));//随机
74 | PointF pointF0 = new PointF(0,mHeight);
75 | PointF pointF3 = new PointF( 300,0);
76 |
77 | ValueAnimator animator = ValueAnimator.ofObject(new BezierEvalutor(pointF1,pointF2),pointF0,pointF3);
78 | animator.setDuration(3000);
79 | animator.setTarget(imageView);
80 | animator.setInterpolator(new LinearInterpolator());
81 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
82 | @Override
83 | public void onAnimationUpdate(ValueAnimator animation) {
84 | PointF pointF = (PointF) animation.getAnimatedValue();
85 | imageView.setX(pointF.x);
86 | imageView.setY(pointF.y);
87 | }
88 | });
89 | animator.addListener(new Animator.AnimatorListener() {
90 | @Override
91 | public void onAnimationStart(Animator animation) {
92 |
93 | }
94 |
95 | @Override
96 | public void onAnimationEnd(Animator animation) {
97 | removeView(imageView);
98 | }
99 |
100 | @Override
101 | public void onAnimationCancel(Animator animation) {
102 |
103 | }
104 |
105 | @Override
106 | public void onAnimationRepeat(Animator animation) {
107 |
108 | }
109 | });
110 | animator.start();
111 |
112 |
113 | }
114 |
115 | class BezierEvalutor implements TypeEvaluator{
116 | //B(t) = P0 * (1-t)^3 + 3 * P1 * t(1-t)^2 + 3 * P2 * t^2(1-t) + P3 * t^3
117 |
118 | //中间两个控制点
119 | private PointF pointF1;
120 | private PointF pointF2;
121 |
122 | public BezierEvalutor(PointF pointF1, PointF pointF2) {
123 | this.pointF1 = pointF1;
124 | this.pointF2 = pointF2;
125 | }
126 |
127 |
128 | @Override
129 | public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
130 | float timeLeft = 1.0f - fraction;
131 | PointF pointF = new PointF();
132 |
133 | //代入公式
134 | pointF.x = startValue.x * timeLeft * timeLeft * timeLeft +
135 | 3 * pointF1.x * fraction * timeLeft * timeLeft +
136 | 3 * pointF2.x * fraction * fraction * timeLeft +
137 | endValue.x + fraction * fraction * fraction;
138 |
139 | pointF.y = startValue.y * timeLeft * timeLeft * timeLeft +
140 | 3 * pointF1.y * fraction * timeLeft * timeLeft +
141 | 3 * pointF2.y * fraction * fraction * timeLeft +
142 | endValue.y + fraction * fraction * fraction;
143 |
144 | return pointF;
145 | }
146 | }
147 |
148 |
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xlg/weidget/PathMeasureView.java:
--------------------------------------------------------------------------------
1 | package com.xlg.weidget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.graphics.PathEffect;
9 | import android.graphics.PathMeasure;
10 | import android.graphics.PointF;
11 | import android.graphics.RectF;
12 | import android.transition.PathMotion;
13 | import android.util.AttributeSet;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.view.animation.PathInterpolator;
17 |
18 | /**
19 | * Created by xulinggang on 15/6/28.
20 | */
21 | public class PathMeasureView extends View {
22 | private static final String TAG = "PathMeasureView";
23 | private int mViewWide;
24 | private int mViewHeight;
25 | private float radius;
26 | public PathMeasureView(Context context, AttributeSet attrs) {
27 | super(context, attrs);
28 | }
29 |
30 | @Override
31 | protected void onFinishInflate() {
32 | super.onFinishInflate();
33 | }
34 |
35 |
36 |
37 | @Override
38 | protected void onDraw(Canvas canvas) {
39 | super.onDraw(canvas);
40 | canvas.drawColor(Color.BLACK);
41 | drawPathCircle(canvas);
42 | }
43 |
44 |
45 | @Override
46 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
47 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
48 | mViewWide = getMeasuredWidth();
49 | mViewHeight = getMeasuredHeight();
50 | radius = 200;
51 | Log.i(TAG, "wide = " + mViewWide + ", mViewHeight = " + mViewHeight);
52 | }
53 |
54 | private void drawPathCircle(Canvas canvas) {
55 | PointF center = new PointF(mViewWide / 2, mViewHeight / 2);
56 | RectF area = new RectF(center.x - radius,center.y - radius, center.x + radius, center.y + radius);
57 | Path orbit = new Path();
58 | orbit.addArc(area, 180, 90);
59 |
60 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
61 | paint.setStrokeWidth(6);
62 | paint.setStyle(Paint.Style.STROKE);
63 | paint.setColor(Color.RED);
64 | canvas.drawPath(orbit, paint);
65 |
66 | Paint paintPoint = new Paint(Paint.ANTI_ALIAS_FLAG);
67 | paintPoint.setStrokeWidth(6);
68 | paintPoint.setStyle(Paint.Style.STROKE);
69 | paintPoint.setColor(Color.WHITE);
70 | canvas.drawPoint(center.x, center.y, paintPoint);
71 |
72 | // PathInterpolator pathInterpolator = new PathInterpolator()
73 | // PathEffect
74 | PathMeasure pathMeasure = new PathMeasure(orbit,false);
75 | float length = pathMeasure.getLength();
76 | Log.i(TAG,"length => " + length);
77 | float divider = length / 4;
78 | float[] position = new float[2];
79 | for (int i = 0; i <= 4; i++) {
80 | pathMeasure.getPosTan(divider * i,position , null);
81 | canvas.drawPoint(position[0],position[1],paintPoint);
82 | }
83 |
84 |
85 |
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
13 |
14 |
21 |
22 |
30 |
31 |
40 |
41 |
42 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/bt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/bt.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/heart1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/heart1.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/heart2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/heart2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/heart3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/heart3.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/heart4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/heart4.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xhdpi/image.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BeiSaierLearn
3 |
4 | Hello world!
5 | Settings
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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:1.2.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 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xlg/BeiSaierLearn/acab5bceb0b9e30f2a109fcf743c0133c68d1d22/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------