Android自定义UI实战(基础篇2)---搜索酷炫界面

来源:互联网 发布:windows live id注册 编辑:程序博客网 时间:2024/06/08 04:19

在实现搜索功能的时候,比如蓝牙搜索,附近热点搜索等,通常我们需要一个比较友好的界面,以下通过自定义View来实现一个搜索界面。

效果图如下:
这里写图片描述

当实现一个这样的动画的时候,思路是这样的呢?将整个View拆分,可以分为三个部分。

第一部分: 实现中间的图片
第二部分: 实现扩散的圆
第三部分: 实现游标转动

这样一个酷炫的搜索效果就出来了,用到的资源文件主要有两张图片:

这里写图片描述这里写图片描述

首先自定义一个类继承自View,实现对应的构造方法,添加自定义属性。上篇有详细的实现流程。
相关代码如下:
定义两张图片的属性

<declare-styleable name="SearchAnimation">    <attr name="drawable_search_center" format="reference"/>    <attr name="drawable_search_cursor" format="reference"/></declare-styleable>

获取属性:

/** * 取得相关自定义属性 * @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 绘制中心图片

@Overrideprotected 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<SearchRadius> {    @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

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#e8e8e8"    android:orientation="vertical">    <com.mirko.customeitem.view.SearchAnimation        android:layout_width="match_parent"        android:layout_height="match_parent"        app:drawable_search_cursor="@drawable/search_circle"        app:drawable_search_center="@drawable/search_circle_center" /></LinearLayout>
2 0
原创粉丝点击