編輯:關於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
Android Telephony Phone詳解
前言本文主要講解Telephony中Phone相關的知識,主要想講明白三件事情:Phone是什麼? Phone從哪裡來? Phone有什麼作用?1. Phone是什麼1.
Android OpenGL入門示例:繪制三角形和正方形 (附完整源碼)
Android上對OpenGl的支持是無縫的,所以才有眾多3D效果如此逼真的游戲,在Camera的一些流程中也有用到GLSurfaceView的情況。本文記錄OpenGL
Android中handler運行原理
在android中提供了一種異步回調機制Handler,使用它,我們可以在完成一個很長時間的任務後做出相應的通知 handler基本使用: 在主線
Android 仿今日頭條簡單的刷新效果實例代碼
點擊按鈕,先自動進行下拉刷新,也可以手動刷新,刷新完後,最後就多一行數據。有四個選項卡。前兩天導師要求做一個給本科學生預定機房座位的app,出發點來自這裡。做著做著遇到很