牙叔教程 简单易懂
一个horizontal
四个子控件
每个子控件有img和text
宽高比例使用 layout_weight 控制
"ui";
ui.layout(
菜单1
...
);
导航栏渲染完成以后, 我们再给文字添加背景
这个文字位于顶层, 底层我们增加一个画板, 用来显示动画;
布局就用帧布局 frame
这里面最重要的是计算这个圆角矩形的rect, 计算方法是先获取textView的rect数据, 以及他的父控件, 还有爷爷控件的rect数据, 最后加减乘除获取最终的圆角矩形的rect, 高度上已经包含了文字, 且文字视觉上有padding, 因此只需要在左右两边, 把圆角矩形拉宽一点, 圆角矩形就不会和文字挨得太近
文字颜色的背景我们选择浅灰色的邻近色
当我们点击导航栏的菜单2时, 我们把这个菜单1的圆角矩形, 慢慢移动到我们点击的菜单2;
这个动画中的圆角矩形的背景rect, 一共四个属性,
left, top, right, bottom
圆角矩形是文字的背景, 文字的高度一般是不变的, top和bottom不变, 我们需要考虑的是left和right,
菜单文字数量可能是两个字, 也可能是三个字, 就是说文字的宽度可能会变,
因此圆角矩形的宽度也可能是变化的,
这个距离的变化我们使用 ValueAnimator
一共有两个ValueNnimator
首先封装一个获取textView的rect的方法
function getTextViewRect(textView) {
let left = textView.getX();
let top = textView.getY();
let right = left + textView.getWidth();
let bottom = top + textView.getHeight();
let textViewParent = textView.parent;
let textViewParentLeft = textViewParent.getX();
let textViewParentTop = textViewParent.getY();
left = textViewParentLeft + left;
top = textViewParentTop + top;
right = textViewParentLeft + right;
bottom = textViewParentTop + bottom;
// textView的爷爷
let textViewGrandParent = textViewParent.parent;
let textViewGrandParentLeft = textViewGrandParent.getX();
let textViewGrandParentTop = textViewGrandParent.getY();
let offset = 32;
left = textViewGrandParentLeft + left - offset;
top = textViewGrandParentTop + top;
right = textViewGrandParentLeft + right + offset;
bottom = textViewGrandParentTop + bottom;
return new android.graphics.RectF(left, top, right, bottom);
}
然后 创建ValueAnimator
let menu1Rect = getTextViewRect(menu1);
let menu2Rect = getTextViewRect(menu2);
let startLeft = menu1Rect.left;
let startRight = menu1Rect.right;
let stopLeft = menu2Rect.left;
let stopRight = menu2Rect.right;
let leftValueAnimator = ValueAnimator.ofFloat(startLeft, stopLeft);
let rightValueAnimator = ValueAnimator.ofFloat(startRight, stopRight);
添加 ValueAnimator 的监听
leftValueAnimator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener({
onAnimationUpdate: function (animation) {
currentLeft = animation.getAnimatedValue();
},
})
);
两个 ValueAnimator , 一共有两个监听
left的监听负责更新left,
right的监听负责更新right, 同时, 负责刷新圆角矩形背景
rightValueAnimator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener({
onAnimationUpdate: function (animation) {
let currentRight = animation.getAnimatedValue();
rect.left = currentLeft;
rect.right = currentRight;
var drawable = new android.graphics.drawable.Drawable({
draw: function (canvas) {
canvas["drawColor(int)"](bgColor);
canvas.drawRoundRect(rect, 20, 20, paint);
},
});
animationAreaView.setBackgroundDrawable(drawable);
animationAreaView.invalidate();
},
})
);
看看此时的效果
变宽, 必然是圆角矩形的left和right的距离大了, 可是我怎么觉得不应该那么大呢?
难道是速度太快, left和right数值更新不及时?
要排除原因, 我们可以把动画的时间延长到10秒, 看看就知道是不是这个原因了.
经过测试, 即使10秒钟, 矩形在动画中间, 依然是很宽的;
那到底是很么原因呢?
经过三天两夜的调试, 终于发现了问题, 差值器,
原来只给left设置了差值器
leftValueAnimator.setInterpolator(new AccelerateInterpolator());
现在给right也设置差值器
rightValueAnimator.setInterpolator(new AccelerateInterpolator());
再看看动画效果
现在就符合预期了
上面的动画只是把圆角矩形从一个地方, 挪动到另一个地方, 圆角矩形没有什么变化, 动画显得单调;
我们调整一下动画, 在圆角矩形, 向右运动的时候, 把圆角矩形变细变长, 到了目的地, 圆角矩形再恢复原来的宽高
想法是美好的, 具体代码怎么写呢?
首先我们处理圆角矩形变细的问题, 这个要控制圆角矩形的top和left, 他的过程是 宽-> 窄-> 宽,
同样我们要增加一个ValueAnimator,
let heightValueAnimator = ValueAnimator.ofFloat(0, heightOffset, 0);
圆角矩形变细的效果
圆角矩形变细了, 面积是一定的, 那么圆角矩形必须变长;
变长有两种办法:
这里我们使用差值器,
那么用哪个差值器可以让圆角矩形变长呢?
如果right的数值比left的数值增长快, 那么就会变长;
我们找那种一开始速度就很快的差值器, 这样的差值器有以下几种
既然我们是在学习差值器, 那么就用个有特点的差值器, 我选择有回弹效果的 OvershootInterpolator
leftValueAnimator.setInterpolator(new OvershootInterpolator(1));
rightValueAnimator.setInterpolator(new OvershootInterpolator(2));
heightValueAnimator.setInterpolator(new OvershootInterpolator(2));
这个是2秒的动画
这个是300毫秒的动画
差值器的效果还是得自己试试, 喜欢那个用那个
最终我选择的差值器是这样的
leftValueAnimator.setInterpolator(new AccelerateInterpolator());
rightValueAnimator.setInterpolator(new DecelerateInterpolator(1.2));
heightValueAnimator.setInterpolator(new DecelerateInterpolator(1));
依据是卡通动画片里的人物, 尤其是米姥鼠唐姥鸭之类的, 他们在动画片里移动就像弹弓一样;
先把身体拉的很细, 然后加速, 唰! 就移动到了另外一个地方
菜单1 跳转 菜单2, 动画有了, 菜单1 跳转 菜单3呢?
菜单4 跳转 菜单2呢?
我们先考虑, 从左往右的动画
如果要封装成一个方法的话, 参数是什么?
我觉得应该是from和to, 当前有圆角矩形背景的控件序号算from, 点击的那个算to;
要获取序号的话, 我们可以给每个菜单设置一个tag, tag里面存储序号
当然了, 还有别的办法确定序号, 不过我觉得设置tag最简单了
什么时候触发动画呢?
就是手指点击控件的时候
let childCount = ui.root.getChildCount();
for (var i = 0; i < childCount; i++) {
let child = ui.root.getChildAt(i);
child.on("click", function (view) {
toastLog("触发动画, view序号: " + view.tag);
});
}
我们有一个全局变量记录当前有背景的控件的序号
let currentNum = 0;
动画完成后, 我们就修改这个变量, 为当前点击的控件序号,
比如, 我们点击的菜单4, 我们就把这个变量的值修改为3 (序号从0开始)
我们将以上代码整理封装一个方法: 从左向右的动画
showAnimationLeftToRight(startView, stopView);
这个方法有两个参数: 一个是动画的起点view, 一个是动画的终点view
每次动画都会设置新的监听, 因此要及时移除旧的监听器
leftValueAnimator && leftValueAnimator.removeAllUpdateListeners();
rightValueAnimator && rightValueAnimator.removeAllUpdateListeners();
heightValueAnimator && heightValueAnimator.removeAllUpdateListeners();
同样还有一个从右向左的动画
showAnimationRightToLeft(startView, stopView)
从左向右和从右向左这两个方法的唯一区别就是差值器不一样,
把left和right的差值器点到一下即可
设备: 小米11pro
Android版本: 12
Autojs版本: 9.2.12
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
部分内容来自网络 本教程仅用于学习, 禁止用于其他用途
留言与评论(共有 0 条评论) “” |