android view动画保持end位置,Android自定义View之App更新动画

本文详细介绍了如何在Android应用中设计App更新过程中的动画效果,包括初始化阶段的按钮消失、准备阶段的进度条扩散、更新阶段的动态提示、成功阶段的缩放消失和安装阶段的图片显示。通过Path动画、Matrix变换和ValueAnimator实现各种视觉反馈。
摘要由CSDN通过智能技术生成

原标题:Android自定义View之App更新动画

为了做一个有温度的IT男,我决定在以后的文章中给大家分享一些看到的,听到的一些东西,如果你不喜欢请留言让我知道,如果你喜欢请点个赞。你也可留言写下自己想分享的东西,温暖你我他。这次分享的是一首歌,毛不易的《消愁》,分享这首歌主要是这首歌的歌词,借用薛之谦的评价:“我是研究歌词的人,我研究了十几年,但是你写到我想给你跪!”,下面贴部分歌词供大家欣赏

一杯敬朝阳,一杯敬月光

唤醒我的向往,温柔了寒窗

于是可以不回头的逆风飞翔

不怕心头有雨,眼底有霜

一杯敬故乡,一杯敬远方

守着我的善良,催着我成长

所以南北的路从此不再漫长

灵魂不再无处安放

好了,言归正传,本篇文章是实现项目中的更新功能,效果如下

58fb658e5047a8769f0bdfd553f15a9e.gif

app更新动画

观察动画,可以分为几个阶段:

初始化阶段 显示立即升级按钮,在点击立即升级按钮后,执行放大再缩小至消失动画

准备阶段 进度条背景从中间向两端扩散,然后进度提示图片显示,进度提示文字显示0%

更新阶段 进度更新时,进度提示图片和文字旋转向前移动,如果一定时间内进度没更新的话,进度提示图片和文字要置回水平状态

成功阶段,进度提示图片缩放消失,进度条背景从两端向中间缩小至消失

安装阶段 马上安装图片放大显示

1.首选看初始化阶段,我们要判断用户是否点击了立即升级按钮,我们通过监听onTouchEvent事件判断手指是否落在可点击区域

//如果点击生效,执行动画

if (rectClickRange.contains(event.getX(), event.getY()))

startBtnDisappear();//立即更新按钮消失动画

其中rectClickRange是我们定义的可点击区域,也就是立即更新图片的显示区域

rectClickRange = new RectF(getWidth() / 2 - startDrawable.getWidth() / 2, getHeight() / 2 - startDrawable.getHeight() / 2, getWidth() / 2 + startDrawable.getWidth() / 2, getHeight() / 2 + startDrawable.getHeight() / 2);//startDrawable是立即更新图片

点击生效后我们执行立即更新按钮消失动画,代码如下

/**

* 点击立即升级的时候,立即升级按钮执行消失动画

* 动画效果是按钮放大一点之后缩小至消失

* 根据效果选择插值器AnticipateInterpolator(开始的时候向后然后向前甩)

* 将bitmapscale设置到立即升级图片上

* 动画结束后状态更新为准备状态

*/

privatevoidstartBtnDisappear(){

initStateData();

ValueAnimator va = ValueAnimator.ofInt( 0, 1);

va.setInterpolator( newAnticipateInterpolator());

va.setDuration( 800);

va.addUpdateListener( newValueAnimator.AnimatorUpdateListener() {

@ Override

publicvoidonAnimationUpdate(ValueAnimator animation) {

bitmapScale = 1- animation.getAnimatedFraction();

invalidate();

}

});

va.addListener( newMyAnimationListener() {

@ Override

publicvoidonAnimationEnd(Animator animation) {

cancleValueAnimator(va_List);

state = PREPARE;

toPrepare();

}

});

va.start();

va_List. add(va);

}

然后在onDraw里面绘制立即升级按钮动画,代码如下:

matrix.reset();

matrix.setScale(bitmapScale, bitmapScale); //缩放图片

matrix.preTranslate( 0, 0);

matrix.postTranslate(width / 2 - startDrawable.getWidth() /2* bitmapScale, height / 2 - startDrawable.getHeight() /2* bitmapScale); //不断的改变缩放的中心点

canvas.drawBitmap(startDrawable, matrix, bitmapPaint);

2.接着我们看一下准备阶段,我们通过画path,并不断的改变path的起点和终点达到所需要的动画效果,代码如下:

/**

* PREPARE状态

* 进度条从中间向两端扩散

* 具体做法是不断改变path的起点和终点坐标

* 动画结束的时候开始下载更新

*/

privatevoidtoPrepare(){

final ValueAnimator va = ValueAnimator.ofFloat( 0, width / 2- pbPaint.getStrokeWidth() * 3- pbProgerssDrawable.getWidth());

va.setInterpolator( newLinearInterpolator());

va.setDuration( 200);

va.addUpdateListener( newValueAnimator.AnimatorUpdateListener() {

@ Override

publicvoidonAnimationUpdate(ValueAnimator animation) {

floatvalue= (Float) animation.getAnimatedValue();

startX = ( int) (width / 2- value);

endX = ( int) (width / 2+ value);

if(animation.getAnimatedFraction() == 1) prepareDone = true;

invalidate();

}

});

va.addListener( newMyAnimationListener() {

@ Override

publicvoidonAnimationEnd(Animator animation) {

if(startDownLoadListener != null&& !isSetListener) {

isSetListener = true;

postDelayed( newRunnable() {

@ Override

publicvoidrun() {

state = UPDATEING;

startDownLoadListener.downLoad(); //动画结束,通知界面开始下载apk

text = progress * 100/ max + "%";

}

}, 200);

}

}

});

va.start();

va_List. add(va);

}

具体的绘制代码如下

pbPath.reset();

pbPath.moveTo(startX, height / 2);

pbPath.lineTo(endX, height / 2);

canvas.drawPath(pbPath, pbPaint); //绘制path

//进度条完全显示后,画进度提示图片和文字

if(prepareDone) {

canvas.drawBitmap(pbProgerssDrawable, startX - pbProgerssDrawable.getWidth() / 2, height / 2- pbProgerssDrawable.getHeight() - pbPaint.getStrokeWidth(), bitmapPaint);

Stringtext = "0%";

textPaint.getTextBounds(text.toCharArray(), 0, text.toCharArray().length, textRect);

canvas.drawText(text, startX - textRect.right / 2, height / 2- pbProgerssDrawable.getHeight() / 2- pbPaint.getStrokeWidth() + textRect.bottom, textPaint);

}

3.这个时候界面就开始下载apk(代码不贴了),然后通知view来更新进度,更新的动画是图片和文字旋转向前移动(我们的做法是将画布旋转),如果一定时间进度没有变化,更新的图片和文字置回正常状态(我们通过启动线程不断的将画布旋转回来并更新view,如果这个阶段进度有更新的话,我们把线程remove掉),绘制代码如下

pbPath.reset();

pbPath.moveTo(startX, height / 2);

pbPath.lineTo(endX, height / 2);

pm.setPath(pbPath, false);

//不断截取进度到pbPathSec并绘制

if(progressOffsetX >= pm.getLength()) {

pm.getSegment( 0, pm.getLength(), pbPathSec, true);

pm.getPosTan(pm.getLength(), POS, null);

} else{

pm.getSegment( 0, progressOffsetX, pbPathSec, true);

pm.getPosTan(progressOffsetX, POS, null);

}

matrix.reset();

matrix.setTranslate(POS[ 0] - pbProgerssDrawable.getWidth() / 2, POS[ 1] - pbProgerssDrawable.getHeight() - pbPaint.getStrokeWidth());

canvas.drawPath(pbPath, pbPaint);

canvas.drawPath(pbPathSec, pbUpdatePaint);

canvas.save();

//如果进度没有到达100%,并且进度在更新的时候,画布旋转,然后画进度提示图片和文字

if(progressOffsetX < pm.getLength() && !isRotate) {

canvas.rotate( -15, POS[ 0], POS[ 1] - pbPaint.getStrokeWidth() / 2);

}

canvas.drawBitmap(pbProgerssDrawable, matrix, bitmapPaint);

if(progressOffsetX >= pm.getLength())

progressOffsetX = pm.getLength();

text = ( int) (progressOffsetX * 100/ pm.getLength()) + "%";

textPaint.getTextBounds(text.toCharArray(), 0, text.toCharArray().length, textRect);

canvas.drawText(text, progressOffsetX + startX - textRect.right / 2, height / 2- pbProgerssDrawable.getHeight() / 2- pbPaint.getStrokeWidth() + textRect.bottom, textPaint);

//我们启动一个线程,如果300毫秒进度没有更新,将画布旋转回来画进度提示图片和文字

if(progressOffsetX < pm.getLength()) postDelayed(rotateRunnable, 300);

elsetoSucc();

canvas.restore();

其中rotateRunnable的代码如下

//每隔一段时间刷新界面,如果进度没有更新,将画布旋转回来

privateRunnable rotateRunnable = newRunnable() {

@Override

publicvoidrun(){

isRotate = true;

invalidate();

}

};

4.当进度达到100%的时候,我们将进度提示图片缩放至消失,并且进度背景执行两端向中间缩小动画,也是改变path的起始点,代码如下

//下载进度达到100时,进度提示图片进行缩放

privatevoidtoSuccBitmapScale(){

cancleValueAnimator(va_List);

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

va.setInterpolator( newAccelerateInterpolator());

va.setDuration( 100);

va.addUpdateListener( newValueAnimator.AnimatorUpdateListener() {

@Override

publicvoidonAnimationUpdate(ValueAnimator animation){

bitmapScale = (Float) animation.getAnimatedValue();

state = SUCCESS;

invalidate();

}

});

va.start();

va.addListener( newMyAnimationListener() {

@Override

publicvoidonAnimationEnd(Animator animation){

toSuccPathAnim();

}

});

va_List.add(va);

}

//成功后进度条缩放动画

privatevoidtoSuccPathAnim(){

cancleValueAnimator(va_List);

ValueAnimator va = ValueAnimator.ofInt( 0, (endX - startX) / 2);

va.setInterpolator( newAccelerateInterpolator());

va.setDuration( 300);

va.addUpdateListener( newValueAnimator.AnimatorUpdateListener() {

@Override

publicvoidonAnimationUpdate(ValueAnimator animation){

transx = ( int) animation.getAnimatedValue();

state = SUCCESS;

invalidate();

}

});

va.start();

va.addListener( newMyAnimationListener() {

@Override

publicvoidonAnimationEnd(Animator animation){

toInstall();

}

});

va_List.add(va);

}

绘制代码如下

pbPath.reset();

pbPath.moveTo(startX + transx, height / 2); //不断的改变起点

pbPath.lineTo(endX - transx, height / 2); //改变终点

pm.setPath(pbPath, false);

pm.getSegment( 0, (endX - startX), pbPathSec, true);

pm.getPosTan(endX - startX, POS, null);

matrix.reset();

matrix.preTranslate(POS[ 0] - pbProgerssDrawable.getWidth() / 2, POS[ 1] - pbProgerssDrawable.getHeight() - pbPaint.getStrokeWidth());

matrix.postScale(bitmapScale, bitmapScale, POS[ 0], POS[ 1] - pbPaint.getStrokeWidth());

canvas.drawPath(pbPath, pbUpdatePaint); //path缩放动画

canvas.drawBitmap(pbProgerssDrawable, matrix, bitmapPaint); //bitmap缩放动画

5.最后就是显示马上安装图片动画了,一个简单的缩放

//显示马上安装图片动画

privatevoidtoInstall(){

cancleValueAnimator(va_List);

ValueAnimator va = ValueAnimator.ofInt( 0, 1);

va.setInterpolator( newLinearInterpolator());

va.setDuration( 400);

va.addUpdateListener( newValueAnimator.AnimatorUpdateListener() {

@ Override

publicvoidonAnimationUpdate(ValueAnimator animation) {

bitmapScale = animation.getAnimatedFraction();

state = INSTALL;

invalidate();

}

});

va.start();

va_List. add(va);

}

//绘制代码如下

matrix.reset();

matrix.setScale(bitmapScale, bitmapScale);

matrix.preTranslate( 0, 0);

matrix.postTranslate(width / 2- succDrawable.getWidth() / 2* bitmapScale, height / 2- succDrawable.getHeight() / 2* bitmapScale);

canvas.drawBitmap(succDrawable, matrix, bitmapPaint);

回过头来看看,其实当我们把动画不断的分解之后,发现其实每个动画并没有那么难,我们这里用到的有path绘制及截取,getPosTan(获取路径上某点的坐标及其切线的坐标),利用Matrix做动画,使用属性动画ValueAnimator。本篇还有好多功能没有实现,比如下载失败动画,失败后恢复至初始化动画,不过任何轮子都不一定能完全适合你,学习到知识之后自己造一个适合自己的才是最重要。

好了,本篇代码已经上传github,欢迎star

github地址:https://github.com/MrAllRight/BezierView

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值