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>
- Android自定义UI实战(基础篇2)---搜索酷炫界面
- Android自定义UI实战(基础篇2)---搜索酷炫界面【转】
- Android自定义UI实战(基础篇1)---组合控件封装
- Android自定义UI实战(基础篇3)---图标圆弧运动
- UI----自定义android界面的标题
- Android DIY(自定义) UI(控件,界面)
- android Splash简单炫酷UI界面
- Android炫酷的UI界面
- Android UI界面开发基础归纳
- Android 简单炫酷的UI界面 之欢迎界面
- Android界面UI优化2
- Swagger 自定义UI界面
- 如何去掉Android 6.0 UI界面上的搜索框
- Android中的UI界面控制方式和自定义View
- 自定义控件实战-Android UI模板设计(文末小彩蛋)
- Android自定义搜索界面,搜索词单独设置颜色
- shareSDK自定义分享界面UI
- shareSDK自定义分享界面UI
- 三村合建水厂问题研究
- orcle索引的建立修改及删除
- 地图---CoreLocation框架(学习笔记)
- 全角半角转换
- 算法 排序算法之快速排序
- Android自定义UI实战(基础篇2)---搜索酷炫界面
- 笔记
- Java编程基础-运算符
- Linux下安装配置VSFTP服务器
- krpano户型地图
- android低功耗蓝牙APP开发问题记录
- oracle-其他一些预定义异常
- 迭代Iteratio
- 设计模式之单例(Java)