├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── xys │ │ └── com │ │ └── pathart │ │ ├── activities │ │ ├── MainActivity.java │ │ ├── PathPainterActivity.java │ │ ├── PathPainterEffectActivity.java │ │ └── PathTanActivity.java │ │ └── views │ │ ├── PathPainter.java │ │ ├── PathPainterEffect.java │ │ └── PathTan.java │ └── res │ ├── layout │ ├── activity_main.xml │ ├── activity_path_painter.xml │ ├── activity_path_painter_effect.xml │ └── activity_path_tan.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── 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 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | PathArt -------------------------------------------------------------------------------- /.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/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "24.0.0" 6 | 7 | defaultConfig { 8 | applicationId "xys.com.pathart" 9 | minSdkVersion 14 10 | targetSdkVersion 24 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:24.0.0' 26 | } 27 | -------------------------------------------------------------------------------- /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/xuyisheng/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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | import xys.com.pathart.R; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_main); 16 | } 17 | 18 | public void pathPainter(View view) { 19 | startActivity(new Intent(this, PathPainterActivity.class)); 20 | } 21 | 22 | public void pathPainterEffect(View view) { 23 | startActivity(new Intent(this, PathPainterEffectActivity.class)); 24 | } 25 | 26 | public void pathPainterTan(View view) { 27 | startActivity(new Intent(this, PathTanActivity.class)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/activities/PathPainterActivity.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import xys.com.pathart.R; 7 | 8 | public class PathPainterActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_path_painter); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/activities/PathPainterEffectActivity.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import xys.com.pathart.R; 7 | 8 | public class PathPainterEffectActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_path_painter_effect); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/activities/PathTanActivity.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import xys.com.pathart.R; 7 | 8 | public class PathTanActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_path_tan); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/views/PathPainter.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.views; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PathMeasure; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | /** 13 | * 路径动画 PathMeasure 14 | *

15 | * Created by xuyisheng on 16/7/15. 16 | */ 17 | public class PathPainter extends View { 18 | 19 | private Path mPath; 20 | private Paint mPaint; 21 | private PathMeasure mPathMeasure; 22 | private float mAnimatorValue; 23 | private Path mDst; 24 | private float mLength; 25 | 26 | public PathPainter(Context context) { 27 | super(context); 28 | } 29 | 30 | public PathPainter(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | mPathMeasure = new PathMeasure(); 33 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 34 | mPaint.setStyle(Paint.Style.STROKE); 35 | mPaint.setStrokeWidth(5); 36 | mPath = new Path(); 37 | mPath.addCircle(400, 400, 100, Path.Direction.CW); 38 | mPathMeasure.setPath(mPath, true); 39 | mLength = mPathMeasure.getLength(); 40 | mDst = new Path(); 41 | 42 | final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); 43 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 44 | @Override 45 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 46 | mAnimatorValue = (float) valueAnimator.getAnimatedValue(); 47 | invalidate(); 48 | } 49 | }); 50 | valueAnimator.setDuration(2000); 51 | valueAnimator.setRepeatCount(ValueAnimator.INFINITE); 52 | valueAnimator.start(); 53 | } 54 | 55 | public PathPainter(Context context, AttributeSet attrs, int defStyleAttr) { 56 | super(context, attrs, defStyleAttr); 57 | } 58 | 59 | @Override 60 | protected void onDraw(Canvas canvas) { 61 | super.onDraw(canvas); 62 | mDst.reset(); 63 | // 硬件加速的BUG 64 | mDst.lineTo(0,0); 65 | float stop = mLength * mAnimatorValue; 66 | float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength)); 67 | mPathMeasure.getSegment(start, stop, mDst, true); 68 | canvas.drawPath(mDst, mPaint); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/views/PathPainterEffect.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.views; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.DashPathEffect; 8 | import android.graphics.Paint; 9 | import android.graphics.Path; 10 | import android.graphics.PathEffect; 11 | import android.graphics.PathMeasure; 12 | import android.util.AttributeSet; 13 | import android.view.View; 14 | import android.view.animation.AccelerateDecelerateInterpolator; 15 | 16 | /** 17 | * 路径绘制——DashPathEffect 18 | *

19 | * Created by xuyisheng on 16/7/15. 20 | */ 21 | public class PathPainterEffect extends View implements View.OnClickListener{ 22 | 23 | private Paint mPaint; 24 | private Path mPath; 25 | private PathMeasure mPathMeasure; 26 | private PathEffect mEffect; 27 | private float fraction = 0; 28 | private ValueAnimator mAnimator; 29 | 30 | public PathPainterEffect(Context context) { 31 | super(context); 32 | } 33 | 34 | public PathPainterEffect(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | mPath = new Path(); 37 | mPath.reset(); 38 | mPath.moveTo(100, 100); 39 | mPath.lineTo(100, 500); 40 | mPath.lineTo(400, 300); 41 | mPath.close(); 42 | 43 | mPaint = new Paint(); 44 | mPaint.setColor(Color.BLACK); 45 | mPaint.setStyle(Paint.Style.STROKE); 46 | mPaint.setStrokeWidth(5); 47 | 48 | mPathMeasure = new PathMeasure(mPath, false); 49 | final float length = mPathMeasure.getLength(); 50 | 51 | mAnimator = ValueAnimator.ofFloat(1, 0); 52 | mAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 53 | mAnimator.setDuration(2000); 54 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 55 | @Override 56 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 57 | fraction = (float) valueAnimator.getAnimatedValue(); 58 | mEffect = new DashPathEffect(new float[]{length, length}, fraction * length); 59 | mPaint.setPathEffect(mEffect); 60 | invalidate(); 61 | } 62 | }); 63 | setOnClickListener(this); 64 | } 65 | 66 | public PathPainterEffect(Context context, AttributeSet attrs, int defStyleAttr) { 67 | super(context, attrs, defStyleAttr); 68 | } 69 | 70 | @Override 71 | protected void onDraw(Canvas canvas) { 72 | super.onDraw(canvas); 73 | canvas.drawPath(mPath, mPaint); 74 | } 75 | 76 | @Override 77 | public void onClick(View view) { 78 | mAnimator.start(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/xys/com/pathart/views/PathTan.java: -------------------------------------------------------------------------------- 1 | package xys.com.pathart.views; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PathMeasure; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | /** 13 | * 曲线上切点 14 | *

15 | * Created by xuyisheng on 16/7/15. 16 | */ 17 | public class PathTan extends View implements View.OnClickListener { 18 | 19 | private Path mPath; 20 | private float[] pos; 21 | private float[] tan; 22 | private Paint mPaint; 23 | float currentValue = 0; 24 | private PathMeasure mMeasure; 25 | 26 | public PathTan(Context context) { 27 | super(context); 28 | } 29 | 30 | public PathTan(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | mPath = new Path(); 33 | mPaint = new Paint(); 34 | mPaint.setStyle(Paint.Style.STROKE); 35 | mPaint.setStrokeWidth(4); 36 | mMeasure = new PathMeasure(); 37 | mPath.addCircle(0, 0, 200, Path.Direction.CW); 38 | mMeasure.setPath(mPath, false); 39 | pos = new float[2]; 40 | tan = new float[2]; 41 | setOnClickListener(this); 42 | } 43 | 44 | public PathTan(Context context, AttributeSet attrs, int defStyleAttr) { 45 | super(context, attrs, defStyleAttr); 46 | } 47 | 48 | @Override 49 | protected void onDraw(Canvas canvas) { 50 | super.onDraw(canvas); 51 | mMeasure.getPosTan(mMeasure.getLength() * currentValue, pos, tan); 52 | float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); 53 | 54 | canvas.save(); 55 | canvas.translate(400, 400); 56 | canvas.drawPath(mPath, mPaint); 57 | canvas.drawCircle(pos[0], pos[1], 10, mPaint); 58 | canvas.rotate(degrees); 59 | canvas.drawLine(0, -200, 300, -200, mPaint); 60 | canvas.restore(); 61 | } 62 | 63 | @Override 64 | public void onClick(View view) { 65 | ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 66 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 67 | @Override 68 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 69 | currentValue = (float) valueAnimator.getAnimatedValue(); 70 | invalidate(); 71 | } 72 | }); 73 | animator.setDuration(3000); 74 | animator.setRepeatCount(ValueAnimator.INFINITE); 75 | animator.start(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 |