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

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

效果图如下:
Android自定义UI实战(基础篇2)---搜索酷炫界面_第1张图片

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

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

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

Android自定义UI实战(基础篇2)---搜索酷炫界面_第2张图片这里写图片描述

首先自定义一个类继承自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 绘制中心图片

@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<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();

效果如下:

Android自定义UI实战(基础篇2)---搜索酷炫界面_第3张图片

一个圆绘制成功了,接着就可以绘制余下的圆了,实现原理一样,

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份,均匀分配,看起来均匀变化。

现在来看一下实现效果:
Android自定义UI实战(基础篇2)---搜索酷炫界面_第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);  //旋转画布,实现游标以中心点旋转

就可以实现一个动态的效果。至此,就已经实现了开篇的效果

Android自定义UI实战(基础篇2)---搜索酷炫界面_第5张图片
相关代码如下:

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


<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>

你可能感兴趣的:(Android自定义UI实战)