android 天气动画,Android 自定义 View - 小米 MIUI8 天气动画(晴天)

介绍

升级MIUI8之后,小米自带的天气应用晴天的时候动画效果很赞。然后就捣鼓了几下写了个简易的demo。

我们先来看看实现后的效果:

正常模式:273f03abca65398af3c9d1846d7879d5.gifimg1

View跟随手机摇晃后移动:

05e646cb8a6c61f32a5d901f4544245a.gifimg2

分析

动画分解

飞翔的鸟群

树的枝干会倾斜15~30度,并在达到倾斜的最大角度后抛出树叶,等树叶消失后,回到最初位置。

树叶每次抛出角度不固定,并且在下落一定距离后渐渐消失。

左右摇晃手机,背景的山会随着手机移动。

实现

(1) 下载app,然后将其后缀名改为zip解压获取我们需要的资源图片:

a9718072c87cc4e76692ac242a3292f9.pngimg3

(2) 实现鸟群动画

思路:通过解压后得到的素材可以发现鸟扇动翅膀的实现类似于帧动画,鸟群的运动轨迹是曲线,我们可以使用贝塞尔曲线,为了能够实时移动鸟群,我们需要获取贝塞尔曲线上面的点坐标,这里我是使用函数来计算出点坐标,也可以用PathMeasure来实现。综上,用ValueAnimator实现实时计算鸟群移动点坐标,设定一个计数器,用来更新鸟的Bitmap实现帧动画效果。

核心代码

isDrawBirds = true;

BezierEvaluator bezierEvaluator = new BezierEvaluator(newPointF(birdControlPointX, birdControlPointY));

final ValueAnimator anim = ValueAnimator.ofObject(bezierEvaluator,

new PointF(birdStartPointX, birdStartPointY),

new PointF(birdEndPointX, birdEndPointY));

anim.setDuration(8000);

anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator valueAnimator) {

PointF point = (PointF) valueAnimator.getAnimatedValue();

birdMovePointX = (int) point.x;

birdMovePointY = (int) point.y;

++birdTimes;

if (birdTimes % 8 == 0){

updatebirds(birdTimes);

}

invalidate();

}

});

anim.setInterpolator(new LinearInterpolator());

anim.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationRepeat(Animator animation) {

super.onAnimationRepeat(animation);

birdTimes = 0 ;

}

});

anim.setRepeatMode(ValueAnimator.RESTART);

anim.setRepeatCount(ValueAnimator.INFINITE);

anim.start();

private void updatebirds(int birdTimes){

bird = birdsList.get(birdTimes % 16);

}复制代码

(2) 树动画

255b15c6060acda0c1c3baaa5ba205a4.png

思路:看到这素材就知道树是拼凑出来的。树动画的实现,通过观察可以发现是treeBallLeft,treeBallMiddle,treeBallRight,通过绕treeBranch的中心点实现。

核心代码

valueAnimatorTree = new ValueAnimator().ofFloat(0, 15);

valueAnimatorTree.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

rotateValueTree = 360 - (float) animation.getAnimatedValue();

invalidate();

}

});

valueAnimatorTree.addPauseListener(new Animator.AnimatorPauseListener() {

@Override

public void onAnimationPause(Animator animation) {

rotateValueTree = 0;

isDrawTree = true;

startAnimLeaf();

}

@Override

public void onAnimationResume(Animator animation) {

}

});

valueAnimatorTree.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationRepeat(Animator animation) {

++timesTree;

if (timesTree % 2 == 1)

valueAnimatorTree.pause();

}

});

valueAnimatorTree.setDuration(2000);

valueAnimatorTree.setRepeatMode(ValueAnimator.REVERSE);

valueAnimatorTree.setRepeatCount(ValueAnimator.INFINITE);

valueAnimatorTree.setInterpolator(new LinearInterpolator());

valueAnimatorTree.start();复制代码

(3) 树叶动画

思路:树叶的初始位置,将树叶藏在treeBallMiddle里面。然后执行路径动画,执行路径动画的同时,根据getAnimatedFraction() 获取动画进行的百分比计算出树叶的旋转角度,动画到运行75%的时候执行透明动画就行了

核心代码

public void startAnimLeaf() {

randomRotate = Math.random() * 90;

BezierEvaluator bezierEvaluator = new BezierEvaluator(new PointF(treeControlPointX, treeControlPointY));

final ValueAnimator anim = ValueAnimator.ofObject(bezierEvaluator,

new PointF(treeStartPointX, treeStartPointY),

new PointF(treeEndPointX, treeEndPointY));

anim.setDuration(4000);

anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator valueAnimator) {

PointF point = (PointF) valueAnimator.getAnimatedValue();

moveLeafX = point.x;

moveLeafY = point.y;

float fraction =valueAnimator.getAnimatedFraction();

rotateValueLeaf = 90 * fraction;

if (fraction>=0.75 && valueAnimatorAlpha.isRunning()== false){

startAlpha();

}

invalidate();

}

});

anim.setInterpolator(new LinearInterpolator());

anim.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

isDrawTree = false;

valueAnimatorTree.resume();

invalidate();

}

});

anim.start();

}

private void startAlpha(){

valueAnimatorAlpha = ValueAnimator.ofFloat(0,1);

valueAnimatorAlpha.setDuration(1000);

valueAnimatorAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator valueAnimator) {

float fraction = (float) valueAnimator.getAnimatedValue();

treeLeafPaint.setAlpha((int) (255 - 255 * fraction));

}

});

valueAnimatorAlpha.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

treeLeafPaint = new Paint();

}

});

valueAnimatorAlpha.setInterpolator(new LinearInterpolator());

valueAnimatorAlpha.start();

}复制代码

(4) 背景

思路:通过BitmapFactory.Option来压缩背景图片的大小,保证View的流畅性。

摇晃手机的同时,背景跟着动的实现:设置背景图片的时候,宽是屏幕宽度+maxoffsetX*2。通过implements SensorEventListener,获取摇摆手机时候晃动的百分比,然后在onDraw中调用

currentOffsetX = maxOffsetX * mProgress;

canvas.translate(currentOffsetX, 0);复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值