android动图状态延迟获取到,android实现牛顿摆

本文介绍了一位开发者如何通过自定义View在Android中实现牛顿摆动画,详细阐述了从获取自定义参数、计算View大小、绘制摆球到计算运动间隔的整个过程。博客中还提及了物理知识的应用,如重力加速度,并分享了在项目中学到的其他知识点,如MeasureSpec的理解。此外,作者提供了GitHub项目链接,供读者参考学习。
摘要由CSDN通过智能技术生成

上次看到一个小伙伴实现了牛顿摆,发现挺有意思的,于是乎自己也想着做一个,虽然说是重复造轮子,我看中的是这个过程,顺便巩固一下自定义view,并且我做了一个扩展,可以自定义线和小球的尺寸与颜色,还有初始角度。效果图如下:

0818b9ca8b590ca3270a3433284dd417.png

其实通过做这个项目可以扩展学习到很多姿势,首当其冲的就是物理姿势(我发现我连重力加速等于多少都忘了,so sad)。通过一步步推算得出角速度与偏移角度的公式(在线编辑数学公式网站):

0818b9ca8b590ca3270a3433284dd417.png 为啥要算这个角速度与偏移角度的关系?

其实我要的是view绘制的频率,知道了角速度,我们就知道偏移一个单位的角度需要间隔多长的时间,这个时间就是我们绘制的间隔,间隔的长短就可以提现出速度的快慢。

自定义View步骤:

1、新建一个类

继承自View;

2、在构造函数中获取自定义参数:

先在res/values目录下新建attrs.xml文件,定义我们要使用属性的名字:

这样在使用我们的自定义View时就能设置自定义属性:

android:layout_width="300dp"

android:layout_height="200dp"

android:background="#00ee00"

test:ballColor="#CCCCCC"

test:lineColor="#333333"

test:lineWidth="2dp"

test:radius="10dp"

test:initAngle="30"/>

然后就可以在构造函数中获取设置的自定义参数值了:

public NewtonCradle(Context context) {

this(context, null);

}

public NewtonCradle(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public NewtonCradle(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NewtonCradle, defStyleAttr, 0);

lineColor = typedArray.getColor(R.styleable.NewtonCradle_lineColor, Color.BLACK);

ballColor = typedArray.getColor(R.styleable.NewtonCradle_ballColor, Color.BLACK);

lineWidth = typedArray.getDimension(R.styleable.NewtonCradle_lineWidth, 1f);

radius = typedArray.getDimension(R.styleable.NewtonCradle_radius, 10f);

initAngle = typedArray.getInt(R.styleable.NewtonCradle_initAngle, 10);

swingAngle = initAngle; // 初始偏移角度

typedArray.recycle();

}注意使用完typedArray后释放。

3、在onMeasure方法中计算View的大小

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int width;

int height;

int widthSize;

int widthMode;

int heightSize;

int heightMode;

widthSize = MeasureSpec.getSize(widthMeasureSpec);

widthMode = MeasureSpec.getMode(widthMeasureSpec);

heightSize = MeasureSpec.getSize(heightMeasureSpec);

heightMode = MeasureSpec.getMode(heightMeasureSpec);

if (widthMode == MeasureSpec.EXACTLY) {

width = widthSize;

} else {

width = (int) (2 * lineLength + 10 * radius);

}

if (heightMode == MeasureSpec.EXACTLY) {

height = heightSize;

} else {

height = (int) (lineLength + 2 * radius);

}

// 根据宽高获取符合的最小线长

lineLength = (int) Math.min((width-10*radius)/2, height-2*radius);

setMeasuredDimension(width, height);

init();

}这时又百度了一下MeasureSpec的知识,所以只有自己动手才能发现发现知识的死角。当指定view精确大小时,使用指定的大小;没有指定精确大小时,根据线长lineLength和小球半径radius来计算最小宽度和高度,lineLength有一个默认初始值。根据宽高计算最小线长。

同时初始化画笔Paint:

private void init() {

linePaint = new Paint();

linePaint.setColor(lineColor);

linePaint.setStrokeWidth(lineWidth);

linePaint.setStyle(Paint.Style.FILL);

ballPaint = new Paint();

ballPaint.setColor(ballColor);

ballPaint.setStyle(Paint.Style.FILL);

}

4、在onDraw绘制摆球

@Override

protected void onDraw(Canvas canvas) {

int width = getWidth();

// 画中间的圆

canvas.drawLine(width/2, 0, width/2, lineLength, linePaint);

canvas.drawCircle(width/2, lineLength, radius, ballPaint);

// 画左一的圆

canvas.drawLine(width/2-2*radius, 0, width/2-2*radius, lineLength, linePaint);

canvas.drawCircle(width/2-2*radius, lineLength, radius, ballPaint);

// 画右一的圆

canvas.drawLine(width/2+2*radius, 0, width/2+2*radius, lineLength, linePaint);

canvas.drawCircle(width/2+2*radius, lineLength, radius, ballPaint);

pSswingAngle = Math.toRadians(swingAngle);

switch (location) {

case LOCATION_LEFT:

// 画右二的圆

canvas.drawLine(width/2+4*radius, 0, width/2+4*radius, lineLength, linePaint);

canvas.drawCircle(width/2+4*radius, lineLength, radius, ballPaint);

// 画左二的圆

ballX = (int) (width/2-4*radius - lineLength*Math.sin(pSswingAngle));

ballY = (int) (lineLength * Math.cos(pSswingAngle));

canvas.drawLine(width/2-4*radius, 0, ballX, ballY, linePaint);

canvas.drawCircle(ballX, ballY, radius, ballPaint);

delay = (long) Math.sqrt(((long)lineLength) / (2*g*(Math.cos(swingAngle - cosInitAngle))));

if (isLeftSwing) {

swingAngle ++;

if (swingAngle >= initAngle) {

swingAngle = initAngle;

isLeftSwing = false;

}

} else {

swingAngle --;

if (swingAngle <= 0) {

swingAngle = 0;

location = LOCATION_RIGHT;

}

}

break;

case LOCATION_MIDDLE:

// 画左二的圆

canvas.drawLine(width/2-4*radius, 0, width/2-4*radius, lineLength, linePaint);

canvas.drawCircle(width/2-4*radius, lineLength, radius, ballPaint);

// 画右二的圆

canvas.drawLine(width/2+4*radius, 0, width/2+4*radius, lineLength, linePaint);

canvas.drawCircle(width/2+4*radius, lineLength, radius, ballPaint);

if (isLeftSwing)

location = LOCATION_LEFT;

else

location = LOCATION_RIGHT;

delay = 200; // 中间停顿毫秒数

break;

case LOCATION_RIGHT:

// 画左二的圆

canvas.drawLine(width/2-4*radius, 0, width/2-4*radius, lineLength, linePaint);

canvas.drawCircle(width/2-4*radius, lineLength, radius, ballPaint);

// 画右二的圆

ballX = (int) (width/2+4*radius + lineLength*Math.sin(pSswingAngle));

ballY = (int) (lineLength * Math.cos(pSswingAngle));

canvas.drawLine(width/2+4*radius, 0, ballX, ballY, linePaint);

canvas.drawCircle(ballX, ballY, radius, ballPaint);

delay = (long) Math.sqrt(((long)lineLength) / (2*g*(Math.cos(swingAngle - cosInitAngle))));

if (isLeftSwing) {

swingAngle --;

if (swingAngle <= 0) {

swingAngle = 0;

location = LOCATION_MIDDLE;

}

} else {

swingAngle ++;

if (swingAngle >= initAngle) {

swingAngle = initAngle;

isLeftSwing = true;

}

}

break;

}

postInvalidateDelayed(delay);

}一个有5个球,中间3个球是不动的,所以先绘制中间3个静止的球。

在使用sin和cos计算时,使用的是弧度,并不是我们平时说的角度,所以先要把角度转换成弧度,使用Math.toRadians()方法。

摆球的运动可分为3类,一类是左边的一个球在动,一类是中间传递(都不动),还有一类是右边的摆球在动。

4.1 左边球运动时

此时右边的球是静止不动的,先绘制它。

由于我们每次都是偏移单位角度,所以其实我们是知道偏移角度的,根据偏移角度就能算出运动小球瞬间的中心坐标。

上面我们推算出角速度与偏移角度的公式,利用该公式就可以知道偏移单位角度需要的时间间隔,我们就在这个时间间隔后重新绘制。

小球运动时可能会有两个方向,向左或者向右运动。向左运动到初始角度时,就要换向了。当偏移角度为0时,就进入了中间传递状态。

4.2 中间传递状态

实际上,这个传递过程是需要时间的,这时5个球都短暂的静止,然后继续向左或者向右传递。

4.3 右边球运动时

与4.1左边球运动一致。

最后延迟绘制就行了。

github地址如下:

另外还学会了两个东西:

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值