android svg指纹录取动画_Android实现炫酷SVG动画效果

本文介绍了如何在Android上利用SVG实现指纹录取动画。SVG是一种矢量图形语言,允许在代码中创建和编辑图像。文章展示了Android使用SVG库创建炫酷动画效果,包括SVG的解析和绘制。通过一个开源控件,可以方便地将SVG转换为Android动画,并提供了SVG文件的解析工具类和SVG控件类的详细代码示例。
摘要由CSDN通过智能技术生成

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

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

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

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

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

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

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

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

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

SVG里面还有各种标签:

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

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

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

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

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

下面我们来看这个控件的简单使用,作为一个自定义控件,我们只需要在布局文件里面添加<?xml  version="1.0" encoding="utf-8"?>

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:background="#ff0000"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

xmlns:app="http://schemas.android.com/apk/res-auto"

android:id="@+id/pathView"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:pathColor="@android:color/white"

app:svg="@raw/ironman_white"

app:pathWidth="5"/>

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

然后来看Activity里面:public class MainActivity extends FragmentActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

final PathView pathView = (PathView) findViewById(R.id.pathView);

//        final Path path = makeConvexArrow(50, 100);

//        pathView.setPath(path);

pathView.setFillAfter(true);

pathView.useNaturalColors();

pathView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

pathView.getPathAnimator().

delay(100).

duration(1500).

interpolator(new AccelerateDecelerateInterpolator()).

start();

}

});

}

private Path makeConvexArrow(float length, float height) {

final Path path = new Path();

path.moveTo(0.0f, 0.0f);

path.lineTo(length / 4f, 0.0f);

path.lineTo(length, height / 2.0f);

path.lineTo(length / 4f, height);

path.lineTo(0.0f, height);

path.lineTo(length * 3f / 4f, height / 2f);

path.lineTo(0.0f, 0.0f);

path.close();

return path;

}

}

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

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

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

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

* Util class to init and get paths from svg.

*/

public class SvgUtils {

/**

* It is for logging purposes.

*/

private static final String LOG_TAG = "SVGUtils";

/**

* All the paths with their attributes from the svg.

*/

private final List mPaths = new ArrayList();

/**

* The paint provided from the view.

*/

private final Paint mSourcePaint;

/**

* The init svg.

*/

private SVG mSvg;

/**

* Init the SVGUtils with a paint for coloring.

*

* @param sourcePaint - the paint for the coloring.

*/

public SvgUtils(final Paint sourcePaint) {

mSourcePaint = sourcePaint;

}

/**

* Loading the svg from the resources.

*

* @param context     Context object to get the resources.

* @param svgResource int resource id of the svg.

*/

public void load(Context context, int svgResource) {

if (mSvg != null) return;

try {

mSvg = SVG.getFromResource(context, svgResource);

mSvg.setDocumentPreserveAspectRatio(PreserveAspectRatio.UNSCALED);

} catch (SVGParseException e) {

Log.e(LOG_TAG, "Could not load specified SVG resource", e);

}

}

/**

* Draw the svg to the canvas.

*

* @param canvas The canvas to be drawn.

* @param width  The width of the canvas.

* @param height The height of the canvas.

*/

public void drawSvgAfter(final Canvas canvas, final int width, final int height) {

final float strokeWidth = mSourcePaint.getStrokeWidth();

rescaleCanvas(width, height, strokeWidth, canvas);

}

/**

* Render the svg to canvas and catch all the paths while rendering.

*

* @param width  - the width to scale down the view to,

* @param height - the height to scale down the view to,

* @return All the paths from the svg.

*/

public List getPathsForViewport(final int width, final int height) {

final float strokeWidth = mSourcePaint.getStrokeWidth();

Canvas canvas = new Canvas() {

private final Matrix mMatrix = new Matrix();

@Override

public int getWidth() {

return width;

}

@Override

public int getHeight() {

return height;

}

@Override

public void drawPath(Path path, Paint paint) {

Path dst = new Path();

//noinspection deprecation

getMatrix(mMatrix);

path.transform(mMatrix, dst);

paint.setAntiAlias(true);

paint.setStyle(Paint.Style.STROKE);

paint.setStrokeWidth(strokeWidth);

mPaths.add(new SvgPath(dst, paint));

}

};

rescaleCanvas(width, height, strokeWidth, canvas);

return mPaths;

}

/**

* Rescale the canvas with specific width and height.

*

* @param width       The width of the canvas.

* @param height      The height of the canvas.

* @param strokeWidth Width of the path to add to scaling.

* @param canvas      The canvas to be drawn.

*/

private void rescaleCanvas(int width, int height, float strokeWidth, Canvas canvas) {

final RectF viewBox = mSvg.getDocumentViewBox();

final float scale = Math.min(width

/ (viewBox.width() + strokeWidth),

height / (viewBox.height() + strokeWidth));

canvas.translate((width - viewBox.width() * scale) / 2.0f,

(height - viewBox.height() * scale) / 2.0f);

canvas.scale(scale, scale);

mSvg.renderToCanvas(canvas);

}

/**

* Path with bounds for scalling , length and paint.

*/

public static class SvgPath {

/**

* Region of the path.

*/

private static final Region REGION = new Region();

/**

* This is done for clipping the bounds of the path.

*/

private static final Region MAX_CLIP =

new Region(Integer.MIN_VALUE, Integer.MIN_VALUE,

Integer.MAX_VALUE, Integer.MAX_VALUE);

/**

* The path itself.

*/

final Path path;

/**

* The paint to be drawn later.

*/

final Paint paint;

/**

* The length of the path.

*/

final float length;

/**

* The bounds of the path.

*/

final Rect bounds;

/**

* The measure of the path, we can use it later to get segment of it.

*/

final PathMeasure measure;

/**

* Constructor to add the path and the paint.

*

* @param path  The path that comes from the rendered svg.

* @param paint The result paint.

*/

SvgPath(Path path, Paint paint) {

this.path = path;

this.paint = paint;

measure = new PathMeasure(path, false);

this.length = measure.getLength();

REGION.setPath(path, MAX_CLIP);

bounds = REGION.getBounds();

}

}

}

一个是SVG控件类:/**

* PathView is an View that animate paths.

*/

public class PathView extends View {

/**

* Logging tag.

*/

public static final String LOG_TAG = "PathView";

/**

* The paint for the path.

*/

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

/**

* Utils to catch the paths from the svg.

*/

private final SvgUtils svgUtils = new SvgUtils(paint);

/**

* All the paths provided to the view. Both from Path and Svg.

*/

private List paths = new ArrayList(0);

/**

* This is a lock before the view is redrawn

* or resided it must be synchronized with this object.

*/

private final Object mSvgLock = new Object();

/**

* Thread for working with the object above.

*/

private Thread mLoader;

/**

* The svg image from the raw directory.

*/

private int svgResourceId;

/**

* Object that build the animation for the path.

*/

private AnimatorBuilder animatorBuilder;

/**

* The progress of the drawing.

*/

private float progress = 0f;

/**

* If the used colors are from the svg or from the set color.

*/

private boolean naturalColors;

/**

* If the view is filled with its natural colors after path drawing.

*/

private boolean fillAfter;

/**

* The width of the view.

*/

private int width;

/**

* The height of the view.

*/

private int height;

/**

* Default constructor.

*

* @param context The Context of the application.

*/

public PathView(Context context) {

this(context, null);

}

/**

* Default constructor.

*

* @param context The Context of the application.

* @param attrs   attributes provided from the resources.

*/

public PathView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

/**

* Default constructor.

*

* @param context  The Context of the application.

* @param attrs    attributes provided from the resources.

* @param defStyle Default style.

*/

public PathView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

paint.setStyle(Paint.Style.STROKE);

getFromAttributes(context, attrs);

}

/**

* Get all the fields from the attributes .

*

* @param context The Context of the application.

* @param attrs   attributes provided from the resources.

*/

private void getFromAttributes(Context context, AttributeSet attrs) {

final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PathView);

try {

if (a != null) {

paint.setColor(a.getColor(R.styleable.PathView_pathColor, 0xff00ff00));

paint.setStrokeWidth(a.getFloat(R.styleable.PathView_pathWidth, 8.0f));

svgResourceId = a.getResourceId(R.styleable.PathView_svg, 0);

}

} finally {

if (a != null) {

a.recycle();

}

}

}

/**

* Set paths to be drawn and animated.

*

* @param paths - Paths that can be drawn.

*/

public void setPaths(final List paths) {

for (Path path : paths) {

this.paths.add(new SvgUtils.SvgPath(path, paint));

}

synchronized (mSvgLock) {

updatePathsPhaseLocked();

}

}

/**

* Set path to be drawn and animated.

*

* @param path - Paths that can be drawn.

*/

public void setPath(final Path path) {

paths.add(new SvgUtils.SvgPath(path, paint));

synchronized (mSvgLock) {

updatePathsPhaseLocked();

}

}

/**

* Animate this property. It is the percentage of the path that is drawn.

* It must be [0,1].

*

* @param percentage float the percentage of the path.

*/

public void setPercentage(float percentage) {

if (percentage  1.0f) {

throw new IllegalArgumentException("setPercentage not between 0.0f and 1.0f");

}

progress = percentage;

synchronized (mSvgLock) {

updatePathsPhaseLocked();

}

invalidate();

}

/**

* This refreshes the paths before draw and resize.

*/

private void updatePathsPhaseLocked() {

final int count = paths.size();

for (int i = 0; i 

SvgUtils.SvgPath svgPath = paths.get(i);

svgPath.path.reset();

svgPath.measure.getSegment(0.0f, svgPath.length * progress, svgPath.path, true);

// Required only for Android 4.4 and earlier

svgPath.path.rLineTo(0.0f, 0.0f);

}

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

synchronized (mSvgLock) {

canvas.save();

canvas.translate(getPaddingLeft(), getPaddingTop());

final int count = paths.size();

for (int i = 0; i 

final SvgUtils.SvgPath svgPath = paths.get(i);

final Path path = svgPath.path;

final Paint paint1 = naturalColors ? svgPath.paint : paint;

canvas.drawPath(path, paint1);

}

fillAfter(canvas);

canvas.restore();

}

}

/**

* If there is svg , the user called setFillAfter(true) and the progress is finished.

*

* @param canvas Draw to this canvas.

*/

private void fillAfter(final Canvas canvas) {

if (svgResourceId != 0 && fillAfter && progress == 1f) {

svgUtils.drawSvgAfter(canvas, width, height);

}

}

@Override

protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

if (mLoader != null) {

try {

mLoader.join();

} catch (InterruptedException e) {

Log.e(LOG_TAG, "Unexpected error", e);

}

}

if (svgResourceId != 0) {

mLoader = new Thread(new Runnable() {

@Override

public void run() {

svgUtils.load(getContext(), svgResourceId);

synchronized (mSvgLock) {

width = w - getPaddingLeft() - getPaddingRight();

height = h - getPaddingTop() - getPaddingBottom();

paths = svgUtils.getPathsForViewport(width, height);

updatePathsPhaseLocked();

}

}

}, "SVG Loader");

mLoader.start();

}

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (svgResourceId != 0) {

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSize = MeasureSpec.getSize(heightMeasureSpec);

setMeasuredDimension(widthSize, heightSize);

return;

}

int desiredWidth = 0;

int desiredHeight = 0;

final float strokeWidth = paint.getStrokeWidth() / 2;

for (SvgUtils.SvgPath path : paths) {

desiredWidth += path.bounds.left + path.bounds.width() + strokeWidth;

desiredHeight += path.bounds.top + path.bounds.height() + strokeWidth;

}

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSize = MeasureSpec.getSize(heightMeasureSpec);

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

int heightMode = MeasureSpec.getMode(widthMeasureSpec);

int measuredWidth, measuredHeight;

if (widthMode == MeasureSpec.AT_MOST) {

measuredWidth = desiredWidth;

} else {

measuredWidth = widthSize;

}

if (heightMode == MeasureSpec.AT_MOST) {

measuredHeight = desiredHeight;

} else {

measuredHeight = heightSize;

}

setMeasuredDimension(measuredWidth, measuredHeight);

}

/**

* If the real svg need to be drawn after the path animation.

*

* @param fillAfter - boolean if the view needs to be filled after path animation.

*/

public void setFillAfter(final boolean fillAfter) {

this.fillAfter = fillAfter;

}

/**

* If you want to use the colors from the svg.

*/

public void useNaturalColors() {

naturalColors = true;

}

/**

* Animator for the paths of the view.

*

* @return The AnimatorBuilder to build the animation.

*/

public AnimatorBuilder getPathAnimator() {

if (animatorBuilder == null) {

animatorBuilder = new AnimatorBuilder(this);

}

return animatorBuilder;

}

/**

* Get the path color.

*

* @return The color of the paint.

*/

public int getPathColor() {

return paint.getColor();

}

/**

* Set the path color.

*

* @param color -The color to set to the paint.

*/

public void setPathColor(final int color) {

paint.setColor(color);

}

/**

* Get the path width.

*

* @return The width of the paint.

*/

public float getPathWidth() {

return paint.getStrokeWidth();

}

/**

* Set the path width.

*

* @param width - The width of the path.

*/

public void setPathWidth(final float width) {

paint.setStrokeWidth(width);

}

/**

* Get the svg resource id.

*

* @return The svg raw resource id.

*/

public int getSvgResource() {

return svgResourceId;

}

/**

* Set the svg resource id.

*

* @param svgResource - The resource id of the raw svg.

*/

public void setSvgResource(int svgResource) {

svgResourceId = svgResource;

}

/**

* Object for building the animation of the path of this view.

*/

public static class AnimatorBuilder {

/**

* Duration of the animation.

*/

private int duration = 350;

/**

* Interpolator for the time of the animation.

*/

private Interpolator interpolator;

/**

* The delay before the animation.

*/

private int delay = 0;

/**

* ObjectAnimator that constructs the animation.

*/

private final ObjectAnimator anim;

/**

* Listener called before the animation.

*/

private ListenerStart listenerStart;

/**

* Listener after the animation.

*/

private ListenerEnd animationEnd;

/**

* Animation listener.

*/

private PathViewAnimatorListener pathViewAnimatorListener;

/**

* Default constructor.

*

* @param pathView The view that must be animated.

*/

public AnimatorBuilder(final PathView pathView) {

anim = ObjectAnimator.ofFloat(pathView, "percentage", 0.0f, 1.0f);

}

/**

* Set the duration of the animation.

*

* @param duration - The duration of the animation.

* @return AnimatorBuilder.

*/

public AnimatorBuilder duration(final int duration) {

this.duration = duration;

return this;

}

/**

* Set the Interpolator.

*

* @param interpolator - Interpolator.

* @return AnimatorBuilder.

*/

public AnimatorBuilder interpolator(final Interpolator interpolator) {

this.interpolator = interpolator;

return this;

}

/**

* The delay before the animation.

*

* @param delay - int the delay

* @return AnimatorBuilder.

*/

public AnimatorBuilder delay(final int delay) {

this.delay = delay;

return this;

}

/**

* Set a listener before the start of the animation.

*

* @param listenerStart an interface called before the animation

* @return AnimatorBuilder.

*/

public AnimatorBuilder listenerStart(final ListenerStart listenerStart) {

this.listenerStart = listenerStart;

if (pathViewAnimatorListener == null) {

pathViewAnimatorListener = new PathViewAnimatorListener();

anim.addListener(pathViewAnimatorListener);

}

return this;

}

/**

* Set a listener after of the animation.

*

* @param animationEnd an interface called after the animation

* @return AnimatorBuilder.

*/

public AnimatorBuilder listenerEnd(final ListenerEnd animationEnd) {

this.animationEnd = animationEnd;

if (pathViewAnimatorListener == null) {

pathViewAnimatorListener = new PathViewAnimatorListener();

anim.addListener(pathViewAnimatorListener);

}

return this;

}

/**

* Starts the animation.

*/

public void start() {

anim.setDuration(duration);

anim.setInterpolator(interpolator);

anim.setStartDelay(delay);

anim.start();

}

/**

* Animation listener to be able to provide callbacks for the caller.

*/

private class PathViewAnimatorListener implements Animator.AnimatorListener {

@Override

public void onAnimationStart(Animator animation) {

if (listenerStart != null) listenerStart.onAnimationStart();

}

@Override

public void onAnimationEnd(Animator animation) {

if (animationEnd != null) animationEnd.onAnimationEnd();

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

}

/**

* Called when the animation start.

*/

public interface ListenerStart {

/**

* Called when the path animation start.

*/

void onAnimationStart();

}

/**

* Called when the animation end.

*/

public interface ListenerEnd {

/**

* Called when the path animation end.

*/

void onAnimationEnd();

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值