这篇文章将为大家详细讲解有关Android如何使用Path实现搜索动态加载动画效果,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
效果如:
实现这个就是使用Path中的getSegment()不断的去改变它截取片段的start和stop,再结合动画,今天就分步骤实现它,看完以后你也会觉的不是很难,只是没想到这么实现而已,所以要多见识,所谓眼界决定你的高度,还是延续我写博客的习惯,一步步分析,第一步就是绘制如下图:
如果单纯的绘制这个图很简单很简单的,绘制一个圆,然后再绘制一根线就搞定,但是要考虑这里的效果,就不能这么干了,如果你看了上面的gif图就知道,其实这是2个同心圆,然后前一个path的起点和后一个path的起点相连接就是形成一条直线了,但是path中的图形内容也就是这个圆是怎么绘制出来的呢?如果是绘制圆的话,上面的线起点和终点位置怎么去计算,这是个问题,但是我们绘制圆还可以使用绘制椭圆的形式也是可以绘制达到圆的效果,从45度开始绘制一个圆,是不是这个线的起点搞定了,分析图如下:
那么好,根据上面的分析开始写代码绘制出一个静态的搜索图:
package com.tuya; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * Created by admin on 2016/12/17. */ public class DynamicSearchView2 extends View { private Paint paint; private int width;//view的宽度 private int height;//view的高度 private Path searchPath; private Path circlePath; private float BigCircleRectWidth;//搜索圆对应的外切正方形边长 private PathMeasure pathMeasure; private float[] pos; public DynamicSearchView2(Context context) { this(context,null); } public DynamicSearchView2(Context context, AttributeSet attrs) { this(context, attrs,0); } public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(3); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; initPath(); } /** * 初始化path */ private void initPath() { searchPath = new Path(); circlePath = new Path(); if(width>height){//长方形 BigCircleRectWidth = height; }else if(width<height){ BigCircleRectWidth = width; }else{ BigCircleRectWidth = width; } float smallbordWidth =BigCircleRectWidth/8; RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth); searchPath.addArc(searchRect,45,360); float bigBordWidth = smallbordWidth*2; RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth); circlePath.addArc(circleRect,45,-360); pathMeasure = new PathMeasure(circlePath,false); pos = new float[2]; pathMeasure.getPosTan(0,pos,null); searchPath.lineTo(pos[0],pos[1]); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点 canvas.drawPath(searchPath,paint); canvas.drawPath(circlePath,paint); } }
效果图:
本来这个外圆是不需要draw上去的,我在这绘制上去只是告诉你这二个圆是有一定的联系,哪为什么这根线是这样的呢?我们在绘制这个圆的时候是从45度开始绘制360刚好是一周,形成了一个圆,现在做个测试不要360,就写个330度,效果如下:
这个时候你会发现这条线是对的,导致问题其实是这样的,如图分析:
把绘制椭圆的关键代码:
searchPath.addArc(searchRect,45,358);
circlePath.addArc(circleRect,45,-358);
不要写成360,改为358试试,效果图:
发现这线是不是正常了,至于外面的圆还有点缺口,第一你可以把358改成359应该没事了,还有就是我们其实真实的效果并不需要这个外面的圆,所以不改也没事,那么好,第一步算是完成了,现在想想第二步怎么实现,先把第二步的效果用gif展示看下,不然光想没思路,就像你看美女,第一眼看那,是吧,就不多说了!要有画面感,
还是画布分析:
哪我们只要改变startD这个离起始点的位置值就ok,当然有很多种方法,但是Android中基本上都是使用值动画,ok,根据这个思路实现这个第二步逻辑:
package com.tuya; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * Created by admin on 2016/12/17. */ public class DynamicSearchView2 extends View { private Paint paint; private int width;//view的宽度 private int height;//view的高度 private Path searchPath; private Path circlePath; private float BigCircleRectWidth;//搜索圆对应的外切正方形边长 private PathMeasure pathMeasure; private float[] pos; private float animPercent;// private ValueAnimator serchStartAnim; private long animDuration = 2000;//动画时间 public DynamicSearchView2(Context context) { this(context,null); } public DynamicSearchView2(Context context, AttributeSet attrs) { this(context, attrs,0); } public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { initPaint(); initAnim(); initAnimListener(); startAnim(); } /** * 开始执行动画 */ private void startAnim() { serchStartAnim.start(); } /** * 动画监听 */ private void initAnimListener() { serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); } /** * 初始化动画 */ private void initAnim() { serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration); } /** * 初始化画笔 */ private void initPaint() { paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(3); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; initPath(); } /** * 初始化path */ private void initPath() { searchPath = new Path(); circlePath = new Path(); if(width>height){//长方形 BigCircleRectWidth = height; }else if(width<height){ BigCircleRectWidth = width; }else{ BigCircleRectWidth = width; } float smallbordWidth =BigCircleRectWidth/8; RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth); searchPath.addArc(searchRect,45,358); float bigBordWidth = smallbordWidth*2; RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth); circlePath.addArc(circleRect,45,-358); pathMeasure = new PathMeasure(circlePath,false); pos = new float[2]; pathMeasure.getPosTan(0,pos,null); searchPath.lineTo(pos[0],pos[1]); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点 drawSearch(canvas); } private void drawSearch(Canvas canvas) { Path dst = new Path(); pathMeasure.setPath(searchPath,false); pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true); canvas.drawPath(searchPath,paint); } }
效果:
现在还我们效果还差外圆的大圆的效果了,那么大圆是在小圆动画执行完毕后再去做旋转效果的,那好,我们只要监听动画就可以,画图:
package com.tuya; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * Created by admin on 2016/12/17. */ public class DynamicSearchView2 extends View { private static final String TAG = "DynamicSearchView2"; private Paint paint; private int width;//view的宽度 private int height;//view的高度 private Path searchPath; private Path circlePath; private float BigCircleRectWidth;//搜索圆对应的外切正方形边长 private PathMeasure pathMeasure; private float[] pos; private float animPercent;// private ValueAnimator serchStartAnim; private ValueAnimator bigCircleAnim;//外面大圆运动的动画 private long animDuration = 2000;//动画时间 private int drawTag = 1;//区分是绘制搜索框还是外层圆 public DynamicSearchView2(Context context) { this(context,null); } public DynamicSearchView2(Context context, AttributeSet attrs) { this(context, attrs,0); } public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { initPaint(); initAnim(); initAnimListener(); startAnim(); } /** * 开始执行动画 */ private void startAnim() { drawTag = 1; serchStartAnim.start(); invalidate(); } /** * 开启大圆执行动画 */ public void startBigCirCleAnim(){ serchStartAnim.removeAllUpdateListeners();//把上一个动画监听移除 以免总成诡异的bug bigCircleAnim.start(); drawTag = 2; } /** * 动画监听 */ private void initAnimListener() { serchStartAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { startBigCirCleAnim(); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); } /** * 初始化动画 */ private void initAnim() { bigCircleAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration); serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration); } /** * 初始化画笔 */ private void initPaint() { paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(3); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; initPath(); } /** * 初始化path */ private void initPath() { searchPath = new Path(); circlePath = new Path(); if(width>height){//长方形 BigCircleRectWidth = height; }else if(width<height){ BigCircleRectWidth = width; }else{ BigCircleRectWidth = width; } float smallbordWidth =BigCircleRectWidth/8; RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth); searchPath.addArc(searchRect,45,358); float bigBordWidth = smallbordWidth*2; RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth); circlePath.addArc(circleRect,45,-358); pathMeasure = new PathMeasure(circlePath,false); pos = new float[2]; pathMeasure.getPosTan(0,pos,null); searchPath.lineTo(pos[0],pos[1]); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点 drawSearch(canvas); } private void drawSearch(Canvas canvas) { if(drawTag==1){ drawSearchGraph(canvas); }else if(drawTag==2){ drawBigCircleGraph(canvas); } } /** * 绘制外层大圆 * @param canvas */ private void drawBigCircleGraph(Canvas canvas) { pathMeasure.setPath(circlePath, false); Path dst2 = new Path(); float stop = pathMeasure.getLength() * animPercent; float start = (float) (stop - ((0.5 - Math.abs(animPercent - 0.5)) * 200f)); pathMeasure.getSegment(start, stop, dst2, true); canvas.drawPath(dst2, paint); } /** * 绘制搜索框 * @param canvas */ private void drawSearchGraph(Canvas canvas) { pathMeasure.setPath(searchPath,false); Path dst = new Path(); pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true); canvas.drawPath(dst,paint); } }
效果:
发现转一圈就到头了,如果有特定的需求肯定是要控制整个转圈的圈数,如果是网络加载的话,除非网络特别的好,先不管了,因为等下还要写周报,也是很痛苦的
现在还差最后一步就是大圆的运动完后要绘制搜索框出来,其实这个和第一步效果刚好是相关的,
package com.tuya; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * Created by admin on 2016/12/17. */ public class DynamicSearchView2 extends View { private static final String TAG = "DynamicSearchView2"; private Paint paint; private int width;//view的宽度 private int height;//view的高度 private Path searchPath; private Path circlePath; private float BigCircleRectWidth;//搜索圆对应的外切正方形边长 private PathMeasure pathMeasure; private float[] pos; private float animPercent;// private ValueAnimator serchStartAnim; private ValueAnimator bigCircleAnim;//外面大圆运动的动画 private ValueAnimator startDrawSearchAnim;//最后一步绘制搜索框 private long animDuration = 2000;//动画时间 private int drawTag = 1;//区分是绘制搜索框还是外层圆 public DynamicSearchView2(Context context) { this(context,null); } public DynamicSearchView2(Context context, AttributeSet attrs) { this(context, attrs,0); } public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { initPaint(); initAnim(); initAnimListener(); startAnim(); } /** * 开始执行动画 */ private void startAnim() { drawTag = 1; serchStartAnim.start(); invalidate(); } /** * 开启大圆执行动画 */ public void startBigCirCleAnim(){ serchStartAnim.removeAllUpdateListeners();//把上一个动画监听移除 以免总成诡异的bug bigCircleAnim.start(); drawTag = 2; } /** * 最后绘制搜索框的动画 */ public void drawSearchAanim(){ bigCircleAnim.removeAllUpdateListeners();//把上一个动画监听移除 以免总成诡异的bug startDrawSearchAnim.start(); drawTag = 3; } /** * 动画监听 */ private void initAnimListener() { bigCircleAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { drawSearchAanim(); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); serchStartAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { startBigCirCleAnim(); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); startDrawSearchAnim .addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //获取动画在单位时间内,每次执行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); } /** * 初始化动画 */ private void initAnim() { bigCircleAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration); serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration); startDrawSearchAnim = ValueAnimator.ofFloat(1,0).setDuration(animDuration); } /** * 初始化画笔 */ private void initPaint() { paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(6); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; initPath(); } /** * 初始化path */ private void initPath() { searchPath = new Path(); circlePath = new Path(); if(width>height){//长方形 BigCircleRectWidth = height; }else if(width<height){ BigCircleRectWidth = width; }else{ BigCircleRectWidth = width; } float smallbordWidth =BigCircleRectWidth/8; RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth); searchPath.addArc(searchRect,45,358); float bigBordWidth = smallbordWidth*2; RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth); circlePath.addArc(circleRect,45,-358); pathMeasure = new PathMeasure(circlePath,false); pos = new float[2]; pathMeasure.getPosTan(0,pos,null); searchPath.lineTo(pos[0],pos[1]); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(width/2,height/2);//平移画布把这个view的中心点当做原点 drawSearch(canvas); } private void drawSearch(Canvas canvas) { if(drawTag==1){ drawSearchGraph(canvas); }else if(drawTag==2){ drawBigCircleGraph(canvas); }else if(drawTag==3){ drawSearchBox(canvas); } } /** * 最后一步绘制搜索框 从终点到起点 * @param canvas */ private void drawSearchBox(Canvas canvas) { pathMeasure.setPath(searchPath, false); Path dst3 = new Path(); pathMeasure.getSegment(pathMeasure.getLength() * animPercent, pathMeasure.getLength(), dst3, true); canvas.drawPath(dst3, paint); } /** * 绘制外层大圆 * @param canvas */ private void drawBigCircleGraph(Canvas canvas) { pathMeasure.setPath(circlePath, false); Path dst2 = new Path(); float stop = pathMeasure.getLength() * animPercent; float start = (float) (stop - ((0.5 - Math.abs(animPercent - 0.5)) * 200f)); pathMeasure.getSegment(start, stop, dst2, true); canvas.drawPath(dst2, paint); } /** * 绘制搜索框 * @param canvas */ private void drawSearchGraph(Canvas canvas) { pathMeasure.setPath(searchPath,false); Path dst = new Path(); pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true); canvas.drawPath(dst,paint); } }
效果:
Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。
关于“Android如何使用Path实现搜索动态加载动画效果”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。