Android高级UICanvas和Path使用——自定义SearchView搜索框动画
2017-07-13 14:46
459 查看
预览:
一、实现思路:
圆形搜索框长度应该与下面的横线的长度相同
整个图形分成三部分:圆、与圆相连的手柄、与手柄相连的横线
动画过程中圆与手柄是一体的,其长度与下面的横线的长度成相反的关系
二、实现步骤:
1,初始化画布及画笔
2,分别绘制圆,手柄及下边的横线
3,启动动画,更改相应的比例参数实现该效果
1,初始化画布及画笔:
2、计算总的路径的长度
round为圆的周长,lLength为定义的手柄的长度,length则为总长度,也就是下边横线的总长度
3、重新ondraw方法开始绘制
绘制圆弧使用path.addArc(RectF oval, float startAngle, float sweepAngle)方法:
path.addArc方法用于绘制圆弧,这个圆弧取自RectF矩形的内接椭圆上的一部分,圆弧长度由后两个参数决定
startAngle:起始位置的角度值
sweepAngle:旋转的角度值
Math.sin(2*Math.PI/360*45):此处计算正切值传入的参数是弧度值,而不是角度值
4、动画
展开动画
还原动画
最后在MotionEvent.ACTION_DOWN 的时候启动动画就行了
完整代码:
一、实现思路:
圆形搜索框长度应该与下面的横线的长度相同
整个图形分成三部分:圆、与圆相连的手柄、与手柄相连的横线
动画过程中圆与手柄是一体的,其长度与下面的横线的长度成相反的关系
二、实现步骤:
1,初始化画布及画笔
2,分别绘制圆,手柄及下边的横线
3,启动动画,更改相应的比例参数实现该效果
1,初始化画布及画笔:
path = new Path(); paint = new Paint(); paint.setColor(Color.RED); paint.setFlags(Paint.DITHER_FLAG); paint.setStyle(Paint.Style.STROKE); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(8); paint.setAntiAlias(true); paint.setDither(true);
2、计算总的路径的长度
round = (float) (2*Math.PI*radius); length = round+lLength;
round为圆的周长,lLength为定义的手柄的长度,length则为总长度,也就是下边横线的总长度
3、重新ondraw方法开始绘制
绘制圆弧使用path.addArc(RectF oval, float startAngle, float sweepAngle)方法:
path.addArc方法用于绘制圆弧,这个圆弧取自RectF矩形的内接椭圆上的一部分,圆弧长度由后两个参数决定
startAngle:起始位置的角度值
sweepAngle:旋转的角度值
@Override protected void onDraw(Canvas canvas) { path.reset(); //mpro 用于更改圆弧的角度,-360*mpro 为顺时针旋转 if(mpro>0){ path.addArc(mRectF,45,-360*mpro); } canvas.drawPath(path,paint); canvas.save(); //绘制手柄的直线,此处也可以将画布旋转45度以后直接绘制,此处采用正切值计算起始点的坐标 canvas.drawLine( pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength), pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength), pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)), pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)), paint); //绘制下边的直线 canvas.drawLine(pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-lineLen, pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)), pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)), pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)), paint); }
Math.sin(2*Math.PI/360*45):此处计算正切值传入的参数是弧度值,而不是角度值
4、动画
展开动画
public void startViewAnimation(){ if(valueAnimator!=null&&valueAnimator.isRunning()){ return; } valueAnimator = ValueAnimator.ofFloat(0,length); valueAnimator.setDuration((long) (2000)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float curLen = (float) animation.getAnimatedValue(); float lingTh = curLen -round; //修改圆弧坐标点 pointF.x+=2; mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius); if(lingTh>0){ lLength = (int) (80-lingTh); }else{ if(curLen<=round){ mpro= 1-curLen/round - 0.009f; } } lineLen = curLen; invalidate(); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { lLength = 80; pointF.x = 100; } @Override public void onAnimationEnd(Animator animation) { endViewAnimation(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); valueAnimator.start();
还原动画
public void endViewAnimation(){ ValueAnimator valueAnimator = ValueAnimator.ofFloat(length,0); valueAnimator.setDuration((long) (2000)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float curLen = (float) animation.getAnimatedValue(); float lingTh = curLen -round; pointF.x-=2; mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius); if(lingTh>0){ lLength = (int) (80-lingTh); }else{ if(curLen<=round){ mpro= 1-curLen/round; } } lineLen = curLen; invalidate(); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { startViewAnimation(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); valueAnimator.start(); }
最后在MotionEvent.ACTION_DOWN 的时候启动动画就行了
完整代码:
package com.example.flowproject;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
public class DrawView extends View {
private float round;
private Path path;
private Paint paint = null;
private int VIEW_WIDTH = 800;
private int VIEW_HEIGHT = 600;
Bitmap cacheBitmap = null;
//定义缓冲区Cache的Canvas对象
Canvas cacheCanvas = null;
private RectF mRectF;
public float mpro = -1;
float length;
float lineLen;
PointF pointF;
int radius = 50;
int lLength = LINE_LENGTH;
private static final int LINE_LENGTH = 80;
private ValueAnimator valueAnimator;
public DrawView(Context context) {
this(context,null);
pointF = new PointF(100,800);
mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
mpro= 0;
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
//创建一个与该VIew相同大小的缓冲区
cacheBitmap = Bitmap.createBitmap(VIEW_WIDTH,VIEW_HEIGHT,Bitmap.Config.ARGB_8888);
//创建缓冲区Cache的Canvas对象
cacheCanvas = new Canvas();
path = new Path();
//设置cacheCanvas将会绘制到内存的bitmap上
cacheCanvas.setBitmap(cacheBitmap);
paint = new Paint();
paint.setColor(Color.RED);
paint.setFlags(Paint.DITHER_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(8);
paint.setAntiAlias(true);
paint.setDither(true);
round = (float) (2*Math.PI*radius); length = round+lLength;
}
@Override
protected void onDraw(Canvas canvas) {
path.reset();
//mpro为圆的角度的进度,当mpro==0时,则整个圆绘制完成
if(mpro>0){
path.addArc(mRectF,45,-360*mpro);
}
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawPath(path,paint);
canvas.save();
canvas.drawLine(
pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength),
pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength),
pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
paint);
canvas.drawLine(pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-lineLen,
pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
paint);
}
public void startViewAnimation(){
if(valueAnimator!=null&&valueAnimator.isRunning()){
return;
}
valueAnimator = ValueAnimator.ofFloat(0,length);
valueAnimator.setDuration((long) (2000));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float curLen = (float) animation.getAnimatedValue();
float lingTh = curLen -round;
pointF.x+=2;
mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
if(lingTh>0){
lLength = (int) (80-lingTh);
}else{
if(curLen<=round){
mpro= 1-curLen/round - 0.009f;
}
}
lineLen = curLen;
invalidate();
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
lLength = 80;
pointF.x = 100;
}
@Override
public void onAnimationEnd(Animator animation) {
endViewAnimation();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
valueAnimator.start();
}
public void endViewAnimation(){ ValueAnimator valueAnimator = ValueAnimator.ofFloat(length,0); valueAnimator.setDuration((long) (2000)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float curLen = (float) animation.getAnimatedValue(); float lingTh = curLen -round; pointF.x-=2; mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius); if(lingTh>0){ lLength = (int) (80-lingTh); }else{ if(curLen<=round){ mpro= 1-curLen/round; } } lineLen = curLen; invalidate(); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { startViewAnimation(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); valueAnimator.start(); }
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
startViewAnimation();
break;
}
return true;
}
}
相关文章推荐
- Android自定义view --Path 的高级用法之-搜索按钮动画
- Android自定义view Path 的高级用法之搜索按钮动画
- 创建Material Design风格的Android应用--使用自定义动画
- [Android]Fragment自定义动画、动画监听以及兼容性包使用 - Ginsan
- Android 动画--使用Path来规划动画的轨迹
- Android自定义动画框架让View实现Path动画
- Android自定义view之path类描绘二阶贝塞尔曲线+属性动画(模仿QQ账号信息曲线动画)
- 创建Material Design风格的Android应用--使用自定义动画
- android canvas\paint\path简单使用(自定义view必学)
- Android使用自定义View绘制渐隐渐现动画
- Android 自定义View——Path的使用
- android 使用Path实现搜索动态加载动画效果
- Android SVG动画PathView源码解析与使用教程(API 14)
- 安卓开发之使用PathMeasure自定义加载动画控件
- [Android]Fragment自定义动画、动画监听以及兼容性包使用
- Android使用Path自定义波浪加载View
- Android自定义控件之二 使用path绘图
- Android 自定义View——Path的使用
- Android自定义View系列之Path绘制仿支付宝支付成功动画
- Android -- 自定义View小Demo,关于Path类的使用(一)