Android实现炫酷SVG动画效果

svg是目前十分流行的图像文件格式了,svg严格来说应该是一种开放标准的矢量图形语言,使用svg格式我们可以直接用代码来描绘图像,可以用任何文字处理工具打开svg图像,通过改变部分代码来使图像具有交互功能,并可以随时插入到HTML中通过浏览器(如火狐浏览器)来观看。使用svg格式可让你设计激动人心的、高分辨率的Web图形页面。


svg格式具备目前网络流行的jpg和png等格式无法具备的优势:可以任意放大图形显示,但绝不会以牺牲图像质量为代价;可在svg图像中保留可编辑和可搜寻的状态;平均来讲,svg文件比其它格式的图像文件要小很多,因而下载也很快。


我们先来看几张Android上使用SVG的效果图:

                                        


从上面的图片看到,如果我们自己来实现这样的特效,非常的麻烦,不过接下来给大家介绍一个开源控件,就可以配合SVG实现这些效果。

首先我们来了解SVG文件的格式,举个例子:

[html]  view plain copy
  1. <svg xmlns="http://www.w3.org/2000/svg" id="svg" class="svg" viewBox="0 0 960 480" preserveAspectRatio="xMinYMin meet">  
  2.         <path fill="#B4BEC8" stroke="#B4BEC8" stroke-width="2px" stroke-miterlimit="10" d="M570.14 440.2l-29.165-28.99c-7.103-8.5-6.152-36.718-6.02-40.665H425.048c.133 3.947 1.082 32.164-6.018 40.666l-29.166 28.99c-1.237 1.404-1.712 2.505-1.623 3.37h-.054c.76 7.727 6.664 6.332 13.607 6.332H558.01c6.696 0 12.412 1.27 13.493-5.56.58-.953.274-2.282-1.364-4.14z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 474.095184326172px, 474.095184326172px; stroke-dashoffset: 0px;"></path>  
  3.   
  4.         <path fill="#C8D2DC" stroke="#C8D2DC" stroke-width="2px" stroke-miterlimit="10" d="M727.488 355.125c0 8.514-6.597 15.42-14.738 15.42h-465.5c-8.14 0-14.74-6.906-14.74-15.42V45.42c0-8.517 6.6-15.42 14.74-15.42h465.5c8.142 0 14.738 6.903 14.738 15.42v309.705z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 1645.18310546875px, 1645.18310546875px; stroke-dashoffset: 0px;"></path>  
  5.   
  6.         <path fill="#fff" stroke="#C8D2DC" stroke-width="2px" stroke-miterlimit="10" d="M489.01 343.713c-.042-4.223 3.447-6.254 3.605-6.352-1.963-2.866-5.018-3.263-6.102-3.31-2.602-.26-5.074 1.53-6.39 1.53s-3.356-1.49-5.506-1.448c-2.836.04-5.445 1.645-6.907 4.182-2.942 5.11-.75 12.672 2.116 16.814 1.4 2.02 3.072 4.305 5.268 4.22 2.114-.08 2.913-1.362 5.467-1.362 2.556 0 3.274 1.363 5.51 1.322 2.273-.04 3.716-2.064 5.105-4.098 1.61-2.35 2.273-4.63 2.313-4.748-.05-.02-4.434-1.7-4.48-6.75M484.807 331.31c1.168-1.41 1.953-3.37 1.738-5.327-1.68.068-3.713 1.12-4.916 2.53-1.08 1.247-2.027 3.245-1.77 5.16 1.87.143 3.784-.95 4.947-2.362" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 115.244583129883px, 115.244583129883px; stroke-dashoffset: 0px;"></path>  
  7.   
  8.         <path fill="#3C4650" stroke="#3C4650" stroke-width="2px" stroke-miterlimit="10" d="M727.488 315.527V45.982c0-8.828-6.597-15.982-14.738-15.982h-465.5c-8.14 0-14.74 7.155-14.74 15.982v269.545H727.49z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 1547.85571289063px, 1547.85571289063px; stroke-dashoffset: 0px;"></path>  
  9.   
  10.         <path fill="#141E28" stroke="#141E28" stroke-width="2px" stroke-miterlimit="10" d="M251.2 48.887h457.205v245.52H251.2z" style="fill-opacity: 1; stroke-opacity: 0; -webkit-transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; transition: fill-opacity 1s ease-in-out, stroke-opacity 1s ease-in-out; stroke-dasharray: 1405.44995117188px, 1405.44995117188px; stroke-dashoffset: 0px;"></path>  
  11.       </svg>  

上面的代码很复杂,如果说它们是代码的话,但是我们可以注意到,这种书写方式,有点类似于html,都是使用标签

使用最多的标签是path,也就是路径


有的人也会想到,要实现照片上的动态效果,我们可以使用Android自带的绘图类和函数,复杂的曲线路径,我们可以使用path这个类来制定

那会不会SVG里面的path,其实也是这样,那么我们就可以将SVG中的path,对应到android,然后绘制出来就好了。


SVG里面还有各种标签:

包括line直线,circle圆,rect矩形,eliipse椭圆,polygon多边形,等等


这些只要我们又一个SVG文件,都可以将其转换成java代码

作为一个程序员,我们当然不能手动去做这个工作,那就涉及两个问题,一个是SVG的解析,一个是解析后的绘制


幸运的是,已经有人完成了这个工作,并且在Github上开源 https://github.com/geftimov/android-pathview


这篇文章将作为一个简单的例子,来使用上面的开源控件

为了解析SVG,我们需要将一个androidsvg.jar包含进我们的lib


下面我们来看这个控件的简单使用,作为一个自定义控件,我们只需要在布局文件里面添加

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:orientation="vertical"  
  5.     android:background="#ff0000"  
  6.     android:layout_width="fill_parent"  
  7.     android:layout_height="fill_parent">  
  8.   
  9.     <com.example.kaiyicky.myapplication.PathView  
  10.         xmlns:app="http://schemas.android.com/apk/res-auto"  
  11.         android:id="@+id/pathView"  
  12.         android:layout_width="match_parent"  
  13.         android:layout_height="match_parent"  
  14.         app:pathColor="@android:color/white"  
  15.         app:svg="@raw/ironman_white"  
  16.         app:pathWidth="5"/>  
  17. </LinearLayout>  

其实app:svg指定了一个SVG文件,我们可以把这个文章放在raw目录下面


然后来看Activity里面:

[java]  view plain copy
  1. public class MainActivity extends FragmentActivity {  
  2.       
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.   
  8.         final PathView pathView = (PathView) findViewById(R.id.pathView);  
  9. //        final Path path = makeConvexArrow(50, 100);  
  10. //        pathView.setPath(path);  
  11.         pathView.setFillAfter(true);  
  12.         pathView.useNaturalColors();  
  13.         pathView.setOnClickListener(new View.OnClickListener() {  
  14.             @Override  
  15.             public void onClick(View v) {  
  16.                 pathView.getPathAnimator().  
  17.                         delay(100).  
  18.                         duration(1500).  
  19.                         interpolator(new AccelerateDecelerateInterpolator()).  
  20.                         start();  
  21.             }  
  22.         });  
  23.     }  
  24.   
  25.     private Path makeConvexArrow(float length, float height) {  
  26.         final Path path = new Path();  
  27.         path.moveTo(0.0f, 0.0f);  
  28.         path.lineTo(length / 4f, 0.0f);  
  29.         path.lineTo(length, height / 2.0f);  
  30.         path.lineTo(length / 4f, height);  
  31.         path.lineTo(0.0f, height);  
  32.         path.lineTo(length * 3f / 4f, height / 2f);  
  33.         path.lineTo(0.0f, 0.0f);  
  34.         path.close();  
  35.         return path;  
  36.     }  
  37.   
  38. }  

看到注释的部分,调用了makeConvexArraw()方法,如果我们没有在xml文件里面指定svg文件,我们也可以在代码中手动指定绘制的路径

让代码跑起来,点击屏幕,于是就实现了以下效果:



就是这么简单,至于这么制作SVG文件,大家可以找美工帮忙,使用ps和ai,可以将图片转换成SVG


最后是源码下载地址:http://download.csdn.net/detail/kangaroo835127729/9016349


整个过程有两个类,一个是SVG解析工具类:

[java]  view plain copy
  1. /** 
  2.  * Util class to init and get paths from svg. 
  3.  */  
  4. public class SvgUtils {  
  5.     /** 
  6.      * It is for logging purposes. 
  7.      */  
  8.     private static final String LOG_TAG = "SVGUtils";  
  9.     /** 
  10.      * All the paths with their attributes from the svg. 
  11.      */  
  12.     private final List<SvgPath> mPaths = new ArrayList<SvgPath>();  
  13.     /** 
  14.      * The paint provided from the view. 
  15.      */  
  16.     private final Paint mSourcePaint;  
  17.     /** 
  18.      * The init svg. 
  19.      */  
  20.     private SVG mSvg;  
  21.   
  22.     /** 
  23.      * Init the SVGUtils with a paint for coloring. 
  24.      * 
  25.      * @param sourcePaint - the paint for the coloring. 
  26.      */  
  27.     public SvgUtils(final Paint sourcePaint) {  
  28.         mSourcePaint = sourcePaint;  
  29.     }  
  30.   
  31.     /** 
  32.      * Loading the svg from the resources. 
  33.      * 
  34.      * @param context     Context object to get the resources. 
  35.      * @param svgResource int resource id of the svg. 
  36.      */  
  37.     public void load(Context context, int svgResource) {  
  38.         if (mSvg != nullreturn;  
  39.         try {  
  40.             mSvg = SVG.getFromResource(context, svgResource);  
  41.             mSvg.setDocumentPreserveAspectRatio(PreserveAspectRatio.UNSCALED);  
  42.         } catch (SVGParseException e) {  
  43.             Log.e(LOG_TAG, "Could not load specified SVG resource", e);  
  44.         }  
  45.     }  
  46.   
  47.     /** 
  48.      * Draw the svg to the canvas. 
  49.      * 
  50.      * @param canvas The canvas to be drawn. 
  51.      * @param width  The width of the canvas. 
  52.      * @param height The height of the canvas. 
  53.      */  
  54.     public void drawSvgAfter(final Canvas canvas, final int width, final int height) {  
  55.         final float strokeWidth = mSourcePaint.getStrokeWidth();  
  56.         rescaleCanvas(width, height, strokeWidth, canvas);  
  57.     }  
  58.   
  59.     /** 
  60.      * Render the svg to canvas and catch all the paths while rendering. 
  61.      * 
  62.      * @param width  - the width to scale down the view to, 
  63.      * @param height - the height to scale down the view to, 
  64.      * @return All the paths from the svg. 
  65.      */  
  66.     public List<SvgPath> getPathsForViewport(final int width, final int height) {  
  67.         final float strokeWidth = mSourcePaint.getStrokeWidth();  
  68.         Canvas canvas = new Canvas() {  
  69.             private final Matrix mMatrix = new Matrix();  
  70.   
  71.             @Override  
  72.             public int getWidth() {  
  73.                 return width;  
  74.             }  
  75.   
  76.             @Override  
  77.             public int getHeight() {  
  78.                 return height;  
  79.             }  
  80.   
  81.             @Override  
  82.             public void drawPath(Path path, Paint paint) {  
  83.                 Path dst = new Path();  
  84.   
  85.                 //noinspection deprecation  
  86.                 getMatrix(mMatrix);  
  87.                 path.transform(mMatrix, dst);  
  88.                 paint.setAntiAlias(true);  
  89.                 paint.setStyle(Paint.Style.STROKE);  
  90.                 paint.setStrokeWidth(strokeWidth);  
  91.                 mPaths.add(new SvgPath(dst, paint));  
  92.             }  
  93.         };  
  94.   
  95.         rescaleCanvas(width, height, strokeWidth, canvas);  
  96.   
  97.         return mPaths;  
  98.     }  
  99.   
  100.     /** 
  101.      * Rescale the canvas with specific width and height. 
  102.      * 
  103.      * @param width       The width of the canvas. 
  104.      * @param height      The height of the canvas. 
  105.      * @param strokeWidth Width of the path to add to scaling. 
  106.      * @param canvas      The canvas to be drawn. 
  107.      */  
  108.     private void rescaleCanvas(int width, int height, float strokeWidth, Canvas canvas) {  
  109.         final RectF viewBox = mSvg.getDocumentViewBox();  
  110.   
  111.         final float scale = Math.min(width  
  112.                         / (viewBox.width() + strokeWidth),  
  113.                 height / (viewBox.height() + strokeWidth));  
  114.   
  115.         canvas.translate((width - viewBox.width() * scale) / 2.0f,  
  116.                 (height - viewBox.height() * scale) / 2.0f);  
  117.         canvas.scale(scale, scale);  
  118.   
  119.         mSvg.renderToCanvas(canvas);  
  120.     }  
  121.   
  122.     /** 
  123.      * Path with bounds for scalling , length and paint. 
  124.      */  
  125.     public static class SvgPath {  
  126.         /** 
  127.          * Region of the path. 
  128.          */  
  129.         private static final Region REGION = new Region();  
  130.         /** 
  131.          * This is done for clipping the bounds of the path. 
  132.          */  
  133.         private static final Region MAX_CLIP =  
  134.                 new Region(Integer.MIN_VALUE, Integer.MIN_VALUE,  
  135.                         Integer.MAX_VALUE, Integer.MAX_VALUE);  
  136.         /** 
  137.          * The path itself. 
  138.          */  
  139.         final Path path;  
  140.         /** 
  141.          * The paint to be drawn later. 
  142.          */  
  143.         final Paint paint;  
  144.         /** 
  145.          * The length of the path. 
  146.          */  
  147.         final float length;  
  148.         /** 
  149.          * The bounds of the path. 
  150.          */  
  151.         final Rect bounds;  
  152.         /** 
  153.          * The measure of the path, we can use it later to get segment of it. 
  154.          */  
  155.         final PathMeasure measure;  
  156.   
  157.         /** 
  158.          * Constructor to add the path and the paint. 
  159.          * 
  160.          * @param path  The path that comes from the rendered svg. 
  161.          * @param paint The result paint. 
  162.          */  
  163.         SvgPath(Path path, Paint paint) {  
  164.             this.path = path;  
  165.             this.paint = paint;  
  166.   
  167.             measure = new PathMeasure(path, false);  
  168.             this.length = measure.getLength();  
  169.   
  170.             REGION.setPath(path, MAX_CLIP);  
  171.             bounds = REGION.getBounds();  
  172.         }  
  173.     }  
  174. }  

一个是SVG控件类:

[java]  view plain copy
  1. /** 
  2.  * PathView is an View that animate paths. 
  3.  */  
  4. public class PathView extends View {  
  5.     /** 
  6.      * Logging tag. 
  7.      */  
  8.     public static final String LOG_TAG = "PathView";  
  9.     /** 
  10.      * The paint for the path. 
  11.      */  
  12.     private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  13.     /** 
  14.      * Utils to catch the paths from the svg. 
  15.      */  
  16.     private final SvgUtils svgUtils = new SvgUtils(paint);  
  17.     /** 
  18.      * All the paths provided to the view. Both from Path and Svg. 
  19.      */  
  20.     private List<SvgUtils.SvgPath> paths = new ArrayList<SvgUtils.SvgPath>(0);  
  21.     /** 
  22.      * This is a lock before the view is redrawn 
  23.      * or resided it must be synchronized with this object. 
  24.      */  
  25.     private final Object mSvgLock = new Object();  
  26.     /** 
  27.      * Thread for working with the object above. 
  28.      */  
  29.     private Thread mLoader;  
  30.   
  31.     /** 
  32.      * The svg image from the raw directory. 
  33.      */  
  34.     private int svgResourceId;  
  35.     /** 
  36.      * Object that build the animation for the path. 
  37.      */  
  38.     private AnimatorBuilder animatorBuilder;  
  39.     /** 
  40.      * The progress of the drawing. 
  41.      */  
  42.     private float progress = 0f;  
  43.   
  44.     /** 
  45.      * If the used colors are from the svg or from the set color. 
  46.      */  
  47.     private boolean naturalColors;  
  48.     /** 
  49.      * If the view is filled with its natural colors after path drawing. 
  50.      */  
  51.     private boolean fillAfter;  
  52.     /** 
  53.      * The width of the view. 
  54.      */  
  55.     private int width;  
  56.     /** 
  57.      * The height of the view. 
  58.      */  
  59.     private int height;  
  60.   
  61.     /** 
  62.      * Default constructor. 
  63.      * 
  64.      * @param context The Context of the application. 
  65.      */  
  66.     public PathView(Context context) {  
  67.         this(context, null);  
  68.     }  
  69.   
  70.     /** 
  71.      * Default constructor. 
  72.      * 
  73.      * @param context The Context of the application. 
  74.      * @param attrs   attributes provided from the resources. 
  75.      */  
  76.     public PathView(Context context, AttributeSet attrs) {  
  77.         this(context, attrs, 0);  
  78.     }  
  79.   
  80.     /** 
  81.      * Default constructor. 
  82.      * 
  83.      * @param context  The Context of the application. 
  84.      * @param attrs    attributes provided from the resources. 
  85.      * @param defStyle Default style. 
  86.      */  
  87.     public PathView(Context context, AttributeSet attrs, int defStyle) {  
  88.         super(context, attrs, defStyle);  
  89.         paint.setStyle(Paint.Style.STROKE);  
  90.         getFromAttributes(context, attrs);  
  91.     }  
  92.   
  93.     /** 
  94.      * Get all the fields from the attributes . 
  95.      * 
  96.      * @param context The Context of the application. 
  97.      * @param attrs   attributes provided from the resources. 
  98.      */  
  99.     private void getFromAttributes(Context context, AttributeSet attrs) {  
  100.         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PathView);  
  101.         try {  
  102.             if (a != null) {  
  103.                 paint.setColor(a.getColor(R.styleable.PathView_pathColor, 0xff00ff00));  
  104.                 paint.setStrokeWidth(a.getFloat(R.styleable.PathView_pathWidth, 8.0f));  
  105.                 svgResourceId = a.getResourceId(R.styleable.PathView_svg, 0);  
  106.             }  
  107.         } finally {  
  108.             if (a != null) {  
  109.                 a.recycle();  
  110.             }  
  111.         }  
  112.     }  
  113.   
  114.     /** 
  115.      * Set paths to be drawn and animated. 
  116.      * 
  117.      * @param paths - Paths that can be drawn. 
  118.      */  
  119.     public void setPaths(final List<Path> paths) {  
  120.         for (Path path : paths) {  
  121.             this.paths.add(new SvgUtils.SvgPath(path, paint));  
  122.         }  
  123.         synchronized (mSvgLock) {  
  124.             updatePathsPhaseLocked();  
  125.         }  
  126.     }  
  127.   
  128.     /** 
  129.      * Set path to be drawn and animated. 
  130.      * 
  131.      * @param path - Paths that can be drawn. 
  132.      */  
  133.     public void setPath(final Path path) {  
  134.         paths.add(new SvgUtils.SvgPath(path, paint));  
  135.         synchronized (mSvgLock) {  
  136.             updatePathsPhaseLocked();  
  137.         }  
  138.     }  
  139.   
  140.     /** 
  141.      * Animate this property. It is the percentage of the path that is drawn. 
  142.      * It must be [0,1]. 
  143.      * 
  144.      * @param percentage float the percentage of the path. 
  145.      */  
  146.     public void setPercentage(float percentage) {  
  147.         if (percentage < 0.0f || percentage > 1.0f) {  
  148.             throw new IllegalArgumentException("setPercentage not between 0.0f and 1.0f");  
  149.         }  
  150.         progress = percentage;  
  151.         synchronized (mSvgLock) {  
  152.             updatePathsPhaseLocked();  
  153.         }  
  154.         invalidate();  
  155.     }  
  156.   
  157.     /** 
  158.      * This refreshes the paths before draw and resize. 
  159.      */  
  160.     private void updatePathsPhaseLocked() {  
  161.         final int count = paths.size();  
  162.         for (int i = 0; i < count; i++) {  
  163.             SvgUtils.SvgPath svgPath = paths.get(i);  
  164.             svgPath.path.reset();  
  165.             svgPath.measure.getSegment(0.0f, svgPath.length * progress, svgPath.path, true);  
  166.             // Required only for Android 4.4 and earlier  
  167.             svgPath.path.rLineTo(0.0f, 0.0f);  
  168.         }  
  169.     }  
  170.   
  171.     @Override  
  172.     protected void onDraw(Canvas canvas) {  
  173.         super.onDraw(canvas);  
  174.   
  175.         synchronized (mSvgLock) {  
  176.             canvas.save();  
  177.             canvas.translate(getPaddingLeft(), getPaddingTop());  
  178.             final int count = paths.size();  
  179.             for (int i = 0; i < count; i++) {  
  180.                 final SvgUtils.SvgPath svgPath = paths.get(i);  
  181.                 final Path path = svgPath.path;  
  182.                 final Paint paint1 = naturalColors ? svgPath.paint : paint;  
  183.                 canvas.drawPath(path, paint1);  
  184.             }  
  185.             fillAfter(canvas);  
  186.             canvas.restore();  
  187.         }  
  188.     }  
  189.   
  190.     /** 
  191.      * If there is svg , the user called setFillAfter(true) and the progress is finished. 
  192.      * 
  193.      * @param canvas Draw to this canvas. 
  194.      */  
  195.     private void fillAfter(final Canvas canvas) {  
  196.         if (svgResourceId != 0 && fillAfter && progress == 1f) {  
  197.             svgUtils.drawSvgAfter(canvas, width, height);  
  198.         }  
  199.     }  
  200.   
  201.     @Override  
  202.     protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {  
  203.         super.onSizeChanged(w, h, oldw, oldh);  
  204.   
  205.         if (mLoader != null) {  
  206.             try {  
  207.                 mLoader.join();  
  208.             } catch (InterruptedException e) {  
  209.                 Log.e(LOG_TAG, "Unexpected error", e);  
  210.             }  
  211.         }  
  212.         if (svgResourceId != 0) {  
  213.             mLoader = new Thread(new Runnable() {  
  214.                 @Override  
  215.                 public void run() {  
  216.   
  217.                     svgUtils.load(getContext(), svgResourceId);  
  218.   
  219.                     synchronized (mSvgLock) {  
  220.                         width = w - getPaddingLeft() - getPaddingRight();  
  221.                         height = h - getPaddingTop() - getPaddingBottom();  
  222.                         paths = svgUtils.getPathsForViewport(width, height);  
  223.                         updatePathsPhaseLocked();  
  224.                     }  
  225.                 }  
  226.             }, "SVG Loader");  
  227.             mLoader.start();  
  228.         }  
  229.     }  
  230.   
  231.     @Override  
  232.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  233.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  234.         if (svgResourceId != 0) {  
  235.             int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  236.             int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  237.             setMeasuredDimension(widthSize, heightSize);  
  238.             return;  
  239.         }  
  240.   
  241.         int desiredWidth = 0;  
  242.         int desiredHeight = 0;  
  243.         final float strokeWidth = paint.getStrokeWidth() / 2;  
  244.         for (SvgUtils.SvgPath path : paths) {  
  245.             desiredWidth += path.bounds.left + path.bounds.width() + strokeWidth;  
  246.             desiredHeight += path.bounds.top + path.bounds.height() + strokeWidth;  
  247.         }  
  248.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  249.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  250.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  251.         int heightMode = MeasureSpec.getMode(widthMeasureSpec);  
  252.   
  253.         int measuredWidth, measuredHeight;  
  254.   
  255.         if (widthMode == MeasureSpec.AT_MOST) {  
  256.             measuredWidth = desiredWidth;  
  257.         } else {  
  258.             measuredWidth = widthSize;  
  259.         }  
  260.   
  261.         if (heightMode == MeasureSpec.AT_MOST) {  
  262.             measuredHeight = desiredHeight;  
  263.         } else {  
  264.             measuredHeight = heightSize;  
  265.         }  
  266.   
  267.         setMeasuredDimension(measuredWidth, measuredHeight);  
  268.     }  
  269.   
  270.     /** 
  271.      * If the real svg need to be drawn after the path animation. 
  272.      * 
  273.      * @param fillAfter - boolean if the view needs to be filled after path animation. 
  274.      */  
  275.     public void setFillAfter(final boolean fillAfter) {  
  276.         this.fillAfter = fillAfter;  
  277.     }  
  278.   
  279.     /** 
  280.      * If you want to use the colors from the svg. 
  281.      */  
  282.     public void useNaturalColors() {  
  283.         naturalColors = true;  
  284.     }  
  285.   
  286.     /** 
  287.      * Animator for the paths of the view. 
  288.      * 
  289.      * @return The AnimatorBuilder to build the animation. 
  290.      */  
  291.     public AnimatorBuilder getPathAnimator() {  
  292.         if (animatorBuilder == null) {  
  293.             animatorBuilder = new AnimatorBuilder(this);  
  294.         }  
  295.         return animatorBuilder;  
  296.     }  
  297.   
  298.     /** 
  299.      * Get the path color. 
  300.      * 
  301.      * @return The color of the paint. 
  302.      */  
  303.     public int getPathColor() {  
  304.         return paint.getColor();  
  305.     }  
  306.   
  307.     /** 
  308.      * Set the path color. 
  309.      * 
  310.      * @param color -The color to set to the paint. 
  311.      */  
  312.     public void setPathColor(final int color) {  
  313.         paint.setColor(color);  
  314.     }  
  315.   
  316.     /** 
  317.      * Get the path width. 
  318.      * 
  319.      * @return The width of the paint. 
  320.      */  
  321.     public float getPathWidth() {  
  322.         return paint.getStrokeWidth();  
  323.     }  
  324.   
  325.     /** 
  326.      * Set the path width. 
  327.      * 
  328.      * @param width - The width of the path. 
  329.      */  
  330.     public void setPathWidth(final float width) {  
  331.         paint.setStrokeWidth(width);  
  332.     }  
  333.   
  334.     /** 
  335.      * Get the svg resource id. 
  336.      * 
  337.      * @return The svg raw resource id. 
  338.      */  
  339.     public int getSvgResource() {  
  340.         return svgResourceId;  
  341.     }  
  342.   
  343.     /** 
  344.      * Set the svg resource id. 
  345.      * 
  346.      * @param svgResource - The resource id of the raw svg. 
  347.      */  
  348.     public void setSvgResource(int svgResource) {  
  349.         svgResourceId = svgResource;  
  350.     }  
  351.   
  352.     /** 
  353.      * Object for building the animation of the path of this view. 
  354.      */  
  355.     public static class AnimatorBuilder {  
  356.         /** 
  357.          * Duration of the animation. 
  358.          */  
  359.         private int duration = 350;  
  360.         /** 
  361.          * Interpolator for the time of the animation. 
  362.          */  
  363.         private Interpolator interpolator;  
  364.         /** 
  365.          * The delay before the animation. 
  366.          */  
  367.         private int delay = 0;  
  368.         /** 
  369.          * ObjectAnimator that constructs the animation. 
  370.          */  
  371.         private final ObjectAnimator anim;  
  372.         /** 
  373.          * Listener called before the animation. 
  374.          */  
  375.         private ListenerStart listenerStart;  
  376.         /** 
  377.          * Listener after the animation. 
  378.          */  
  379.         private ListenerEnd animationEnd;  
  380.         /** 
  381.          * Animation listener. 
  382.          */  
  383.         private PathViewAnimatorListener pathViewAnimatorListener;  
  384.   
  385.         /** 
  386.          * Default constructor. 
  387.          * 
  388.          * @param pathView The view that must be animated. 
  389.          */  
  390.         public AnimatorBuilder(final PathView pathView) {  
  391.             anim = ObjectAnimator.ofFloat(pathView, "percentage"0.0f, 1.0f);  
  392.         }  
  393.   
  394.         /** 
  395.          * Set the duration of the animation. 
  396.          * 
  397.          * @param duration - The duration of the animation. 
  398.          * @return AnimatorBuilder. 
  399.          */  
  400.         public AnimatorBuilder duration(final int duration) {  
  401.             this.duration = duration;  
  402.             return this;  
  403.         }  
  404.   
  405.         /** 
  406.          * Set the Interpolator. 
  407.          * 
  408.          * @param interpolator - Interpolator. 
  409.          * @return AnimatorBuilder. 
  410.          */  
  411.         public AnimatorBuilder interpolator(final Interpolator interpolator) {  
  412.             this.interpolator = interpolator;  
  413.             return this;  
  414.         }  
  415.   
  416.         /** 
  417.          * The delay before the animation. 
  418.          * 
  419.          * @param delay - int the delay 
  420.          * @return AnimatorBuilder. 
  421.          */  
  422.         public AnimatorBuilder delay(final int delay) {  
  423.             this.delay = delay;  
  424.             return this;  
  425.         }  
  426.   
  427.         /** 
  428.          * Set a listener before the start of the animation. 
  429.          * 
  430.          * @param listenerStart an interface called before the animation 
  431.          * @return AnimatorBuilder. 
  432.          */  
  433.         public AnimatorBuilder listenerStart(final ListenerStart listenerStart) {  
  434.             this.listenerStart = listenerStart;  
  435.             if (pathViewAnimatorListener == null) {  
  436.                 pathViewAnimatorListener = new PathViewAnimatorListener();  
  437.                 anim.addListener(pathViewAnimatorListener);  
  438.             }  
  439.             return this;  
  440.         }  
  441.   
  442.         /** 
  443.          * Set a listener after of the animation. 
  444.          * 
  445.          * @param animationEnd an interface called after the animation 
  446.          * @return AnimatorBuilder. 
  447.          */  
  448.         public AnimatorBuilder listenerEnd(final ListenerEnd animationEnd) {  
  449.             this.animationEnd = animationEnd;  
  450.             if (pathViewAnimatorListener == null) {  
  451.                 pathViewAnimatorListener = new PathViewAnimatorListener();  
  452.                 anim.addListener(pathViewAnimatorListener);  
  453.             }  
  454.             return this;  
  455.         }  
  456.   
  457.         /** 
  458.          * Starts the animation. 
  459.          */  
  460.         public void start() {  
  461.             anim.setDuration(duration);  
  462.             anim.setInterpolator(interpolator);  
  463.             anim.setStartDelay(delay);  
  464.             anim.start();  
  465.         }  
  466.   
  467.         /** 
  468.          * Animation listener to be able to provide callbacks for the caller. 
  469.          */  
  470.         private class PathViewAnimatorListener implements Animator.AnimatorListener {  
  471.   
  472.             @Override  
  473.             public void onAnimationStart(Animator animation) {  
  474.                 if (listenerStart != null) listenerStart.onAnimationStart();  
  475.             }  
  476.   
  477.             @Override  
  478.             public void onAnimationEnd(Animator animation) {  
  479.                 if (animationEnd != null) animationEnd.onAnimationEnd();  
  480.             }  
  481.   
  482.             @Override  
  483.             public void onAnimationCancel(Animator animation) {  
  484.   
  485.             }  
  486.   
  487.             @Override  
  488.             public void onAnimationRepeat(Animator animation) {  
  489.   
  490.             }  
  491.         }  
  492.   
  493.         /** 
  494.          * Called when the animation start. 
  495.          */  
  496.         public interface ListenerStart {  
  497.             /** 
  498.              * Called when the path animation start. 
  499.              */  
  500.             void onAnimationStart();  
  501.         }  
  502.   
  503.         /** 
  504.          * Called when the animation end. 
  505.          */  
  506.         public interface ListenerEnd {  
  507.             /** 
  508.              * Called when the path animation end. 
  509.              */  
  510.             void onAnimationEnd();  
  511.         }  
  512.     }  
  513. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值