autojs导航栏添加背景动画

牙叔教程 简单易懂

动画效果预览


导航栏UI

一个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

  • left
  • right


首先封装一个获取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
  • 一个更新right


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);


圆角矩形变细的效果


差值器

圆角矩形变细了, 面积是一定的, 那么圆角矩形必须变长;

变长有两种办法:

  • 修改left和right的距离
  • 使用差值器


这里我们使用差值器,

那么用哪个差值器可以让圆角矩形变长呢?

如果right的数值比left的数值增长快, 那么就会变长;

我们找那种一开始速度就很快的差值器, 这样的差值器有以下几种

  • DecelerateInterpolator在开始的地方快然后慢
  • OvershootInterpolator向前甩一定值后再回到原来位置


既然我们是在学习差值器, 那么就用个有特点的差值器, 我选择有回弹效果的 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 条评论) “”
   
验证码:

相关文章

推荐文章