Android自定义View学习总结

        转眼已经18年底了,在Androd这行已经混了3年,一直说写些东西,总是没有执行。有想法的时候没时间,有时间的时候没想法,今天终于开始写一些东西了。

        回首Androd,自定义View是使用最多的,也是我第一次有计划的去学习和整理的模块。以下是我总结的自定义View的一些用法,没有涉及底层原理。

一:自定义View需要重新的2个方法。

 1、onMeasure 方法

        通过onMeasure方法可以得到父类的specMode和size

  1.1、当父View的specMode为EXACTLY的时候:父View强加给子View一个确切的大小,有如下两种情况

       1.1.1) 子View的layout_width或者layout_height设置为MATCH_PARENT的时候,子View的specMode为EXACTLY

       1.1.2) 子View的layout_width或者layout_height设置为WRAP_CONTENT的时候,子View的specMode为AT_MOST

       1.1.3) 不论子view为match_parent或者wrap_content,resultSize都等于父类的size.

  1.2、当父view的specMode为AT_MOST的时候:父View强加给一个最大的size给子view,最大的size也就是父view的size

       1.2.1) 此时不论子view的为match_parent或者wrap_content,子view的specMode都为AT_MOST

       1.2.2) resultSize的大小被设置为父view的大小

  1.3、当父view的specMode为UNSPECIFIED的时候:

       1.3.1) 此时不论子view的为match_parent或者wrap_content,子view的specMode都为UNSPECIFIED

       1.3.2) 此时reusltSize = 0

2、onDraw方法

2.1、画笔

mPaint = new Paint(); //初始化

1、mPaint.setAntiAlias(true);//去除边缘锯齿,优化绘制效果

2、mPaint.setColor(Color.BLACK);//设置颜色

3、setStrokeWidth(float width) //设置画笔宽度 

4、//设置线冒样式,取值有Cap.ROUND(圆形线冒)、Cap.SQUARE(方形线冒)、Cap.BUTT(无线冒) 

    //注意:冒多出来的那块区域就是线帽!就相当于给原来的直线加上一个帽子一样,所以叫线帽 

    setStrokeCap(Paint.Cap cap)

5、//设置线段连接处样式,取值有:Join.MITER(结合处为锐角)、Join.Round(结合处为圆弧)、Join.BEVEL(结合处为直线) 

    setStrokeJoin(Paint.Join join) 

6、//设置笔画的倾斜度,90度拿画笔与30拿画笔,画出来的线条样式肯定是不一样的吧。

    setStrokeMiter(float miter) 

7、void reset() //清空画笔复位。

8、void set(Paint src) //设置一个外来Paint画笔

9、//获取与设置alpha值、颜色、ARGB等。

    void setARGB(int a, int r, int g, int b)

    int getAlpha()

    void setAlpha(int a)

    int getColor()

    void setColor(int color)

10、//获取与设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢,一般会开启。设置后会平滑一些;

    void setAntiAlias(boolean aa) 

11、//获取与设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满、图像更加清晰。

    final boolean isDither()

    void setDither(boolean dither) 

12、setPathEffect(PathEffect effect) //设置绘制路径的效果

13、CornerPathEffect //圆形拐角效果

14、paint.setPathEffect(newCornerPathEffect(100));//利用半径R=50的圆来代替原来两条直线间的夹角

15、DashPathEffect //虚线效果

16、//设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果

    setXfermode(Xfermode xfermode)

17、//设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等

    setMaskFilter(MaskFilter maskfilter)

18、//设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果

    setColorFilter(ColorFilter colorfilter)

19、//设置图像效果,使用Shader可以绘制出各种渐变效果

    setShader(Shader shader)

20、//在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色

    setShadowLayer(float radius ,float dx,floatdy,int color)

21、//设置画笔样式1.

    Paint.Style.FILL :填充内部

    Paint.Style.FILL_AND_STROKE:填充内部和描边

    Paint.Style.STROKE :仅描边、

2.2、画圆

1. canvas.drawCircle(cx, cy, radius,;//外圆

mPaint.setColor(Color.WHITE);

canvas.drawCircle(cx, cy, radius - mBorderWidth, mPaint);//内圆

2.3、绘画弧形圆

//画上面黑色半圆

mPaint.setStyle(Paint.Style.FILL);

mPaint.setColor(Color.BLACK);

RectF blackHalfRect = new RectF(cx - 45, cy - 90, cx + 45, cy);

canvas.drawArc(blackHalfRect, 270, 180, true, mPaint);

mPaint.setStyle(Paint.Style.FILL);

mPaint.setColor(Color.WHITE);

RectF whiteHalfRect = new RectF(cx - 45, cy, cx + 45, cy + 90);

canvas.drawArc(whiteHalfRect, 270, -180, true, mPaint);

   2.4、绘画半圆

RectF mRectf = new RectF(left, top, right,botton);

mPaint.setColor(Color.WHITE);

canvas.drawArc(mRectf, 270, 180, true, mPaint);

mPaint.setColor(Color.BLACK);

canvas.drawArc(mRectf, 270, -180, true, mPaint);

   2.5、贝塞尔曲线

    2.5.1、什么是贝塞尔曲线

    贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。主要结构:起始点、终止点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。

    2.5.2、贝塞尔曲线的分类

    2.5.2.1、一阶贝塞尔曲线(线段):

    公式:

    原理:

    由 P0 至 P1 的连续点, 描述的一条线段

    2.5.2.2、二阶贝塞尔曲线(抛物线):

    公式:

    原理:

    由 P0 至 P1 的连续点 Q0,描述一条线段。

    由 P1 至 P2 的连续点 Q1,描述一条线段。

    由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。

    2.5.2.3、三阶贝塞尔曲线:

    公式:

    原理:

   2.5.3、贝塞尔曲线的代码实现

   quadTo()方法从上一个点为起点开始绘制贝塞尔曲线,其中(x1,y1)为辅 助控制点,(x2,y2)为终点。

    Path mPath = new Path();

    mPath.moveTo(x0,y0);

    mPath.quadTo(x1,y1,x2,y2);

    如调用以上代码,即绘制起点(x0,y0),终点(x2,y2),辅助控制点(x1,y1)的贝塞尔曲线。因此,通过不断改变这三个点的位置,我们可以绘制出各种各样的曲线。

     cubicTo()方法从上一个点为起点开始绘制三阶贝塞尔曲线,其中(x1,y1),( x2, y2 )为辅助控制点,(x3,y3)为终点。

   2.5.4、贝塞尔曲线的应用

    2.5.4.1、波浪纹

    要实现一个波浪不断涌动的效果,这种效果在很多手机应用中可见,例如手机电量,内存剩余等。类似这种需要实现波浪的效果,我们需要绘制带有平滑自然效果的曲线,这时候就需要贝塞尔曲线来辅助了。

    原理图:

         图中的矩阵即为视图的可见范围,也就是我们手机常见的区域。通过属性动画类ValueAnimator不断改变点1的横坐标,随着点1横坐标向右移动,点2,点3,点4,点5,以及四个控制点的坐标随着点1的移动同时位移相同距离,每一次坐标点更新,我们调用一次invalidate()方法,调用draw重新绘制视图,绘制四段贝塞尔曲线。最后点1移动到原先点3的位置,这样就完成了一次动画。

    这样,通过循环不断的动画效果,我们就实现了波浪的效果。

    2.5.4.2、粘连体

    利用二阶贝塞尔曲线还可以实现,类似两种物体粘合在一起的效果,比如我们常用的qq,在qq聊天列表上有一个非常有意思的功能,就是当我们用手指移动聊天列表上的未读消息标志的时候,它与聊天列表会产生粘连的效果:

    现在,我们来模拟这种效果,利用学到的二阶贝塞尔曲线来绘制。

    我们看到原理图,基本构造为两个圆,和两端贝塞尔曲线,绘制贝塞尔曲线,由于这是一个二阶的贝塞尔曲线,我们只需要一个控制点,在这个图里,我们的两条贝塞尔曲线的两个控制点分别为(x1,y1)(x4, y4)的中点,(x2, y2)(x3, y3)的中点。

    从图中可以看出,我们的贝塞尔曲线由我们的控制点控制,控制点又是被起点和终点控制着,因此,当两个圆距离越大,曲线越趋于平缓,当两个圆距离越小,曲线的波动度越大,这样,我们想要的粘连的效果就实现了。另外,这里有一个还有角度(图中的45度角)可以用来控制,也可以作为控制曲线波动度的参数。

    通过以上分析,我们通过一个方法来绘制两个圆之间的粘连体路径:

    我们给控件设置一个粘连的最大距离,即如果两个圆之间的距离超过这个值,则不再绘制粘连体。

    2.5.4.3、弹性球

    三阶贝塞尔曲线,就是有两个控制点,它相比二阶曲线的优点是,由于控制点的增加,它能够更加轻松地绘制出更平滑更自然的曲线。

    如何绘制类似这种,看起来具有弹性球的滑动球,我们需要使用三阶贝塞尔曲线,那么首先如何用三阶贝塞尔曲线绘制出一个圆,这里有一篇文章,是关于如何用贝塞尔曲线绘制圆:http://spencermortensen.com/articles/bezier-circle/ ,大概意思是讲,我们需要一个值就是c = 0.552284749,如下图,要绘制右上角的圆弧,我们需要两个控制点,其中B就是一个控制点,我们需要保证AB = c *r,即可以画出1/4的圆弧,以此类推,连续画四段这样的圆弧,就可以画出一个标准的圆。

    接下来我们观察弹性球的运动,大概可以分为以下几个阶段:

1)开始启动,此时右边点位移,其他点不动

2)开始加速

3)减速

4)到达终点

5)回弹效果

上面完成了一个弹性球的封装,可以实现四个方向的运动,然后我们实现一个弹性球的loader

package com.zero.bezier.widget.elastic;


import android.animation.Animator;  

import android.animation.ValueAnimator;  

import android.graphics.Path;  

import android.graphics.PointF;  

import android.view.animation.AccelerateDecelerateInterpolator;  

/** 

* 弹性球 

* @author linzewu 

* @date 2016/6/1 

*/  

public class ElasticBall extends Ball {  

/** 

 * 向上运动 

 */  

private static final int DIRECTION_UP = 1;  

 /** 

 * 向下运动 

  */  

private static final int DIRECTION_DOWN = 2;  

 /** 

* 向左运动 

  */  

private static final int DIRECTION_LEFT = 3;  

 /** 

 * 向右运动 

  */  

private static final int DIRECTION_RIGHT = 4;  

 /** 

 * 运动方向 

 */  

private int mDirection;  

/** 

* 动画完成百分比(0~1) 

 */  

private float mAnimPercent;  

 /** 

 * 弹性距离 

 */  

private float mElasticDistance;  

 /** 

* 弹性比例 

*/  

private float mElasticPercent = 0.8f;  

 /** 

* 位移距离 

*/  

private float mMoveDistance;  

/** 

* 动画消费时间 

 */  

private long mDuration = 1500;  


 /** 

* 偏移值 

*/  

private float offsetTop, offsetBottom, offsetLeft, offsetRight;  

/** 

* 圆形偏移比例 

*/  

private float c = 0.551915024494f;  


private float c2 = 0.65f;  

 /** 

* 动画开始点 

*/  

private Ball mStartPoint;  


/** 

* 动画结束点 

*/  

private Ball mEndPoint;  


 /** 

 * 构造方法 

 * 

* @param x 圆心横坐标 

* @param y 圆心纵坐标 

* @param radius 圆半径 

*/  

public ElasticBall(float x, float y, float radius) {  

super(x, y, radius);  

init();  

 }  


private void init() {  

mElasticDistance = mElasticPercent * radius;  

offsetTop = c * radius;  

offsetBottom = c * radius;  

offsetLeft = c * radius;  

offsetRight = c * radius;  

}  


public interface ElasticBallInterface{  

void onChange(Path path);  

void onFinish();  

}  

private ElasticBallInterface mElasticBallInterface;  

/** 

* 对外公布方法,设置弹性比例 (0~1) 

 * @param elasticPercent 

 */  

 public void setElasticPercent(float elasticPercent) {  

 this. mElasticPercent= elasticPercent; 

 }  

 /** 

* 对外公布方法,设置动画时间 

* @param duration 

 */  

public void setDuration(long duration) {  

 this.mDuration = duration;  

 }  


/** 

 * 对外公布方法, 开启动画 

* @param endPoint 

*/  

public void startElasticAnim(PointF endPoint, ElasticBallInterface elasticBallInterface) {  

this.mEndPoint = new ElasticBall(endPoint.x, endPoint.y, radius);  

this.mStartPoint = new ElasticBall(x, y, radius);  

this.mStatusPoint1 = new ElasticBall(x, y, radius);  

this.mStatusPoint2 = new ElasticBall(x, y, radius);  

this.mStatusPoint3 = new ElasticBall(x, y, radius);  

this.mStatusPoint4 = new ElasticBall(x, y, radius);  

this.mStatusPoint5 = new ElasticBall(x, y, radius);  

this.mElasticBallInterface = elasticBallInterface;  

judgeDirection();  

mMoveDistance = getDistance(mStartPoint.x, mStatusPoint1.y, endPoint.x, endPoint.y);  

animStatus0();  

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);  

valueAnimator.setDuration(mDuration);  

valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());  

valueAnimator.start();  

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  

@Override  

public void onAnimationUpdate(ValueAnimator animation) {  

mAnimPercent = (float) animation.getAnimatedValue();  

if(mAnimPercent>=0 && mAnimPercent <= 0.2){  

animStatus1();  

 }  else if(mAnimPercent > 0.2 && mAnimPercent <= 0.5){  

animStatus2();  

 }  else if(mAnimPercent > 0.5 && mAnimPercent <= 0.8){  

 animStatus3();  

 }  else if(mAnimPercent > 0.8 && mAnimPercent <= 0.9){  

animStatus4();  

 }   else if(mAnimPercent > 0.9&&mAnimPercent <= 1){  

 animStatus5();  

 }  

 if (mElasticBallInterface != null) {  

 mElasticBallInterface.onChange(drawElasticCircle(topX, topY, offsetTop, offsetTop,  

 bottomX, bottomY, offsetBottom, offsetBottom,  

leftX, leftY, offsetLeft, offsetLeft,  

 rightX, rightY, offsetRight, offsetRight));  

 }  

}  

 });  

 valueAnimator.addListener(new Animator.AnimatorListener() {  

@Override  

public void onAnimationStart(Animator animation) {  


 }  

@Override  

 public void onAnimationEnd(Animator animation) {  

if (mElasticBallInterface != null) {  

mElasticBallInterface.onFinish();  

 }  

}  

@Override  

public void onAnimationCancel(Animator animation) {  

  }  

@Override  

 public void onAnimationRepeat(Animator animation) {  

}  

 });  

}  

 private void judgeDirection() {  

if (mEndPoint.x - mStartPoint.x > 0) {  

mDirection = DIRECTION_RIGHT;  

}else if (mEndPoint.x - mStartPoint.x < 0) {  

mDirection = DIRECTION_LEFT;  

}else if (mEndPoint.y - mStartPoint.x > 0) {  

 mDirection = DIRECTION_DOWN;  

 }else if (mEndPoint.y - mStartPoint.y < 0){  

mDirection = DIRECTION_UP;  

 }  

  }  

/**

 * 动画状态0 (初始状态:圆形) 

 */  

 private void animStatus0() {  

offsetTop = c * radius;  

offsetBottom = c * radius;  

offsetLeft = c * radius;  

offsetRight = c * radius;  

 }  

 private Ball mStatusPoint1;  

/** 

* 动画状态1 (0~0.2) 

 */  

private void animStatus1() {  

 float percent = mAnimPercent * 5f;  

if (mDirection == DIRECTION_LEFT) {  

 leftX = mStartPoint.leftX - percent * mElasticDistance;  

else if (mDirection == DIRECTION_RIGHT) {  

rightX = mStartPoint.rightX + percent * mElasticDistance;  

 } else if (mDirection == DIRECTION_UP) {  

 topY = mStartPoint.topY - percent * mElasticDistance;  

 } else if (mDirection == DIRECTION_DOWN) {  

  bottomY = mStartPoint.bottomY + percent * mElasticDistance;  

 }  

 mStatusPoint1.refresh(x, y, topX, topY, bottomX, bottomY,  

  leftX, leftY, rightX, rightY);  

  }  

private Ball mStatusPoint2;  

 /** 

 * 动画状态2 (0.2~0.5) 

  */  

 private void animStatus2() {  

 float percent = (float) ((mAnimPercent - 0.2) * (10f / 3));  

 if (mDirection == DIRECTION_LEFT) {  

 leftX = mStatusPoint1.leftX - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 x = mStatusPoint1.x - percent * (mMoveDistance / 2);  

rightX = mStatusPoint1.rightX - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 topX = x;  

 bottomX = x;  

//偏移值稍作变化  

 offsetTop = radius * c + radius * ( c2 - c ) * percent;  

offsetBottom = radius * c + radius * ( c2 - c ) * percent;  

else if (mDirection == DIRECTION_RIGHT) {  

 rightX = mStatusPoint1.rightX + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 x = mStatusPoint1.x + percent * (mMoveDistance / 2);  

leftX = mStatusPoint1.leftX + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 topX = x;  

 bottomX = x;  

//偏移值稍作变化  

 offsetTop = radius * c + radius * ( c2 - c ) * percent;  

offsetBottom = radius * c + radius * ( c2 - c ) * percent;  

else if (mDirection == DIRECTION_UP) {  

 topY = mStatusPoint1.topY - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

y = mStatusPoint1.y - percent * (mMoveDistance / 2);  

bottomY = mStatusPoint1.bottomY - percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 leftY = y;  

 rightY = y;  

 //偏移值稍作变化  

offsetLeft = radius * c + radius * ( c2 - c ) * percent;  

offsetRight = radius * c + radius * ( c2 - c ) * percent;  

else if (mDirection == DIRECTION_DOWN) {  

bottomY = mStatusPoint1.bottomY + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 y = mStatusPoint1.y + percent * (mMoveDistance / 2);  

 topY = mStatusPoint1.topY + percent * (mMoveDistance / 2 - mElasticDistance / 2 );  

 leftY = y;  

rightY = y;  

 //偏移值稍作变化  

offsetLeft = radius * c + radius * ( c2 - c ) * percent;  

 offsetRight = radius * c + radius * ( c2 - c ) * percent;  

  }  

 mStatusPoint2.refresh(x, y, topX, topY, bottomX, bottomY,  

 leftX, leftY, rightX, rightY);  

 }  

private Ball mStatusPoint3;   

 /** 

 * 动画状态3 (0.5~0.8) 

  */  

private void animStatus3() {  

float percent = (mAnimPercent - 0.5f) * (10f / 3f);  

 if (mDirection == DIRECTION_LEFT) {  

  leftX = mStatusPoint2.leftX - Math.abs(percent * (mEndPoint.rightX - mStatusPoint2.rightX));  

x = mStatusPoint2.x - Math.abs(percent * (mEndPoint.x - mStatusPoint2.x));  

 rightX = mStatusPoint2.rightX - Math.abs(percent * (mEndPoint.x - mStatusPoint2.x));  

 topX = x;  

 bottomX = x;  

 //偏移值稍作变化  

 offsetTop = radius * c2 - radius * ( c2 - c ) * percent;  

 offsetBottom = radius * c2 - radius * ( c2 - c ) * percent;  

  } else if (mDirection == DIRECTION_RIGHT) {  

rightX = mStatusPoint2.rightX + percent * (mEndPoint.rightX - mStatusPoint2.rightX);  

 x = mStatusPoint2.x + percent * (mEndPoint.x - mStatusPoint2.x);  

  leftX = mStatusPoint2.leftX + percent * (mEndPoint.x - mStatusPoint2.x);  

  topX = x;  

  bottomX = x;  

   //偏移值稍作变化  

  offsetTop = radius * c2 - radius * ( c2 - c ) * percent;  

 offsetBottom = radius * c2 - radius * ( c2 - c ) * percent;  

 } else if (mDirection == DIRECTION_UP) {  

topY = mStatusPoint2.topY - Math.abs(percent * (mEndPoint.topY - mStatusPoint2.topY));  

y = mStatusPoint2.y - Math.abs(percent * (mEndPoint.y - mStatusPoint2.y));  

 bottomY = mStatusPoint2.bottomY - Math.abs(percent * (mEndPoint.y - mStatusPoint2.y));  

leftY = y;  

 rightY = y;  

 //偏移值稍作变化  

offsetLeft = radius * c2 - radius * ( c2 - c ) * percent;  

 offsetRight = radius * c2 - radius * ( c2 - c ) * percent;  

 } else if (mDirection == DIRECTION_DOWN) {  

 bottomY = mStatusPoint2.bottomY + percent * (mEndPoint.bottomY - mStatusPoint2.bottomY);  

 y = mStatusPoint2.y + percent * (mEndPoint.y - mStatusPoint2.y);  

 topY = mStatusPoint2.topY + percent * (mEndPoint.y - mStatusPoint2.y);  

leftY = y;  

rightY = y;  

//偏移值稍作变化  

 offsetLeft = radius * c2 - radius * ( c2 - c ) * percent;  

offsetRight = radius * c2 - radius * ( c2 - c ) * percent;  

}  

mStatusPoint3.refresh(x, y, topX, topY, bottomX, bottomY,  

 leftX, leftY, rightX, rightY);  

 }   

private Ball mStatusPoint4;      

  /** 

  * 动画状态4 (0.8~0.9) 

  */  

private void animStatus4() {  

 float percent = (float) (mAnimPercent - 0.8) * 10;  

  if (mDirection == DIRECTION_LEFT) {  

 rightX = mStatusPoint3.rightX - percent * (Math.abs(mEndPoint.rightX - mStatusPoint3.rightX) + mElasticDistance/2);  

 //再做一次赋值,防止和终点不重合  

 leftX = mEndPoint.leftX;  

x = mEndPoint.x;  

  bottomX = mEndPoint.bottomX;  

  topX = mEndPoint.topX;  

  } else if (mDirection == DIRECTION_RIGHT) {  

  leftX = mStatusPoint3.leftX + percent * (mEndPoint.leftX - mStatusPoint3.leftX +  

     mElasticDistance/2);  

   //再做一次赋值,防止和终点不重合  

           rightX = mEndPoint.rightX;  

            x = mEndPoint.x;  

            bottomX = mEndPoint.bottomX;  

            topX = mEndPoint.topX;  

        } else if (mDirection == DIRECTION_UP) {  

            bottomY = mStatusPoint3.bottomY - percent * (Math.abs(mEndPoint.bottomY - mStatusPoint3  

                   .bottomY) + mElasticDistance/2);  

            //再做一次赋值,防止和终点不重合  

           topY = mEndPoint.topY;  

           y = mEndPoint.y;  

           leftY = mEndPoint.leftY;  

           rightY = mEndPoint.rightY;  

       } else if (mDirection == DIRECTION_DOWN) {  

            topY = mStatusPoint3.topY + percent * (mEndPoint.topY - mStatusPoint3  

                   .topY + mElasticDistance/2);  

            //再做一次赋值,防止和终点不重合  

            bottomY = mEndPoint.bottomY;  

           y = mEndPoint.y;  

           leftY = mEndPoint.leftY;  

           rightY = mEndPoint.rightY;  

        }  

        mStatusPoint4.refresh(x, y, topX, topY, bottomX, bottomY,  

               leftX, leftY, rightX, rightY);  

    }  


    private Ball mStatusPoint5;  


   /** 

    * 动画状态5 (0.9~1)回弹 

     */  

   private void animStatus5() {  

       float percent = (float) (mAnimPercent - 0.9) * 10;  

       if (mDirection == DIRECTION_LEFT) {  

           rightX = mStatusPoint4.rightX + percent * (mEndPoint.rightX - mStatusPoint4.rightX);  

       } else if (mDirection == DIRECTION_RIGHT) {  

           leftX = mStatusPoint4.leftX + percent * (mEndPoint.leftX - mStatusPoint4.leftX);  

       } else if (mDirection == DIRECTION_UP) {  

           bottomY = mStatusPoint4.bottomY + percent * (mEndPoint.bottomY - mStatusPoint4.bottomY);  

       } else if (mDirection == DIRECTION_DOWN) {  

          topY = mStatusPoint4.topY + percent * (mEndPoint.topY - mStatusPoint4.topY);  

      }  

       mStatusPoint5.refresh(x, y, topX, topY, bottomX, bottomY,  

               leftX, leftY, rightX, rightY);  

  }  


    /** 

     * 绘制弹性圆 

    * 通过绘制四段三阶贝塞尔曲线,来实现有弹性变化的圆 

     * @param topX 

    * @param topY 

   * @param offsetTop1 

   * @param offsetTop2 

    * @param bottomX 

   * @param bottomY 

   * @param offsetBottom1 

    * @param offsetBottom2 

     * @param leftX 

    * @param leftY 

    * @param offsetLeft1 

   * @param offsetLeft2 

     * @param rightX 

     * @param rightY 

   * @param offsetRight1 

   * @param offsetRight2 

   * @return 

   */  

    private Path drawElasticCircle(  

            float topX, float topY, float offsetTop1, float offsetTop2,  

            float bottomX, float bottomY, float offsetBottom1, float offsetBottom2,  

            float leftX, float leftY, float offsetLeft1, float offsetLeft2,  

          float rightX, float rightY, float offsetRight1, float offsetRight2  

 ) {  

  /** 

 * 绘制每一段三阶贝塞尔曲线需要两个控制点 

  */  

PointF controlTop1, controlTop2, controlBottom1, controlBottom2,  

 controlLeft1, controlLeft2, controlRight1, controlRight2;  

controlTop1 = new PointF();  

controlTop1.x = topX - offsetTop1;  

 controlTop1.y = topY;  

controlTop2 = new PointF();  

controlTop2.x = topX + offsetTop2;  

 controlTop2.y = topY;  

controlBottom1 = new PointF();  

controlBottom1.x = bottomX - offsetBottom1;  

controlBottom1.y = bottomY;  

controlBottom2 = new PointF();  

 controlBottom2.x = bottomX + offsetBottom2;  

 controlBottom2.y = bottomY;  

 controlLeft1 = new PointF();  

controlLeft1.x = leftX;  

 controlLeft1.y = leftY - offsetLeft1;  

 controlLeft2 = new PointF();  

controlLeft2.x = leftX;  

 controlLeft2.y = leftY + offsetLeft2;  

controlRight1 = new PointF();  

controlRight1.x = rightX;  

 controlRight1.y = rightY - offsetRight1;  

 controlRight2 = new PointF();  

 controlRight2.x = rightX;  

 controlRight2.y = rightY + offsetRight2;  


Path path = new Path();  

 /** 

 * 绘制top到left的圆弧 

  */  

path.moveTo(topX, topY);  

 path.cubicTo(controlTop1.x, controlTop1.y, controlLeft1.x, controlLeft1.y, leftX, leftY);  

 /** 

* 绘制left到bottom的圆弧 

 */  

path.cubicTo(controlLeft2.x ,controlLeft2.y, controlBottom1.x, controlBottom1.y, bottomX,  bottomY);  

/** 

 * 绘制bottom到right的圆弧 

 */  

       path.cubicTo(controlBottom2.x, controlBottom2.y, controlRight2.x, controlRight2.y,  rightX, rightY);  

       /** 

        * 绘制right到top的圆弧 

         */  

       path.cubicTo(controlRight1.x, controlRight1.y, controlTop2.x, controlTop2.y, topX, topY);  

return path;  

}  

  /** 

 * 求两点之间的距离 

 * @param x1 第一个点的横坐标 

 * @param y1 第一个点的纵坐标 

* @param x2 第二个点的横坐标 

* @param y2 第二个点的纵坐标 

* @return 两点距离 

*/  

private float getDistance(float x1, float y1, float x2, float y2) {  

  return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));  

}   

}  

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 今天女儿学校要表演,早上七点的时候赶紧往回家赶了。七点半匆匆忙忙吃了肠粉就送她去了。 中午表演完因为宝爸下午要上班...
    Ada左岸阅读 138评论 0 0
  • 我平时叫在嘴上最多的是爸,手机联系人上标着老爸,微信里备注了芭比,写到这时更愿意在心底称呼父亲,这么多的称呼...
    梨花心事阅读 309评论 0 3
  • 这几天有意疏远哥哥。今天早上道早安时,哥哥用力的看着我的眼睛,我还是躲开了。后来他外出办公,嘟了一下我的腰。前天还...
    哈姆Y特公主阅读 104评论 0 0
  • 初次接触到这个题目时,是在基础写作课上,老师给我们推荐了这位天才作家的散文,至今想不起来推荐理由,满脑子想的是一位...
    真射手伪文艺阅读 1,997评论 0 2