您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Android如何使用Path實現(xiàn)搜索動態(tài)加載動畫效果,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
效果如:
實現(xiàn)這個就是使用Path中的getSegment()不斷的去改變它截取片段的start和stop,再結合動畫,今天就分步驟實現(xiàn)它,看完以后你也會覺的不是很難,只是沒想到這么實現(xiàn)而已,所以要多見識,所謂眼界決定你的高度,還是延續(xù)我寫博客的習慣,一步步分析,第一步就是繪制如下圖:
如果單純的繪制這個圖很簡單很簡單的,繪制一個圓,然后再繪制一根線就搞定,但是要考慮這里的效果,就不能這么干了,如果你看了上面的gif圖就知道,其實這是2個同心圓,然后前一個path的起點和后一個path的起點相連接就是形成一條直線了,但是path中的圖形內容也就是這個圓是怎么繪制出來的呢?如果是繪制圓的話,上面的線起點和終點位置怎么去計算,這是個問題,但是我們繪制圓還可以使用繪制橢圓的形式也是可以繪制達到圓的效果,從45度開始繪制一個圓,是不是這個線的起點搞定了,分析圖如下:
那么好,根據(jù)上面的分析開始寫代碼繪制出一個靜態(tài)的搜索圖:
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上去的,我在這繪制上去只是告訴你這二個圓是有一定的聯(lián)系,哪為什么這根線是這樣的呢?我們在繪制這個圓的時候是從45度開始繪制360剛好是一周,形成了一個圓,現(xiàn)在做個測試不要360,就寫個330度,效果如下:
這個時候你會發(fā)現(xiàn)這條線是對的,導致問題其實是這樣的,如圖分析:
把繪制橢圓的關鍵代碼:
searchPath.addArc(searchRect,45,358);
circlePath.addArc(circleRect,45,-358);
不要寫成360,改為358試試,效果圖:
發(fā)現(xiàn)這線是不是正常了,至于外面的圓還有點缺口,第一你可以把358改成359應該沒事了,還有就是我們其實真實的效果并不需要這個外面的圓,所以不改也沒事,那么好,第一步算是完成了,現(xiàn)在想想第二步怎么實現(xiàn),先把第二步的效果用gif展示看下,不然光想沒思路,就像你看美女,第一眼看那,是吧,就不多說了!要有畫面感,
還是畫布分析:
哪我們只要改變startD這個離起始點的位置值就ok,當然有很多種方法,但是Android中基本上都是使用值動畫,ok,根據(jù)這個思路實現(xiàn)這個第二步邏輯:
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(); } /** * 開始執(zhí)行動畫 */ private void startAnim() { serchStartAnim.start(); } /** * 動畫監(jiān)聽 */ private void initAnimListener() { serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //獲取動畫在單位時間內,每次執(zhí)行的值 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); } }
效果:
現(xiàn)在還我們效果還差外圓的大圓的效果了,那么大圓是在小圓動畫執(zhí)行完畢后再去做旋轉效果的,那好,我們只要監(jiān)聽動畫就可以,畫圖:
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;//區(qū)分是繪制搜索框還是外層圓 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(); } /** * 開始執(zhí)行動畫 */ private void startAnim() { drawTag = 1; serchStartAnim.start(); invalidate(); } /** * 開啟大圓執(zhí)行動畫 */ public void startBigCirCleAnim(){ serchStartAnim.removeAllUpdateListeners();//把上一個動畫監(jiān)聽移除 以免總成詭異的bug bigCircleAnim.start(); drawTag = 2; } /** * 動畫監(jiān)聽 */ 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) { //獲取動畫在單位時間內,每次執(zhí)行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //獲取動畫在單位時間內,每次執(zhí)行的值 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); } }
效果:
發(fā)現(xiàn)轉一圈就到頭了,如果有特定的需求肯定是要控制整個轉圈的圈數(shù),如果是網絡加載的話,除非網絡特別的好,先不管了,因為等下還要寫周報,也是很痛苦的
現(xiàn)在還差最后一步就是大圓的運動完后要繪制搜索框出來,其實這個和第一步效果剛好是相關的,
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;//區(qū)分是繪制搜索框還是外層圓 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(); } /** * 開始執(zhí)行動畫 */ private void startAnim() { drawTag = 1; serchStartAnim.start(); invalidate(); } /** * 開啟大圓執(zhí)行動畫 */ public void startBigCirCleAnim(){ serchStartAnim.removeAllUpdateListeners();//把上一個動畫監(jiān)聽移除 以免總成詭異的bug bigCircleAnim.start(); drawTag = 2; } /** * 最后繪制搜索框的動畫 */ public void drawSearchAanim(){ bigCircleAnim.removeAllUpdateListeners();//把上一個動畫監(jiān)聽移除 以免總成詭異的bug startDrawSearchAnim.start(); drawTag = 3; } /** * 動畫監(jiān)聽 */ 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) { //獲取動畫在單位時間內,每次執(zhí)行的值 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) { //獲取動畫在單位時間內,每次執(zhí)行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //獲取動畫在單位時間內,每次執(zhí)行的值 animPercent = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); startDrawSearchAnim .addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { //獲取動畫在單位時間內,每次執(zhí)行的值 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內核的自由及開放源代碼的操作系統(tǒng),主要使用于移動設備,如智能手機和平板電腦,由美國Google公司和開放手機聯(lián)盟領導及開發(fā)。
關于“Android如何使用Path實現(xiàn)搜索動態(tài)加載動畫效果”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。