Android通过Path实现复杂效果(搜索按钮+时钟的实现 +svg实现)

简介:
Path :
在Android中复杂的图形的绘制绝大多数是通过path来实现,比如绘制一条曲线,然后让一个物体随着这个曲线运动,比如搜索按钮,比如一个简单时钟的实现:

那么什么是path呢!
定义:path  就是路径,就是图形的路径的集合,它里边包含了路径里边的坐标点,等等的属性。我们可以获取到任意点的坐标,正切值。

那么要获取Path上边所有点的坐标还需要用到一个类,PathMeasure;

PathMesure:

PathMeasure是一个用来测量Path的类,主要有以下方法:

构造方法

方法名 释义
PathMeasure() 创建一个空的PathMeasure
PathMeasure(Path path, boolean forceClosed) 创建 PathMeasure 并关联一个指定的Path(Path需要已经创建完成)。

公共方法

返回值 方法名 释义
void setPath(Path path, boolean forceClosed) 关联一个Path
boolean isClosed() 是否闭合
float getLength() 获取Path的长度
boolean nextContour() 跳转到下一个轮廓
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 截取片段
boolean getPosTan(float distance, float[] pos, float[] tan) 获取指定长度的位置坐标及该点切线值
boolean getMatrix(float distance, Matrix matrix, int flags) 获取指定长度的位置坐标及该点Matrix

可以看到,这个就等于是一个Path的一个工具类,方法很简单,那么就开始我们所要做的按钮跟时钟的开发吧


(1)搜索按钮:

首先上图:

要实现这个功能首先要把他分解开来做;

创建搜索按钮的path路径,然后创建外圈旋转的path,


  
  
  1. public void initPath(){  
  2.       mPath_search = new Path();  
  3.       mPath_circle = new Path();  
  4.   
  5.       mMeasure = new PathMeasure();  
  6.   
  7.       // 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值  
  8.       RectF oval1 = new RectF(-50, -505050);          // 放大镜圆环  
  9.       mPath_search.addArc(oval1, 45359.9f);  
  10.   
  11.       RectF oval2 = new RectF(-100, -100100100);      // 外部圆环  
  12.       mPath_circle.addArc(oval2, 45, -359.9f);  
  13.   
  14.       float[] pos = new float[2];  
  15.   
  16.       mMeasure.setPath(mPath_circle, false);               // 放大镜把手的位置  
  17.       mMeasure.getPosTan(0, pos, null);  
  18.   
  19.       mPath_search.lineTo(pos[0], pos[1]);                 // 放大镜把手  
  20.   
  21.       Log.i("TAG""pos=" + pos[0] + ":" + pos[1]);  
  22.   
  23.   }  



我们要的效果就是点击搜索按钮的时候开始从按钮变为旋转,然后搜索结束以后变为搜索按钮。

所以我们可以确定有四种状态:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public   enum  Seach_State{  
  2.     START,END,NONE,SEARCHING  
  3.  }  

 然后根据状态来进行动态绘制path,动态绘制path就要使用到PathMeasure测量当前path的坐标,然后进行绘制。

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. private void drawPath(Canvas c) {  
  2.     c.translate(mWidth / 2, mHeight / 2);  
  3.     switch (mState){  
  4.   
  5.         case NONE:  
  6.             c.drawPath(mPath_search,mPaint);  
  7.             break;  
  8.   
  9.         case START:  
  10.             mMeasure.setPath(mPath_search,true);  
  11.             Path path = new Path();  
  12.             mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);  
  13.             c.drawPath(path,mPaint);  
  14.             break;  
  15.   
  16.         case SEARCHING:  
  17.             mMeasure.setPath(mPath_circle,true);  
  18.             Path path_search = new Path();  
  19.             mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);  
  20.             c.drawPath(path_search,mPaint);  
  21.             break;  
  22.   
  23.         case END:  
  24.             mMeasure.setPath(mPath_search,true);  
  25.             Path path_view = new Path();  
  26.   
  27.             mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);  
  28.             c.drawPath(path_view,mPaint);  
  29.             break;  
  30.     }  
  31.   
  32. }  


然后就是需要通过使用属性动画来返回当前该绘制的百分百,通过这个值来进行计算要绘制的path。

下边是整个代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.duoku.platform.demo.canvaslibrary.attract.view;  
  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.graphics.Path;  
  10. import android.graphics.PathMeasure;  
  11. import android.graphics.RectF;  
  12. import android.util.AttributeSet;  
  13. import android.util.Log;  
  14. import android.view.View;  
  15.   
  16. /** 
  17.  * Created by chenpengfei_d on 2016/9/7. 
  18.  */  
  19. public class SearchView extends View {  
  20.     private Paint mPaint;  
  21.     private Context mContext;  
  22.     private Path mPath_circle;  
  23.     private Path mPath_search;  
  24.     private PathMeasure mMeasure;  
  25.     private ValueAnimator mValueAnimator_search;  
  26.     private long  defaultduration=3000;  
  27.     private float curretnAnimationValue;  
  28.     private Seach_State mState = Seach_State.SEARCHING;  
  29.     public SearchView(Context context) {  
  30.         super(context);  
  31.         init(context);  
  32.     }  
  33.   
  34.     public SearchView(Context context, AttributeSet attrs) {  
  35.         super(context, attrs);  
  36.         init(context);  
  37.     }  
  38.   
  39.     public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {  
  40.         super(context, attrs, defStyleAttr);  
  41.         init(context);  
  42.     }  
  43.   
  44.     public void init(Context context){  
  45.         this.mContext = context;  
  46.         initPaint();  
  47.         initPath();  
  48.         initAnimation();  
  49.   
  50.     }  
  51.     public void initPaint(){  
  52.         mPaint = new Paint();  
  53.         mPaint.setDither(true);  
  54.         mPaint.setStrokeCap(Paint.Cap.ROUND);//设置笔头效果  
  55.         mPaint.setAntiAlias(true);  
  56.         mPaint.setColor(Color.RED);  
  57.         mPaint.setStrokeWidth(3);  
  58.         mPaint.setStyle(Paint.Style.STROKE);  
  59.     }  
  60.   
  61.     public void initPath(){  
  62.         mPath_search = new Path();  
  63.         mPath_circle = new Path();  
  64.   
  65.         mMeasure = new PathMeasure();  
  66.   
  67.         // 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值  
  68.         RectF oval1 = new RectF(-50, -505050);          // 放大镜圆环  
  69.         mPath_search.addArc(oval1, 45359.9f);  
  70.   
  71.         RectF oval2 = new RectF(-100, -100100100);      // 外部圆环  
  72.         mPath_circle.addArc(oval2, 45, -359.9f);  
  73.   
  74.         float[] pos = new float[2];  
  75.   
  76.         mMeasure.setPath(mPath_circle, false);               // 放大镜把手的位置  
  77.         mMeasure.getPosTan(0, pos, null);  
  78.   
  79.         mPath_search.lineTo(pos[0], pos[1]);                 // 放大镜把手  
  80.   
  81.         Log.i("TAG""pos=" + pos[0] + ":" + pos[1]);  
  82.   
  83.     }  
  84.   
  85.     public void initAnimation(){  
  86.         mValueAnimator_search = ValueAnimator.ofFloat(0f,1.0f).setDuration(defaultduration);  
  87.   
  88.         mValueAnimator_search.addUpdateListener(updateListener);  
  89.   
  90.         mValueAnimator_search.addListener(animationListener);  
  91.     }  
  92.     private ValueAnimator.AnimatorUpdateListener updateListener = new ValueAnimator.AnimatorUpdateListener() {  
  93.         @Override  
  94.         public void onAnimationUpdate(ValueAnimator animation) {  
  95.             curretnAnimationValue = (float) animation.getAnimatedValue();  
  96.             invalidate();  
  97.         }  
  98.     };  
  99.   
  100.     private Animator.AnimatorListener animationListener = new Animator.AnimatorListener() {  
  101.         @Override  
  102.         public void onAnimationStart(Animator animation) {  
  103.   
  104.         }  
  105.   
  106.         @Override  
  107.         public void onAnimationEnd(Animator animation) {  
  108.                 if(mState ==Seach_State.START){  
  109.                     setState(Seach_State.SEARCHING);  
  110.                 }  
  111.         }  
  112.   
  113.         @Override  
  114.         public void onAnimationCancel(Animator animation) {  
  115.   
  116.         }  
  117.   
  118.         @Override  
  119.         public void onAnimationRepeat(Animator animation) {  
  120.   
  121.         }  
  122.     };  
  123.     @Override  
  124.     protected void onDraw(Canvas canvas) {  
  125.         super.onDraw(canvas);  
  126.   
  127.         drawPath(canvas);  
  128.     }  
  129.     private int mWidth,mHeight;  
  130.     @Override  
  131.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  132.         super.onSizeChanged(w, h, oldw, oldh);  
  133.         mWidth = w;  
  134.         mHeight = h;  
  135.   
  136.     }  
  137.   
  138.     private void drawPath(Canvas c) {  
  139.         c.translate(mWidth / 2, mHeight / 2);  
  140.         switch (mState){  
  141.   
  142.             case NONE:  
  143.                 c.drawPath(mPath_search,mPaint);  
  144.                 break;  
  145.   
  146.             case START:  
  147.                 mMeasure.setPath(mPath_search,true);  
  148.                 Path path = new Path();  
  149.                 mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);  
  150.                 c.drawPath(path,mPaint);  
  151.                 break;  
  152.   
  153.             case SEARCHING:  
  154.                 mMeasure.setPath(mPath_circle,true);  
  155.                 Path path_search = new Path();  
  156.                 mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);  
  157.                 c.drawPath(path_search,mPaint);  
  158.                 break;  
  159.   
  160.             case END:  
  161.                 mMeasure.setPath(mPath_search,true);  
  162.                 Path path_view = new Path();  
  163.   
  164.                 mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);  
  165.                 c.drawPath(path_view,mPaint);  
  166.                 break;  
  167.         }  
  168.   
  169.     }  
  170.   
  171.   
  172.     public void setState(Seach_State state){  
  173.         this.mState = state;  
  174.         startSearch();  
  175.     }  
  176.   
  177.     public void startSearch(){  
  178.         switch (mState){  
  179.             case START:  
  180.                 mValueAnimator_search.setRepeatCount(0);  
  181.                 break;  
  182.   
  183.             case SEARCHING:  
  184.                 mValueAnimator_search.setRepeatCount(ValueAnimator.INFINITE);  
  185.                 mValueAnimator_search.setRepeatMode(ValueAnimator.REVERSE);  
  186.                 break;  
  187.   
  188.             case END:  
  189.                 mValueAnimator_search.setRepeatCount(0);  
  190.                 break;  
  191.         }  
  192.         mValueAnimator_search.start();  
  193.     }  
  194.    public   enum  Seach_State{  
  195.        START,END,NONE,SEARCHING  
  196.     }  
  197. }  


(学习的点:path可以组合,可以把不同的path放置到一个path里边,然后进行统一的绘制)

(2)时钟:

效果:


说一下时钟的思路啊,网上很多时钟都是通过Canvas绘制基本图形实现的,没有通过path来实现的,使用path实现是为了以后更加灵活的控制时钟的绘制效果,比如我们要让最外边的圆圈逆时针旋转,还比如在上边添加些小星星啥的,用path的话会更加灵活。

时钟的实现分部分:

1、创建外圈path路径

2、创建刻度path路径,要区分整点,绘制时间点

3、绘制指针,(这个使用的是canvas绘制的线段,也可以使用Path,可以自己测试)

需要计算当前时针,分针,秒针的角度,然后进行绘制

整体代码:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.duoku.platform.demo.canvaslibrary.attract.view;  
  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.PathMeasure;  
  9. import android.os.Handler;  
  10. import android.util.AttributeSet;  
  11. import android.view.View;  
  12.   
  13. import java.util.Calendar;  
  14.   
  15. /** 
  16.  * Created by chenpengfei_d on 2016/9/8. 
  17.  */  
  18. public class TimeView extends View {  
  19.     private Paint mPaint,mPaint_time;  
  20.     private Paint mPaint_h,mPaint_m,mPaint_s;  
  21.     private Path mPath_Circle;  
  22.     private Path mPath_Circle_h;  
  23.     private Path mPath_Circle_m;  
  24.     private Path mPath_h,mPath_m,mPath_s;  
  25.     private Path mPath_duration;  
  26.   
  27.     private PathMeasure mMeasure;  
  28.     private PathMeasure mMeasure_h;  
  29.     private PathMeasure mMeasure_m;  
  30.     private Handler mHandler = new Handler();  
  31.     private Runnable clockRunnable;  
  32.     private boolean isRunning;  
  33.     public TimeView(Context context) {  
  34.         super(context);  
  35.         init();  
  36.     }  
  37.   
  38.     public TimeView(Context context, AttributeSet attrs) {  
  39.         super(context, attrs);  
  40.         init();  
  41.     }  
  42.   
  43.     public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {  
  44.         super(context, attrs, defStyleAttr);  
  45.         init();  
  46.     }  
  47.     int  t = 3;  
  48.     public void init(){  
  49.         //初始化画笔  
  50.         mPaint = new Paint();  
  51.         mPaint.setDither(true);  
  52.         mPaint.setAntiAlias(true);  
  53.         mPaint.setStyle(Paint.Style.STROKE);  
  54.         mPaint.setStrokeWidth(2);  
  55.         mPaint.setStrokeCap(Paint.Cap.ROUND);  
  56.         mPaint.setStrokeJoin(Paint.Join.ROUND);  
  57.         mPaint.setColor(Color.RED);  
  58.         mPaint_time = new Paint();  
  59.         mPaint_time.setDither(true);  
  60.         mPaint_time.setAntiAlias(true);  
  61.         mPaint_time.setStyle(Paint.Style.STROKE);  
  62.         mPaint_time.setStrokeWidth(2);  
  63.         mPaint_time.setTextSize(15);  
  64.         mPaint_time.setStrokeCap(Paint.Cap.ROUND);  
  65.         mPaint_time.setStrokeJoin(Paint.Join.ROUND);  
  66.         mPaint_time.setColor(Color.RED);  
  67.   
  68.         mPaint_h = new Paint();  
  69.         mPaint_h.setDither(true);  
  70.         mPaint_h.setAntiAlias(true);  
  71.         mPaint_h.setStyle(Paint.Style.STROKE);  
  72.         mPaint_h.setStrokeWidth(6);  
  73.         mPaint_h.setTextSize(15);  
  74.         mPaint_h.setStrokeCap(Paint.Cap.ROUND);  
  75.         mPaint_h.setStrokeJoin(Paint.Join.ROUND);  
  76.         mPaint_h.setColor(Color.RED);  
  77.   
  78.         mPaint_m = new Paint();  
  79.         mPaint_m.setDither(true);  
  80.         mPaint_m.setAntiAlias(true);  
  81.         mPaint_m.setStyle(Paint.Style.STROKE);  
  82.         mPaint_m.setStrokeWidth(4);  
  83.         mPaint_m.setTextSize(15);  
  84.         mPaint_m.setStrokeCap(Paint.Cap.ROUND);  
  85.         mPaint_m.setStrokeJoin(Paint.Join.ROUND);  
  86.         mPaint_m.setColor(Color.RED);  
  87.   
  88.         mPaint_s = new Paint();  
  89.         mPaint_s.setDither(true);  
  90.         mPaint_s.setAntiAlias(true);  
  91.         mPaint_s.setStyle(Paint.Style.STROKE);  
  92.         mPaint_s.setStrokeWidth(2);  
  93.         mPaint_s.setTextSize(15);  
  94.         mPaint_s.setStrokeCap(Paint.Cap.ROUND);  
  95.         mPaint_s.setStrokeJoin(Paint.Join.ROUND);  
  96.         mPaint_s.setColor(Color.RED);  
  97.         //初始化刻度  
  98.         mPath_Circle  = new Path();  
  99.         mPath_Circle.addCircle(0,0,250, Path.Direction.CCW);  
  100.         mPath_Circle_h  = new Path();  
  101.         mPath_Circle_h.addCircle(0,0,220, Path.Direction.CCW);  
  102.         mPath_Circle_m  = new Path();  
  103.         mPath_Circle_m.addCircle(0,0,235, Path.Direction.CCW);  
  104.         //初始化PathMeasure测量path坐标,  
  105.         mMeasure = new PathMeasure();  
  106.         mMeasure.setPath(mPath_Circle,true);  
  107.         mMeasure_h = new PathMeasure();  
  108.         mMeasure_h.setPath(mPath_Circle_h,true);  
  109.         mMeasure_m = new PathMeasure();  
  110.         mMeasure_m.setPath(mPath_Circle_m,true);  
  111.         //获取刻度path  
  112.         mPath_duration = new Path();  
  113.         for (int i = 60; i>0 ;i --){  
  114.             Path path = new Path();  
  115.             float pos [] = new float[2];  
  116.             float tan [] = new float[2];  
  117.             float pos2 [] = new float[2];  
  118.             float tan2 [] = new float[2];  
  119.             float pos3 [] = new float[2];  
  120.             float tan3 [] = new float[2];  
  121.             mMeasure.getPosTan(mMeasure.getLength()*i/60,pos,tan);  
  122.             mMeasure_h.getPosTan(mMeasure_h.getLength()*i/60,pos2,tan2);  
  123.             mMeasure_m.getPosTan(mMeasure_m.getLength()*i/60,pos3,tan3);  
  124.   
  125.             float x = pos[0];  
  126.             float y = pos[1];  
  127.             float x2 = pos2[0];  
  128.             float y2 = pos2[1];  
  129.             float x3 = pos3[0];  
  130.             float y3 = pos3[1];  
  131.             path.moveTo(x , y);  
  132.   
  133.             if(i% 5 ==0){  
  134.                 path.lineTo(x2,y2);  
  135.                 if(t>12){  
  136.                     t = t-12;  
  137.                 }  
  138.                 String time = t++ +"";  
  139.                 Path path_time = new Path();  
  140.                 mMeasure_h.getPosTan(mMeasure_h.getLength()*(i-1)/60,pos2,tan2);  
  141.                 mPaint.getTextPath(time,0,time.length(),(x2- (x2/15)),y2-(y2/15),path_time);  
  142.                 path.close();  
  143.                 path.addPath(path_time);  
  144.             }else{  
  145.                 path.lineTo(x3,y3);  
  146.             }  
  147.   
  148.   
  149.             mPath_duration.addPath(path);  
  150.             clockRunnable = new Runnable() {//里面做的事情就是每隔一秒,刷新一次界面  
  151.                 @Override  
  152.                 public void run() {  
  153.                     //线程中刷新界面  
  154.                     postInvalidate();  
  155.                     mHandler.postDelayed(this1000);  
  156.                 }  
  157.             };  
  158.         }  
  159.   
  160.         mPath_h = new Path();  
  161.         mPath_h.rLineTo(50,30);  
  162.   
  163.         mPath_m = new Path();  
  164.         mPath_m.rLineTo(80,80);  
  165.   
  166.         mPath_s = new Path();  
  167.         mPath_s.rLineTo(130,50);  
  168.     }  
  169.     private int mWidth,mHeight;  
  170.     @Override  
  171.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  172.         super.onSizeChanged(w, h, oldw, oldh);  
  173.         mWidth = w;  
  174.         mHeight = h;  
  175.     }  
  176.   
  177.     @Override  
  178.     protected void onDraw(Canvas canvas) {  
  179.         super.onDraw(canvas);  
  180.         if(!isRunning){  
  181.             isRunning = true;  
  182.             mHandler.postDelayed(clockRunnable,1000);  
  183.         }else{  
  184.             canvas.translate(mWidth/2,mHeight/2);  
  185.   
  186.             canvas.drawPath(mPath_Circle,mPaint);  
  187.             canvas.save();  
  188.             canvas.drawPath(mPath_duration,mPaint_time);  
  189.   
  190.             canvas.drawPoint(0,0,mPaint_time);  
  191.   
  192.             drawClockPoint(canvas);  
  193.         }  
  194.   
  195.   
  196.   
  197.   
  198.   
  199.     }  
  200.     private Calendar cal;  
  201.     private int hour;  
  202.     private int min;  
  203.     private int second;  
  204.     private float hourAngle,minAngle,secAngle;  
  205.     /** 
  206.      * 绘制三个指针 
  207.      * @param canvas 
  208.      */  
  209.     private void drawClockPoint(Canvas canvas) {  
  210.         cal = Calendar.getInstance();  
  211.         hour = cal.get(Calendar.HOUR);//Calendar.HOUR获取的是12小时制,Calendar.HOUR_OF_DAY获取的是24小时制  
  212.         min = cal.get(Calendar.MINUTE);  
  213.         second = cal.get(Calendar.SECOND);  
  214.         //计算时分秒指针各自需要偏移的角度  
  215.         hourAngle = (float)hour / 12 * 360 + (float)min / 60 * (360 / 12);//360/12是指每个数字之间的角度  
  216.         minAngle = (float)min / 60 * 360;  
  217.         secAngle = (float)second / 60 * 360;  
  218.         //下面将时、分、秒指针按照各自的偏移角度进行旋转,每次旋转前要先保存canvas的原始状态  
  219.         canvas.save();  
  220.         canvas.rotate(hourAngle,00);  
  221.         canvas.drawLine(00, mWidth/6, getHeight() / 6 - 65, mPaint_h);//时针长度设置为65  
  222.   
  223.         canvas.restore();  
  224.         canvas.save();  
  225.         canvas.rotate(minAngle,00);  
  226.         canvas.drawLine(00, mWidth/6, getHeight() / 6 - 90 , mPaint_m);//分针长度设置为90  
  227.   
  228.         canvas.restore();  
  229.         canvas.save();  
  230.         canvas.rotate(secAngle,00);  
  231.         canvas.drawLine(00, mWidth/6, getHeight() / 6 - 110 , mPaint_s);//秒针长度设置为110  
  232.   
  233.         canvas.restore();  
  234.     }  
  235. }  


这其实还不算特别复杂的动画,也许你有啥好的想法,可以自己通过Path + 属性动画来实现更好看的效果;

比如星空的效果,比如动态绘制文字 + 路径实现类似ppt中播放的一些特效,比如电子书的自动翻页。

(3)下边再介绍一个知识,就是svg:

svg是什么东西呢?

他的学名叫做可缩放矢量图形,是基于可扩展标记语言标准通用标记语言的子集),用于描述二维矢量图形的一种图形格式

这种格式的图形式可以加载到Android的Path里边。

既然可以加载到Path里边,那么是不是就可以实现更复杂的效果呢,下边看图:


这样的动画自己绘制也可以,只要有整个路径的path,但是如果使用svg的话那么久相当的简单了。


实现流程:

主要是解析svg文件,获取path路径集合,然后绘制的话基本上跟以前的一样。

因为网上有人已经给出工具类了,不是很复杂,就不自己写了。

贴出俩个类:

专门绘制Svg的View以及自定义的参数文件attrs:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. <pre name="code" class="java"><?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   <declare-styleable name="SvgView">  
  4.     <attr name="pathColor" format="color|reference"/>  
  5.     <attr name="pathWidth" format="dimension|reference"/>  
  6.     <attr name="svg" format="reference"/>  
  7.     <attr name="fill" format="boolean"/>  
  8.     <attr name="fillColor" format="color|reference"/>  
  9.     <attr name="naturalColors" format="boolean"/>  
  10.   </declare-styleable>  
  11. </resources>  


 
 

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.duoku.platform.demo.canvaslibrary.attract.view;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Color;  
  8. import android.graphics.Paint;  
  9. import android.graphics.Path;  
  10. import android.util.AttributeSet;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13. import android.view.animation.Interpolator;  
  14.   
  15. import com.duoku.platform.demo.canvaslibrary.R;  
  16. import com.duoku.platform.demo.canvaslibrary.attract.view.Utils.SvgUtils;  
  17. import com.nineoldandroids.animation.Animator;  
  18. import com.nineoldandroids.animation.AnimatorSet;  
  19. import com.nineoldandroids.animation.ObjectAnimator;  
  20.   
  21. import java.util.ArrayList;  
  22. import java.util.List;  
  23.   
  24. /** 
  25.  * SvgView 是一个绘制svg的paths路径的动画控件 
  26.  */  
  27. public class SvgView extends View implements SvgUtils.AnimationStepListener {  
  28.   
  29.     public static final String LOG_TAG = "SvgView";  
  30.     /** 
  31.      * 默认画笔. 
  32.      */  
  33.     private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  34.     /** 
  35.      * U工具类用来解析svg文件 
  36.      */  
  37.     private final SvgUtils svgUtils = new SvgUtils(paint);  
  38.     /** 
  39.      * 整个svg文件的path集合 
  40.      */  
  41.     private List<SvgUtils.SvgPath> paths = new ArrayList<>();  
  42.   
  43.     private final Object mSvgLock = new Object();  
  44.   
  45.     private Thread mLoader;  
  46.     /** 
  47.      * svg文件的资源id 
  48.      */  
  49.     private int svgResourceId;  
  50.     /** 
  51.      * 使用第三方动画框架的动画构造者 
  52.      */  
  53.     private AnimatorBuilder animatorBuilder;  
  54.   
  55.     private AnimatorSetBuilder animatorSetBuilder;  
  56.     /** 
  57.      * 绘制的进度 
  58.      */  
  59.     private float progress = 0f;  
  60.   
  61.     /** 
  62.      * 是否使用svg文件中的自然颜色 
  63.      */  
  64.     private boolean naturalColors;  
  65.     /** 
  66.      * 是否view充满了svg本来的颜色在绘制的过程中 
  67.      */  
  68.     private boolean fillAfter;  
  69.   
  70.     private boolean fill;  
  71.   
  72.     private int fillColor;  
  73.   
  74.     private int width;  
  75.   
  76.     private int height;  
  77.   
  78.     private Bitmap mTempBitmap;  
  79.   
  80.     private Canvas mTempCanvas;  
  81.   
  82.     public SvgView(Context context) {  
  83.         this(context, null);  
  84.     }  
  85.   
  86.     public SvgView(Context context, AttributeSet attrs) {  
  87.         this(context, attrs, 0);  
  88.     }  
  89.   
  90.     public SvgView(Context context, AttributeSet attrs, int defStyle) {  
  91.         super(context, attrs, defStyle);  
  92.         paint.setStyle(Paint.Style.STROKE);  
  93.         getFromAttributes(context, attrs);  
  94.     }  
  95.   
  96.     private void getFromAttributes(Context context, AttributeSet attrs) {  
  97.         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SvgView);  
  98.         try {  
  99.             if (a != null) {  
  100.                 paint.setColor(a.getColor(R.styleable.SvgView_pathColor, 0xff00ff00));  
  101.                 paint.setStrokeWidth(a.getDimensionPixelSize(R.styleable.SvgView_pathWidth, 8));  
  102.                 svgResourceId = a.getResourceId(R.styleable.SvgView_svg, 0);  
  103.                 naturalColors = a.getBoolean(R.styleable.SvgView_naturalColors, false);  
  104.                 fill = a.getBoolean(R.styleable.SvgView_fill,false);  
  105.                 fillColor = a.getColor(R.styleable.SvgView_fillColor,Color.argb(0,0,0,0));  
  106.             }  
  107.         } finally {  
  108.             if (a != null) {  
  109.                 a.recycle();  
  110.             }  
  111.             invalidate();  
  112.         }  
  113.     }  
  114.   
  115.     public void setPaths(final List<Path> paths) {  
  116.         for (Path path : paths) {  
  117.             this.paths.add(new SvgUtils.SvgPath(path, paint));  
  118.         }  
  119.         synchronized (mSvgLock) {  
  120.             updatePathsPhaseLocked();  
  121.         }  
  122.     }  
  123.   
  124.     public void setPath(final Path path) {  
  125.         paths.add(new SvgUtils.SvgPath(path, paint));  
  126.         synchronized (mSvgLock) {  
  127.             updatePathsPhaseLocked();  
  128.         }  
  129.     }  
  130.   
  131.     public void setPercentage(float percentage) {  
  132.         if (percentage < 0.0f || percentage > 1.0f) {  
  133.             throw new IllegalArgumentException("setPercentage not between 0.0f and 1.0f");  
  134.         }  
  135.         progress = percentage;  
  136.         synchronized (mSvgLock) {  
  137.             updatePathsPhaseLocked();  
  138.         }  
  139.         invalidate();  
  140.     }  
  141.   
  142.     private void updatePathsPhaseLocked() {  
  143.         final int count = paths.size();  
  144.         for (int i = 0; i < count; i++) {  
  145.             SvgUtils.SvgPath svgPath = paths.get(i);  
  146.             svgPath.path.reset();  
  147.             svgPath.measure.getSegment(0.0f, svgPath.length * progress, svgPath.path, true);  
  148.             // Required only for Android 4.4 and earlier  
  149.             svgPath.path.rLineTo(0.0f, 0.0f);  
  150.         }  
  151.     }  
  152.   
  153.     @Override  
  154.     protected void onDraw(Canvas canvas) {  
  155.         super.onDraw(canvas);  
  156.   
  157.         if(mTempBitmap==null || (mTempBitmap.getWidth()!=canvas.getWidth()||mTempBitmap.getHeight()!=canvas.getHeight()) )  
  158.         {  
  159.             mTempBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);  
  160.             mTempCanvas = new Canvas(mTempBitmap);  
  161.         }  
  162.   
  163.         mTempBitmap.eraseColor(0);  
  164.         synchronized (mSvgLock) {  
  165.             mTempCanvas.save();  
  166.             mTempCanvas.translate(getPaddingLeft(), getPaddingTop());  
  167.             fill(mTempCanvas);  
  168.             final int count = paths.size();  
  169.             for (int i = 0; i < count; i++) {  
  170.                 final SvgUtils.SvgPath svgPath = paths.get(i);  
  171.                 final Path path = svgPath.path;  
  172.                 final Paint paint1 = naturalColors ? svgPath.paint : paint;  
  173.                 mTempCanvas.drawPath(path, paint1);  
  174.             }  
  175.   
  176.             fillAfter(mTempCanvas);  
  177.   
  178.             mTempCanvas.restore();  
  179.   
  180.             applySolidColor(mTempBitmap);  
  181.   
  182.             canvas.drawBitmap(mTempBitmap,0,0,null);  
  183.         }  
  184.     }  
  185.   
  186.     private void fillAfter(final Canvas canvas) {  
  187.         if (svgResourceId != 0 && fillAfter && Math.abs(progress - 1f) < 0.00000001) {  
  188.             svgUtils.drawSvgAfter(canvas, width, height);  
  189.         }  
  190.     }  
  191.   
  192.     private void fill(final Canvas canvas) {  
  193.         if (svgResourceId != 0 && fill) {  
  194.             svgUtils.drawSvgAfter(canvas, width, height);  
  195.         }  
  196.     }  
  197.   
  198.     private void applySolidColor(final Bitmap bitmap) {  
  199.         if(fill && fillColor!=Color.argb(0,0,0,0) )  
  200.             if (bitmap != null) {  
  201.                 for(int x=0;x<bitmap.getWidth();x++)  
  202.                 {  
  203.                     for(int y=0;y<bitmap.getHeight();y++)  
  204.                     {  
  205.                         int argb = bitmap.getPixel(x,y);  
  206.                         int alpha = Color.alpha(argb);  
  207.                         if(alpha!=0)  
  208.                         {  
  209.                             int red = Color.red(fillColor);  
  210.                             int green = Color.green(fillColor);  
  211.                             int blue =  Color.blue(fillColor);  
  212.                             argb = Color.argb(alpha,red,green,blue);  
  213.                             bitmap.setPixel(x,y,argb);  
  214.                         }  
  215.                     }  
  216.                 }  
  217.             }  
  218.     }  
  219.   
  220.     @Override  
  221.     protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {  
  222.         super.onSizeChanged(w, h, oldw, oldh);  
  223.   
  224.         if (mLoader != null) {  
  225.             try {  
  226.                 mLoader.join();  
  227.             } catch (InterruptedException e) {  
  228.                 Log.e(LOG_TAG, "Unexpected error", e);  
  229.             }  
  230.         }  
  231.         if (svgResourceId != 0) {  
  232.             mLoader = new Thread(new Runnable() {  
  233.                 @Override  
  234.                 public void run() {  
  235.   
  236.                     svgUtils.load(getContext(), svgResourceId);  
  237.   
  238.                     synchronized (mSvgLock) {  
  239.                         width = w - getPaddingLeft() - getPaddingRight();  
  240.                         height = h - getPaddingTop() - getPaddingBottom();  
  241.                         paths = svgUtils.getPathsForViewport(width, height);  
  242.                         updatePathsPhaseLocked();  
  243.                     }  
  244.                 }  
  245.             }, "SVG Loader");  
  246.             mLoader.start();  
  247.         }  
  248.     }  
  249.   
  250.     @Override  
  251.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  252.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  253.         if (svgResourceId != 0) {  
  254.             int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  255.             int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  256.             setMeasuredDimension(widthSize, heightSize);  
  257.             return;  
  258.         }  
  259.   
  260.         int desiredWidth = 0;  
  261.         int desiredHeight = 0;  
  262.         final float strokeWidth = paint.getStrokeWidth() / 2;  
  263.         for (SvgUtils.SvgPath path : paths) {  
  264.             desiredWidth += path.bounds.left + path.bounds.width() + strokeWidth;  
  265.             desiredHeight += path.bounds.top + path.bounds.height() + strokeWidth;  
  266.         }  
  267.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  268.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  269.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  270.         int heightMode = MeasureSpec.getMode(widthMeasureSpec);  
  271.   
  272.         int measuredWidth, measuredHeight;  
  273.   
  274.         if (widthMode == MeasureSpec.AT_MOST) {  
  275.             measuredWidth = desiredWidth;  
  276.         } else {  
  277.             measuredWidth = widthSize;  
  278.         }  
  279.   
  280.         if (heightMode == MeasureSpec.AT_MOST) {  
  281.             measuredHeight = desiredHeight;  
  282.         } else {  
  283.             measuredHeight = heightSize;  
  284.         }  
  285.   
  286.         setMeasuredDimension(measuredWidth, measuredHeight);  
  287.     }  
  288.   
  289.     public void setFillAfter(final boolean fillAfter) {  
  290.         this.fillAfter = fillAfter;  
  291.     }  
  292.   
  293.     public void setFill(final boolean fill) {  
  294.         this.fill = fill;  
  295.     }  
  296.   
  297.     public void setFillColor(final int color){  
  298.         this.fillColor=color;  
  299.     }  
  300.   
  301.     public void useNaturalColors() {  
  302.         naturalColors = true;  
  303.     }  
  304.   
  305.     public AnimatorBuilder getPathAnimator() {  
  306.         if (animatorBuilder == null) {  
  307.             animatorBuilder = new AnimatorBuilder(this);  
  308.         }  
  309.         return animatorBuilder;  
  310.     }  
  311.   
  312.     public AnimatorSetBuilder getSequentialPathAnimator() {  
  313.         if (animatorSetBuilder == null) {  
  314.             animatorSetBuilder = new AnimatorSetBuilder(this);  
  315.         }  
  316.         return animatorSetBuilder;  
  317.     }  
  318.   
  319.     public int getPathColor() {  
  320.         return paint.getColor();  
  321.     }  
  322.   
  323.     public void setPathColor(final int color) {  
  324.         paint.setColor(color);  
  325.     }  
  326.   
  327.     public float getPathWidth() {  
  328.         return paint.getStrokeWidth();  
  329.     }  
  330.   
  331.     public void setPathWidth(final float width) {  
  332.         paint.setStrokeWidth(width);  
  333.     }  
  334.   
  335.     public int getSvgResource() {  
  336.         return svgResourceId;  
  337.     }  
  338.   
  339.   
  340.     public void setSvgResource(int svgResource) {  
  341.         svgResourceId = svgResource;  
  342.     }  
  343.   
  344.     public static class AnimatorBuilder {  
  345.   
  346.         private int duration = 350;  
  347.   
  348.         private Interpolator interpolator;  
  349.   
  350.         private int delay = 0;  
  351.   
  352.         private final ObjectAnimator anim;  
  353.   
  354.         private ListenerStart listenerStart;  
  355.   
  356.         private ListenerEnd animationEnd;  
  357.   
  358.         private SvgViewAnimatorListener SvgViewAnimatorListener;  
  359.   
  360.   
  361.         public AnimatorBuilder(final SvgView SvgView) {  
  362.             anim = ObjectAnimator.ofFloat(SvgView, "percentage"0.0f, 1.0f);  
  363.         }  
  364.   
  365.         public AnimatorBuilder duration(final int duration) {  
  366.             this.duration = duration;  
  367.             return this;  
  368.         }  
  369.   
  370.         public AnimatorBuilder interpolator(final Interpolator interpolator) {  
  371.             this.interpolator = interpolator;  
  372.             return this;  
  373.         }  
  374.   
  375.         public AnimatorBuilder delay(final int delay) {  
  376.             this.delay = delay;  
  377.             return this;  
  378.         }  
  379.   
  380.         public AnimatorBuilder listenerStart(final ListenerStart listenerStart) {  
  381.             this.listenerStart = listenerStart;  
  382.             if (SvgViewAnimatorListener == null) {  
  383.                 SvgViewAnimatorListener = new SvgViewAnimatorListener();  
  384.                 anim.addListener(SvgViewAnimatorListener);  
  385.             }  
  386.             return this;  
  387.         }  
  388.   
  389.         public AnimatorBuilder listenerEnd(final ListenerEnd animationEnd) {  
  390.             this.animationEnd = animationEnd;  
  391.             if (SvgViewAnimatorListener == null) {  
  392.                 SvgViewAnimatorListener = new SvgViewAnimatorListener();  
  393.                 anim.addListener(SvgViewAnimatorListener);  
  394.             }  
  395.             return this;  
  396.         }  
  397.   
  398.         /** 
  399.          * 开始动画. 
  400.          */  
  401.         public void start() {  
  402.             anim.setDuration(duration);  
  403.             anim.setInterpolator(interpolator);  
  404.             anim.setStartDelay(delay);  
  405.             anim.start();  
  406.         }  
  407.   
  408.         private class SvgViewAnimatorListener implements Animator.AnimatorListener {  
  409.   
  410.             @Override  
  411.             public void onAnimationStart(Animator animation) {  
  412.                 if (listenerStart != null)  
  413.                     listenerStart.onAnimationStart();  
  414.             }  
  415.   
  416.             @Override  
  417.             public void onAnimationEnd(Animator animation) {  
  418.                 if (animationEnd != null)  
  419.                     animationEnd.onAnimationEnd();  
  420.             }  
  421.   
  422.             @Override  
  423.             public void onAnimationCancel(Animator animation) {  
  424.   
  425.             }  
  426.   
  427.             @Override  
  428.             public void onAnimationRepeat(Animator animation) {  
  429.   
  430.             }  
  431.         }  
  432.   
  433.         public interface ListenerStart {  
  434.   
  435.             void onAnimationStart();  
  436.         }  
  437.   
  438.   
  439.         public interface ListenerEnd {  
  440.   
  441.             void onAnimationEnd();  
  442.         }  
  443.     }  
  444.   
  445.     @Override  
  446.     public void onAnimationStep() {  
  447.         invalidate();  
  448.     }  
  449.   
  450.     /** 
  451.      * 动画构建的类 
  452.      */  
  453.     public static class AnimatorSetBuilder {  
  454.   
  455.         private int duration = 1000;  
  456.   
  457.         private Interpolator interpolator;  
  458.   
  459.         private int delay = 0;  
  460.         private final List<Animator> animators = new ArrayList<>();  
  461.         /** 
  462.          * 动画开始监听 
  463.          */  
  464.         private AnimatorBuilder.ListenerStart listenerStart;  
  465.         /** 
  466.          * 动画结束监听 
  467.          */  
  468.         private AnimatorBuilder.ListenerEnd animationEnd;  
  469.         /** 
  470.          * 动画监听器. 
  471.          */  
  472.         private AnimatorSetBuilder.SvgViewAnimatorListener SvgViewAnimatorListener;  
  473.         /** 
  474.          * 动画path的顺序         */  
  475.         private AnimatorSet animatorSet = new AnimatorSet();  
  476.         /** 
  477.          * 动画路径的列表 
  478.          */  
  479.         private List<SvgUtils.SvgPath> paths;  
  480.   
  481.   
  482.         public AnimatorSetBuilder(final SvgView SvgView) {  
  483.             paths = SvgView.paths;  
  484.             for (SvgUtils.SvgPath path : paths) {  
  485.                 path.setAnimationStepListener(SvgView);  
  486.                 ObjectAnimator animation = ObjectAnimator.ofFloat(path, "length"0.0f, path.getLength());  
  487.                 animators.add(animation);  
  488.             }  
  489.             animatorSet.playSequentially(animators);  
  490.         }  
  491.   
  492.         /** 
  493.          *设置动画时长 
  494.          */  
  495.         public AnimatorSetBuilder duration(final int duration) {  
  496.             this.duration = duration / paths.size();  
  497.             return this;  
  498.         }  
  499.   
  500.         /** 
  501.          * 设置插值器. 
  502.          */  
  503.         public AnimatorSetBuilder interpolator(final Interpolator interpolator) {  
  504.             this.interpolator = interpolator;  
  505.             return this;  
  506.         }  
  507.   
  508.         /** 
  509.          *设置动画延迟 
  510.          */  
  511.         public AnimatorSetBuilder delay(final int delay) {  
  512.             this.delay = delay;  
  513.             return this;  
  514.         }  
  515.   
  516.         /** 
  517.          *设置动画开始监听 
  518.          */  
  519.         public AnimatorSetBuilder listenerStart(final AnimatorBuilder.ListenerStart listenerStart) {  
  520.             this.listenerStart = listenerStart;  
  521.             if (SvgViewAnimatorListener == null) {  
  522.                 SvgViewAnimatorListener = new SvgViewAnimatorListener();  
  523.                 animatorSet.addListener(SvgViewAnimatorListener);  
  524.             }  
  525.             return this;  
  526.         }  
  527.   
  528.         /** 
  529.          * 设置动画结束的监听器 
  530.          */  
  531.         public AnimatorSetBuilder listenerEnd(final AnimatorBuilder.ListenerEnd animationEnd) {  
  532.             this.animationEnd = animationEnd;  
  533.             if (SvgViewAnimatorListener == null) {  
  534.                 SvgViewAnimatorListener = new SvgViewAnimatorListener();  
  535.                 animatorSet.addListener(SvgViewAnimatorListener);  
  536.             }  
  537.             return this;  
  538.         }  
  539.   
  540.         /** 
  541.          * 开始动画 
  542.          */  
  543.         public void start() {  
  544.             resetAllPaths();  
  545.             animatorSet.cancel();  
  546.             animatorSet.setDuration(duration);  
  547.             animatorSet.setInterpolator(interpolator);  
  548.             animatorSet.setStartDelay(delay);  
  549.             animatorSet.start();  
  550.         }  
  551.   
  552.         /** 
  553.          *重置所有path 
  554.          */  
  555.         private void resetAllPaths() {  
  556.             for (SvgUtils.SvgPath path : paths) {  
  557.                 path.setLength(0);  
  558.             }  
  559.         }  
  560.   
  561.         /** 
  562.          * svg动画回调监听接口 
  563.          */  
  564.         private class SvgViewAnimatorListener implements Animator.AnimatorListener {  
  565.   
  566.             @Override  
  567.             public void onAnimationStart(Animator animation) {  
  568.                 if (listenerStart != null)  
  569.                     listenerStart.onAnimationStart();  
  570.             }  
  571.   
  572.             @Override  
  573.             public void onAnimationEnd(Animator animation) {  
  574.                 if (animationEnd != null)  
  575.                     animationEnd.onAnimationEnd();  
  576.             }  
  577.   
  578.             @Override  
  579.             public void onAnimationCancel(Animator animation) {  
  580.   
  581.             }  
  582.   
  583.             @Override  
  584.             public void onAnimationRepeat(Animator animation) {  
  585.   
  586.             }  
  587.         }  
  588.     }  
  589. }  

Svg解析工具类:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.duoku.platform.demo.canvaslibrary.attract.view.Utils;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Canvas;  
  5. import android.graphics.Matrix;  
  6. import android.graphics.Paint;  
  7. import android.graphics.Path;  
  8. import android.graphics.PathMeasure;  
  9. import android.graphics.Rect;  
  10. import android.graphics.RectF;  
  11. import android.graphics.Region;  
  12. import android.util.Log;  
  13.   
  14. import com.caverock.androidsvg.PreserveAspectRatio;  
  15. import com.caverock.androidsvg.SVG;  
  16. import com.caverock.androidsvg.SVGParseException;  
  17.   
  18. import java.util.ArrayList;  
  19. import java.util.List;  
  20.   
  21. /** 
  22.  * Util class to init and get paths from svg. 
  23.  */  
  24. public class SvgUtils {  
  25.     /** 
  26.      * It is for logging purposes. 
  27.      */  
  28.     private static final String LOG_TAG = "SVGUtils";  
  29.     /** 
  30.      * All the paths with their attributes from the svg. 
  31.      */  
  32.     private final List<SvgPath> mPaths = new ArrayList<>();  
  33.     /** 
  34.      * The paint provided from the view. 
  35.      */  
  36.     private final Paint mSourcePaint;  
  37.     /** 
  38.      * The init svg. 
  39.      */  
  40.     private SVG mSvg;  
  41.   
  42.     /** 
  43.      * Init the SVGUtils with a paint for coloring. 
  44.      * 
  45.      * @param sourcePaint - the paint for the coloring. 
  46.      */  
  47.     public SvgUtils(final Paint sourcePaint) {  
  48.         mSourcePaint = sourcePaint;  
  49.     }  
  50.   
  51.     /** 
  52.      * Loading the svg from the resources. 
  53.      * 
  54.      * @param context     Context object to get the resources. 
  55.      * @param svgResource int resource id of the svg. 
  56.      */  
  57.     public void load(Context context, int svgResource) {  
  58.         if (mSvg != null)   
  59.             return;  
  60.         try {  
  61.             mSvg = SVG.getFromResource(context, svgResource);  
  62.             mSvg.setDocumentPreserveAspectRatio(PreserveAspectRatio.UNSCALED);  
  63.         } catch (SVGParseException e) {  
  64.             Log.e(LOG_TAG, "Could not load specified SVG resource", e);  
  65.         }  
  66.     }  
  67.   
  68.     /** 
  69.      * Draw the svg to the canvas. 
  70.      * 
  71.      * @param canvas The canvas to be drawn. 
  72.      * @param width  The width of the canvas. 
  73.      * @param height The height of the canvas. 
  74.      */  
  75.     public void drawSvgAfter(final Canvas canvas, final int width, final int height) {  
  76.         final float strokeWidth = mSourcePaint.getStrokeWidth();  
  77.         rescaleCanvas(width, height, strokeWidth, canvas);  
  78.     }  
  79.   
  80.     /** 
  81.      * Render the svg to canvas and catch all the paths while rendering. 
  82.      * 
  83.      * @param width  - the width to scale down the view to, 
  84.      * @param height - the height to scale down the view to, 
  85.      * @return All the paths from the svg. 
  86.      */  
  87.     public List<SvgPath> getPathsForViewport(final int width, final int height) {  
  88.         final float strokeWidth = mSourcePaint.getStrokeWidth();  
  89.         Canvas canvas = new Canvas() {  
  90.             private final Matrix mMatrix = new Matrix();  
  91.   
  92.             @Override  
  93.             public int getWidth() {  
  94.                 return width;  
  95.             }  
  96.   
  97.             @Override  
  98.             public int getHeight() {  
  99.                 return height;  
  100.             }  
  101.   
  102.             @Override  
  103.             public void drawPath(Path path, Paint paint) {  
  104.                 Path dst = new Path();  
  105.   
  106.                 //noinspection deprecation  
  107.                 getMatrix(mMatrix);  
  108.                 path.transform(mMatrix, dst);  
  109.                 paint.setAntiAlias(true);  
  110.                 paint.setStyle(Paint.Style.STROKE);  
  111.                 paint.setStrokeWidth(strokeWidth);  
  112.                 mPaths.add(new SvgPath(dst, paint));  
  113.             }  
  114.         };  
  115.   
  116.         rescaleCanvas(width, height, strokeWidth, canvas);  
  117.   
  118.         return mPaths;  
  119.     }  
  120.   
  121.     /** 
  122.      * Rescale the canvas with specific width and height. 
  123.      * 
  124.      * @param width       The width of the canvas. 
  125.      * @param height      The height of the canvas. 
  126.      * @param strokeWidth Width of the path to add to scaling. 
  127.      * @param canvas      The canvas to be drawn. 
  128.      */  
  129.     private void rescaleCanvas(int width, int height, float strokeWidth, Canvas canvas) {  
  130.         if (mSvg == null)   
  131.             return;  
  132.         final RectF viewBox = mSvg.getDocumentViewBox();  
  133.   
  134.         final float scale = Math.min(width  
  135.                         / (viewBox.width() + strokeWidth),  
  136.                 height / (viewBox.height() + strokeWidth));  
  137.   
  138.         canvas.translate((width - viewBox.width() * scale) / 2.0f,  
  139.                 (height - viewBox.height() * scale) / 2.0f);  
  140.         canvas.scale(scale, scale);  
  141.   
  142.         mSvg.renderToCanvas(canvas);  
  143.     }  
  144.   
  145.     /** 
  146.      * Path with bounds for scalling , length and paint. 
  147.      */  
  148.     public static class SvgPath {  
  149.   
  150.         /** 
  151.          * Region of the path. 
  152.          */  
  153.         private static final Region REGION = new Region();  
  154.         /** 
  155.          * This is done for clipping the bounds of the path. 
  156.          */  
  157.         private static final Region MAX_CLIP =  
  158.                 new Region(Integer.MIN_VALUE, Integer.MIN_VALUE,  
  159.                         Integer.MAX_VALUE, Integer.MAX_VALUE);  
  160.         /** 
  161.          * The path itself. 
  162.          */  
  163.         public final Path path;  
  164.         /** 
  165.          * The paint to be drawn later. 
  166.          */  
  167.         public final Paint paint;  
  168.         /** 
  169.          * The length of the path. 
  170.          */  
  171.         public float length;  
  172.         /** 
  173.          * Listener to notify that an animation step has happened. 
  174.          */  
  175.         AnimationStepListener animationStepListener;  
  176.         /** 
  177.          * The bounds of the path. 
  178.          */  
  179.         public final Rect bounds;  
  180.         /** 
  181.          * The measure of the path, we can use it later to get segment of it. 
  182.          */  
  183.         public final PathMeasure measure;  
  184.   
  185.         /** 
  186.          * Constructor to add the path and the paint. 
  187.          * 
  188.          * @param path  The path that comes from the rendered svg. 
  189.          * @param paint The result paint. 
  190.          */  
  191.         public SvgPath(Path path, Paint paint) {  
  192.             this.path = path;  
  193.             this.paint = paint;  
  194.   
  195.             measure = new PathMeasure(path, false);  
  196.             this.length = measure.getLength();  
  197.   
  198.             REGION.setPath(path, MAX_CLIP);  
  199.             bounds = REGION.getBounds();  
  200.         }  
  201.   
  202.         /** 
  203.          * Sets the animation step listener. 
  204.          * 
  205.          * @param animationStepListener AnimationStepListener. 
  206.          */  
  207.         public void setAnimationStepListener(AnimationStepListener animationStepListener) {  
  208.             this.animationStepListener = animationStepListener;  
  209.         }  
  210.   
  211.         /** 
  212.          * Sets the length of the path. 
  213.          * 
  214.          * @param length The length to be set. 
  215.          */  
  216.         public void setLength(float length) {  
  217.             path.reset();  
  218.             measure.getSegment(0.0f, length, path, true);  
  219.             path.rLineTo(0.0f, 0.0f);  
  220.   
  221.             if (animationStepListener != null) {  
  222.                 animationStepListener.onAnimationStep();  
  223.             }  
  224.         }  
  225.   
  226.         /** 
  227.          * @return The length of the path. 
  228.          */  
  229.         public float getLength() {  
  230.             return length;  
  231.         }  
  232.     }  
  233.   
  234.     public interface AnimationStepListener {  
  235.   
  236.         /** 
  237.          * Called when an animation step happens. 
  238.          */  
  239.         void onAnimationStep();  
  240.     }  
  241. }  

说一下使用的流程吧。

(1)在布局文件中添加自定义的SvgView,并指定其svg文件

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. <com.duoku.platform.demo.canvaslibrary.attract.view.SvgView  
  2.      android:id="@+id/svg_view"  
  3.      android:layout_width="match_parent"  
  4.      android:layout_height="match_parent"  
  5.     app:svg="@raw/pig"  
  6.  />  
(2)在代码中开启动画效果即可。
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="white-space:pre">    </span>SvgView mSvgView;  
  2.         mSvgView = (SvgView)findViewById(R.id.svg_view);  
  3.         mSvgView.getPathAnimator().duration(5000)  
  4.                 .start();  

如果你还想了解更多的关于svg的资料的话那么希望自己百度。或是等我整理下关系学习Svg的知识,然后会在进行记录。

Demo等以后会上传。

GitHub地址;https://github.com/tianxia--/CanvasSample

相关文章
|
6月前
|
测试技术 Android开发
Android按钮防抖动,避免发送多次请求
Android按钮防抖动,避免发送多次请求
113 0
|
5月前
|
XML IDE 开发工具
【Android UI】自定义带按钮的标题栏
【Android UI】自定义带按钮的标题栏
61 7
【Android UI】自定义带按钮的标题栏
|
5月前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
5月前
|
存储 Android开发
安卓app,MediaPlayer播放本地音频 | 按钮控制播放和停止
在Jetpack Compose中,不直接操作原生Android组件如`Button`和`MediaPlayer`,而是使用Compose UI构建器定义界面并结合ViewModel管理音频播放逻辑。以下示例展示如何播放本地音频并用按钮控制播放/停止:创建一个`AudioPlayerViewModel`管理`MediaPlayer`实例和播放状态,然后在Compose UI中使用`Button`根据`isPlaying`状态控制播放。记得在`MainActivity`设置Compose UI,并处理相关依赖和权限。
|
5月前
|
XML Java Android开发
15. 【Android教程】按钮 Button/ImageButton
15. 【Android教程】按钮 Button/ImageButton
73 2
|
6月前
|
Android开发
Android通讯录开发之通讯录联系人搜索功能最新实现
Android通讯录开发之通讯录联系人搜索功能最新实现
|
6月前
|
调度 Android开发
Android9底部导航栏出现空白按钮问题分析
Android9底部导航栏出现空白按钮问题分析
41 0
|
6月前
|
XML Java Android开发
Android每点击一次按钮就添加一条数据
Android每点击一次按钮就添加一条数据
58 1
|
Android开发 前端开发
Android初级进阶之自定义时钟(二)
在上一篇Android初级进阶之自定义时钟(一)里面已经完成了时钟的静态绘制,是这个样子的: 现在我们要做的是在上面的基础上继续完善,达到以下的效果。
1136 0
|
Java Android开发 前端开发
Android初级进阶之自定义时钟(一)
前言 一直以为仪表盘,时钟之类的非常的难,不敢去触摸这个领域。真正接触了才发现不过如此。 技术要点 1. cancvas 1. Cancvas.
1172 0