牙叔教程 简单易懂
正常动画速度500ms
慢速动画3秒
思路
- 绘制背景, 白色的圆角矩形
- 绘制爱心, 红色的心形
- 添加点击动画
绘制白色的矩形
drawBg(that.roundRectPaint, that.fraction, canvas, that.width, that.height);
是已经封装好的代码, 就是画个圆角矩形
function drawBg(paint, fraction, canvas, width, height) { let left = 0; let top = 0; let right = width; let bottom = height; // scale let scaleX = leap(1, 0.9, 1, fraction); let scaleY = scaleX; let pivotX = width / 2; let pivotY = height / 2; canvas.scale(scaleX, scaleY, pivotX, pivotY); let radius = 100; canvas.drawRoundRect(left, top, right, bottom, radius, radius, paint); }
其中有5个参数
function drawBg(paint, fraction, canvas, width, height)
paint是画笔
fraction是控制Canvas缩放的
canvas是画板
width, height是矩形的宽高
fraction是这里面最关键的, 因为我们这个教程主要就是写动画;
动画的主要形式就是控制canvas的缩放
fraction主要用在leap函数里面, 我们看看leap是什么?
leap
function leap(a, b, c, fraction) { if (fraction <= 0.5) { return MathUtils.lerp(a, b, fraction * 2); } else { let tempFraction = fraction - 0.5; return MathUtils.lerp(b, c, tempFraction * 2); } }
leap返回一个数字, 用这个数字来控制动画;
数字是用lerp计算出来的
lerp
com.google.android.material.math.MathUtils.lerp
我们去安卓官网看看这个方法
public static float lerp (float start, float stop, float amount)
Returns the linear interpolation of amount between start and stop.
返回两个数字时间的线性插值
Canvas
canvas从哪里来的?
canvas是自定义控件的参数, 是onDraw的参数
CustomView.prototype.render = function () { let that = this; return JavaAdapter( android.view.View, { onDraw: function (canvas) {...}
我们绘制矩形和心形都是在 onDraw 方法中绘制;
绘制矩形的时候, 要知道矩形的宽高;
宽高要测量, 测量方法是 onMeasure
onMeasure: function (widthMeasureSpec, heightMeasureSpec) { this.super$onMeasure(heightMeasureSpec, widthMeasureSpec); this.setMeasuredDimension(this.getMeasuredHeight(), this.getMeasuredWidth()); that.width = this.getMeasuredWidth(); that.height = this.getMeasuredHeight(); },onMeasure: function (widthMeasureSpec, heightMeasureSpec) {
心形
绘制完矩形, 我们要绘制心形;
心形使用的是内置icon,
let drawableId = "ic_favorite_black_48dp"; let imgId = resources.getIdentifier(drawableId, "drawable", context.getPackageName()); let drawable = resources.getDrawable(imgId); let bitmap = drawable.getBitmap();
这时候的bitmap不能直接使用, 我们要复制一下bitmap
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
放大心形图片
内置的icon一般都很小, 我们要把心形放大到矩形大小
var img = com.stardust.autojs.core.image.ImageWrapper.ofBitmap(bitmap); let width = that.width; let height = that.height; let fx = 1; if (width > height) { fx = height / img.width; } else { fx = width / img.width; } let fy = fx; let img2 = images.scale(img, fx, fy);
要及时回收图片
events.on("exit", function () { img2.recycle(); }); setTimeout(() => { img.recycle(); }, 666);
图片搞好以后, 我们还要给图片设置颜色
图片着色
用放大后的心形图片, 创建一个canvas实例,
let srcInMode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN); // let drawableCanvas = Canvas(iconBitmap); let drawableCanvas = null; function tintBitmap(paint, iconBitmap) { // change to src in paint.xfermode = srcInMode; drawableCanvas.drawRect(0, 0, iconBitmap.width, iconBitmap.height, paint); paint.xfermode = null; }
这里用的模式是 SRC_IN , 只显示两者相交的部分, 且显示前景色, 也就是我们设置的颜色
动画控制
使用的是 ValueAnimator
// 创建动画 function createAnimator(that) { //动画 let animator = ValueAnimator.ofFloat(0, 1); animator.setDuration(config.duration); animator.setInterpolator(new BounceInterpolator()); animator.addListener( new AnimatorListenerAdapter({ onAnimationStart: function (animation) { that.uiState = UIState.Animating; }, onAnimationEnd: function (animation) { that.uiState = that.uiStateStart == UIState.Like ? UIState.UnLike : UIState.Like; }, onAnimationCancel: function (animation) {}, }) ); animator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener({ onAnimationUpdate: function (valueAnimator) { let value = valueAnimator.getAnimatedValue(); that.fraction = value; that.view.invalidate(); }, }) ); return animator; }
ValueAnimator始终是从0变化到1, 然后赋值给 fraction ,
fraction 变化, 又会引起 scaleX 的变化, scaleX又是 canvas.scale 的参数
canvas.scale(scaleX, scaleY, pivotX, pivotY);
每次绘制矩形和心形之前, 都会先缩放canvas
并且修改了默认的差值器, 使用的是 BounceInterpolator , 弹跳插值器, 模拟自由落体后的回弹动画
animator.setInterpolator(new BounceInterpolator());
自定义控件结构
(function () { util.extend(CustomView, ui.Widget); function CustomView() { ui.Widget.call(this); } CustomView.prototype.render = function () { return JavaAdapter( android.view.View, { onDraw: function (canvas) { this.super$onDraw(canvas); ... }, onMeasure: function (widthMeasureSpec, heightMeasureSpec) { this.super$onMeasure(heightMeasureSpec, widthMeasureSpec); this.setMeasuredDimension(this.getMeasuredHeight(), this.getMeasuredWidth()); }, onSizeChanged: function (w, h, oldW, oldH) { this.super$onSizeChanged(w, h, oldW, oldH); }, }, activity ); }; CustomView.prototype.onViewCreated = function (view) {}; CustomView.prototype.onFinishInflation = function (view) {}; ui.registerWidget("like-button", CustomView); return CustomView; })();
以上, 就是一个点赞动画的关键知识
测试环境
手机: Mi 11 Pro
Android版本: 12
Autojs版本: 9.1.17
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
声明
部分内容来自网络 本教程仅用于学习, 禁止用于其他用途