編輯:關於Android編程
在實現搜索功能的時候,比如藍牙搜索,附近熱點搜索等,通常我們需要一個比較友好的界面,以下通過自定義View來實現一個搜索界面。
效果圖如下:
當實現一個這樣的動畫的時候,思路是這樣的呢?將整個View拆分,可以分為三個部分。
第一部分: 實現中間的圖片
第二部分: 實現擴散的圓
第三部分: 實現游標轉動
這樣一個酷炫的搜索效果就出來了,用到的資源文件主要有兩張圖片:
首先自定義一個類繼承自View,實現對應的構造方法,添加自定義屬性。上篇有詳細的實現流程。
相關代碼如下:
定義兩張圖片的屬性
獲取屬性:
/** * 取得相關自定義屬性 * @param context * @param attrs */ private void initAttrs(Context context,AttributeSet attrs){ mContext = context; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SearchAnimation); drawableSearchCenter = typedArray.getDrawable(R.styleable.SearchAnimation_drawable_search_center); drawableSearchCursor = typedArray.getDrawable(R.styleable.SearchAnimation_drawable_search_cursor); typedArray.recycle(); }
1 繪制中心圖片
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //首先來繪制中心的圖片 dsCenterW = drawableSearchCenter.getIntrinsicWidth(); //獲取到中心圖片的寬高 dsCenterH = drawableSearchCenter.getIntrinsicHeight(); centerX = ViewSizeHelper.getDeviceWidth((Activity) mContext)/2; //獲取到屏幕的寬高 centerY = ViewSizeHelper.getDeviceHeight((Activity) mContext)/2; drawableSearchCenter.setBounds(centerX- (dsCenterW/2),centerY- (dsCenterH/2), centerX+ (dsCenterW/2),centerY+(dsCenterH/2)); //指定Drawable的繪制區域 drawableSearchCenter.draw(canvas); //繪制drawable到畫布上 }
中心圖片就簡單的實現了。
2 實現擴散圓
圓的動態變化,使用ValueAnimator來實現。通過改變圓的半徑,不斷的重繪就可以實現這種效果了,
canvas.save(); canvas.drawCircle(centerX, centerY, radius, mPaint); canvas.restore();
重點是實現radius的變化,這裡使用ValueAnimator,自定義Evalutor來實現(使用ofObject )
下面來看如何實現自定義的Evalutor
首先定義一個類
/** * Created by Mirko on 2016/11/9 20:47. */ public class SearchRadius { private int radius; public SearchRadius(int radius){ this.radius = radius; } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; } }
創建自定義的Evalutor
/** * Created by Mirko on 2016/11/9 20:50. */ public class RadiusEvaluator implements TypeEvaluator{ @Override public SearchRadius evaluate(float fraction, SearchRadius startValue, SearchRadius endValue) { int start = startValue.getRadius(); int end = endValue.getRadius(); int curValue = (int)(start + fraction * (end - start)); //根據初始值和結束值計算出當前值。 return new SearchRadius(curValue); } }
完成上述步驟後,來實現圓的擴散動畫,代碼如下:
private void doAnimCicle1(){ //這裡起始圓以中心圓的高度作直徑 ValueAnimator valueAnimator = ValueAnimator.ofObject(new RadiusEvaluator(),new SearchRadius(dsCenterH/2),new SearchRadius(centerX)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius1 = (SearchRadius)animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setDuration(delayTime); //控制動畫的執行時間 valueAnimator.setInterpolator(new DecelerateInterpolator()); //這裡使用減速插值器 valueAnimator.setRepeatMode(ValueAnimator.RESTART); //設置重復方式 valueAnimator.setRepeatCount(ValueAnimator.INFINITE); //設置無限重復 valueAnimator.start(); }
然後在初始化的時候調用 doAnimCicle1方法來啟動動畫
實現代碼比較簡單,在這裡通過ofObject 自定義Evalutor的方式來實現,熟悉一下自定義Evalutor的用法,此處使用ofFloat
就可以實現,實際使用 ofFloat 就可以了。
現在在onDraw 裡面繪制當前的圓
canvas.save(); canvas.drawCircle(centerX, centerY, circleRadius1.getRadius(), mPaint); canvas.restore();
效果如下:
一個圓繪制成功了,接著就可以繪制余下的圓了,實現原理一樣,
private void doAnimCicle2(){ ValueAnimator valueAnimator = ValueAnimator.ofObject(new RadiusEvaluator(),new SearchRadius(dsCenterH/2),new SearchRadius(centerX)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius1 = (SearchRadius)animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setDuration(delayTime); //控制動畫的執行時間 valueAnimator.setStartDelay(delayTime/4); //設置延時開始的時間 valueAnimator.setInterpolator(new DecelerateInterpolator()); //這裡使用減速插值器 valueAnimator.setRepeatMode(ValueAnimator.RESTART); //設置重復方式 valueAnimator.setRepeatCount(ValueAnimator.INFINITE); //設置無限重復 valueAnimator.start(); } ... 省略其他的實現。
在這裡通過延時啟動動畫,實現圓的先後繪制順序setStartDelay根據實際需要設置,在這裡,將delayTime 分成4份,均勻分配,看起來均勻變化。
現在來看一下實現效果:
4個圓的效果有了,此時並沒有顏色慢慢變淡的效果,只需要在每次繪制圓形的時候將畫筆的透明度改變就可以了
mPaint.setAlpha(((centerX-circleRadius2.getRadius())*circleAlpha)/centerX);
這裡計算出的結果根據圓的半徑增大而減小。
3 實現游標轉動
//繪制光標的圖片 canvas.save(); dsCursorW = drawableSearchCursor.getIntrinsicWidth(); dsCursorH = drawableSearchCursor.getIntrinsicHeight(); drawableSearchCursor.setBounds(centerX- (dsCursorW/2),centerY- (dsCursorH/2), centerX+ (dsCursorW/2),centerY+(dsCursorH/2)); drawableSearchCursor.draw(canvas); canvas.restore();
此時,游標圖片已經繪制好了,下面來實現它的轉動,只需要不斷的旋轉畫布,就可以實現轉動效果。
先來獲取旋轉角度的變化值
private void doAnimCursor(){ ValueAnimator valueAnimator = ValueAnimator.ofInt(0,centerX); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius = (int)animation.getAnimatedValue(); degree = (circleRadius*2)*360/centerX; //畫布從0到360度進行旋轉,*2表示一個圓動作完,游標轉動2圈 invalidate(); } }); valueAnimator.setDuration(delayTime); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setRepeatMode(ValueAnimator.RESTART); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.start(); }
在獲取到變化的角度值後通過
canvas.rotate(degree,centerX,centerY); //旋轉畫布,實現游標以中心點旋轉
就可以實現一個動態的效果。至此,就已經實現了開篇的效果
相關代碼如下:
SearchAnimation
/** * Created by Mirko on 2016/11/9 19:09. */ public class SearchAnimation extends View{ private Context mContext; private Paint mPaint; private int centerX,centerY;//屏幕的中心點 private int dsCenterW,dsCenterH;//中心圖片的寬高 private int dsCursorW,dsCursorH;//光標圖片的寬高 private int strokWidth = 5; //畫筆大小 private int circleAlpha = 70; private int delayTime = 8000; //一個圓動畫執行的時間 private float degree; private int circleRadius ; private SearchRadius circleRadius1 = new SearchRadius(0); private SearchRadius circleRadius2 = new SearchRadius(0); private SearchRadius circleRadius3 = new SearchRadius(0); private SearchRadius circleRadius4 = new SearchRadius(0); private float circleRadiusF; private ValueAnimator valueAnimator; private Drawable drawableSearchCursor; private Drawable drawableSearchCenter; public SearchAnimation(Context context) { super(context); } public SearchAnimation(Context context, AttributeSet attrs) { super(context, attrs); init(context,attrs); } public SearchAnimation(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context,attrs); } private void init(Context context,AttributeSet attrs){ initAttrs(context,attrs); initPaint(); initAnim(); } /** * 取得相關自定義屬性 * @param context * @param attrs */ private void initAttrs(Context context,AttributeSet attrs){ mContext = context; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SearchAnimation); drawableSearchCenter = typedArray.getDrawable(R.styleable.SearchAnimation_drawable_search_center); drawableSearchCursor = typedArray.getDrawable(R.styleable.SearchAnimation_drawable_search_cursor); typedArray.recycle(); } private void initPaint(){ mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(0X46FC3232); } private void initAnim(){ dsCenterW = drawableSearchCenter.getIntrinsicWidth(); //獲取到中心圖片的寬高 dsCenterH = drawableSearchCenter.getIntrinsicHeight(); centerX = ViewSizeHelper.getDeviceWidth((Activity) mContext)/2; //獲取到屏幕的寬高 centerY = ViewSizeHelper.getDeviceHeight((Activity) mContext)/2; doAnimCicle1(); doAnimCicle2(); doAnimCicle3(); doAnimCicle4(); doAnimCursor(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //首先來繪制中心的圖片 drawableSearchCenter.setBounds(centerX- (dsCenterW/2),centerY- (dsCenterH/2), centerX+ (dsCenterW/2),centerY+(dsCenterH/2)); drawableSearchCenter.draw(canvas); //繪制光標的圖片 canvas.save(); canvas.rotate(degree,centerX,centerY); //旋轉畫布,實現游標的旋轉 dsCursorW = drawableSearchCursor.getIntrinsicWidth(); dsCursorH = drawableSearchCursor.getIntrinsicHeight(); drawableSearchCursor.setBounds(centerX- (dsCursorW/2),centerY- (dsCursorH/2), centerX+ (dsCursorW/2),centerY+(dsCursorH/2)); drawableSearchCursor.draw(canvas); canvas.restore(); //繪制圓,4個 mPaint.setStrokeWidth(strokWidth); canvas.save(); mPaint.setAlpha(((centerX-circleRadius1.getRadius())*circleAlpha)/centerX); //設置透明度 canvas.drawCircle(centerX, centerY, circleRadius1.getRadius(), mPaint); canvas.restore(); canvas.save(); mPaint.setAlpha(((centerX-circleRadius2.getRadius())*circleAlpha)/centerX); canvas.drawCircle(centerX, centerY, circleRadius2.getRadius(), mPaint); canvas.restore(); canvas.save(); mPaint.setAlpha(((centerX-circleRadius3.getRadius())*circleAlpha)/centerX); canvas.drawCircle(centerX, centerY, circleRadius3.getRadius(), mPaint); canvas.restore(); canvas.save(); mPaint.setAlpha(((centerX-circleRadius4.getRadius())*circleAlpha)/centerX); canvas.drawCircle(centerX, centerY, circleRadius4.getRadius(), mPaint); canvas.restore(); } private void setAnimParams(ValueAnimator valueAnimator){ valueAnimator.setDuration(delayTime); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.setRepeatMode(ValueAnimator.RESTART); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.start(); } private void doAnimCicle1(){ ValueAnimator valueAnimator = ValueAnimator.ofObject(new RadiusEvaluator(),new SearchRadius(dsCenterH/2),new SearchRadius(centerX)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius1 = (SearchRadius)animation.getAnimatedValue(); invalidate(); } }); setAnimParams(valueAnimator); } private void doAnimCicle2(){ ValueAnimator valueAnimator = ValueAnimator.ofObject(new RadiusEvaluator(),new SearchRadius(dsCenterH/2),new SearchRadius(centerX)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius2 = (SearchRadius)animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setStartDelay(delayTime/4); setAnimParams(valueAnimator); } private void doAnimCicle3(){ ValueAnimator valueAnimator = ValueAnimator.ofObject(new RadiusEvaluator(),new SearchRadius(dsCenterH/2),new SearchRadius(centerX)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius3 = (SearchRadius)animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setStartDelay(delayTime/2); setAnimParams(valueAnimator); } private void doAnimCicle4(){ ValueAnimator valueAnimator = ValueAnimator.ofObject(new RadiusEvaluator(),new SearchRadius(dsCenterH/2),new SearchRadius(centerX)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius4 = (SearchRadius)animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setStartDelay(delayTime/4*3); setAnimParams(valueAnimator); } private void doAnimCursor(){ ValueAnimator valueAnimator = ValueAnimator.ofInt(0,centerX); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleRadius = (int)animation.getAnimatedValue(); degree = (circleRadius*2)*360/centerX; //畫布從0到360度進行旋轉 invalidate(); } }); valueAnimator.setDuration(delayTime); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setRepeatMode(ValueAnimator.RESTART); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.start(); } }
activity_search.xml
前言本文主要講解Telephony中Phone相關的知識,主要想講明白三件事情:Phone是什麼? Phone從哪裡來? Phone有什麼作用?1. Phone是什麼1.
Android上對OpenGl的支持是無縫的,所以才有眾多3D效果如此逼真的游戲,在Camera的一些流程中也有用到GLSurfaceView的情況。本文記錄OpenGL
在android中提供了一種異步回調機制Handler,使用它,我們可以在完成一個很長時間的任務後做出相應的通知 handler基本使用: 在主線
點擊按鈕,先自動進行下拉刷新,也可以手動刷新,刷新完後,最後就多一行數據。有四個選項卡。前兩天導師要求做一個給本科學生預定機房座位的app,出發點來自這裡。做著做著遇到很