autojs模仿某东分类导航栏(二)

314 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情 >>

牙叔教程 简单易懂

接着上一篇教程

autojs模仿某东分类导航栏

www.yuque.com/yashujs/bfu…

上一篇教程写了导航栏的基础部分, 这篇教程我们添加导航栏的动画

某东的导航栏效果

\

他这个动画是在文字下方画一段圆弧, 是有画这个过程的, 大概300ms;

怎么画圆弧

canvas画圆弧的方式是

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,

\

我们先单独测试画圆弧

ui.layout(
    <vertical>
        <canvas id="board" w="300dp" h="300dp"></canvas>
    </vertical>
);

真个界面就只有一个canvas

设置canvas的canvas事件

let startAngle = 135;
let sweepAngle = -100;
let useCenter = false;
ui.board.on("draw", function (canvas) {
    canvas.drawColor(colors.BLACK);
    canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint); //画弧,
});

画出来的圆弧是这样的

\

怎么让他300ms慢慢画完呢?

通过修改背景的方式让他慢慢画完

我们搞一个示例看看

就给只有一个View

ui.layout(
    <vertical>
        <View id="board" w="300dp" h="300dp"></View>
    </vertical>
);

给View设置背景

var drawable = new android.graphics.drawable.Drawable({
    draw: function (canvas) {
        canvas["drawColor(int)"](colors.BLACK);
        canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint); //画弧,
    },
});
ui.board.setBackgroundDrawable(drawable);

\

其中的sweepAngle, 我们用 ValueAnimator 来控制

animator.addUpdateListener(
    new ValueAnimator.AnimatorUpdateListener({
        onAnimationUpdate: function (animation) {
            let value = animation.getAnimatedValue();
            sweepAngle = -value;
            ...
        },
    })
);

慢慢画弧 演示效果

\

给导航栏添加背景动画

let itemLayout = (
    <frame w="wrap_content" margin="8" h="80dp" bg="#b1d5c8">
        <View id='argBg' w="match_parent" h="match_parent" bg="#ff4fc1" />
        <text id="name" w="wrap_content" textSize="24sp" margin="0" padding="0" textColor="#5e128a" gravity="center"></text>
    </frame>
);

然后在点击事件中, 添加动画, 这时又遇到一个问题,

什么时候开始执行动画?

这里我们设计为在滚动结束之后, 开始执行动画;

新的问题又来了, 我们怎么知道滚动结束了?

我们给rv添加一个滚动监听

rv滚动有三种状态

//停止滚动
public static final int SCROLL_STATE_IDLE = 0;
 
//正在被外部拖拽,一般为用户正在用手指滚动
public static final int SCROLL_STATE_DRAGGING = 1;
 
//自动滚动开始
public static final int SCROLL_STATE_SETTLING = 2;

我们只需要在监听 停止滚动 的时候, 开始画圆弧

画圆弧之前要知道的数据

  • 圆弧的起点
  • 圆弧的终点
  • 圆弧的半径

圆弧的起点是哪里?

再次查看某东的动画, 可知, 圆弧在文字中间正下方, 左右各一半圆弧;

那么

圆弧的起点横坐标 = 文字控件中心横坐标 - 一个字的宽度*0.75

圆弧的起点纵坐标 = 文字底部纵坐标

在按照这个公式计算的时候, 发现一个问题: 作为背景的view宽度是0!

作为动画的背景xml是这样的

<View id="arcBg" w="match_parent" h="match_parent" bg="#0000ff" />

整个itemXml是这样的

let itemLayout = (
    <frame w="wrap_content" margin="8" h="60dp" bg="#b1d5c8">
        <text id="name" layout_gravity="center" bg="#770fff" w="wrap_content" h="wrap_content" textSize="24sp" margin="0" padding="0" textColor="#5e128a" gravity="center"></text>
        <View id="arcBg" w="match_parent" h="match_parent" bg="#0000ff" />
    </frame>
);

\

动画背景宽度是0的话, 我们这个计算就没用了, 宽度为0的画板, 画啥也看不见;

因此, 我们得改改这个itemLayout, 修改之后的itemLayout, 动画背景的宽高以textView为标准

let itemLayout = (
    <frame w="wrap_content" margin="8" h="60dp" bg="#b1d5c8">
        <text id="name" layout_gravity="center" bg="#770fff" w="wrap_content" h="wrap_content" textSize="24sp" margin="0" padding="0" textColor="#5e128a" gravity="center"></text>
        <View id="arcBg" h="match_parent" bg="#0000ff" />
    </frame>
);

动画背景的宽度arcBg要在recyclerview的onBindViewHolder方法中动态设置

onBindViewHolder: function (holder, position) {
    ...
    let textViewWidth = getViewWidth(view.name);
    view.arcBg.attr("w", textViewWidth + "px");
}

动画背景的宽度解决以后, 我们就可以套用上面的公式了,

画圆弧的效果如下:

\

这个圆弧是瞬间绘制的, 不是慢慢画出来的, 我们要加一个 ValueAnimator 来控制这个圆弧 sweepAngle 的数值在一定时间内慢慢变大

我们来看看 这个监听器怎么写的

let updateListener = new ValueAnimator.AnimatorUpdateListener({
    onAnimationUpdate: function (animation) {
        let value = animation.getAnimatedValue();
        sweepAngle = -value;
        var drawable = new android.graphics.drawable.Drawable({
            draw: function (canvas) {
                canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint); //画弧,
            },
        });
        ui.board.setBackgroundDrawable(drawable);
    },
});
animator.addUpdateListener(updateListener);

监听器的方法都是类似了, 只有几个参数需要发生变化, 我们有两种写监听器的思路

  • 动画开始绘制的时候, 先移除旧的监听器, 再添加新的监听器, 这样要多次写监听器
  • 动画开始绘制的时候, 改变监听器里的, 必须修改的变量的值, 这样只写一次监听器即可

我选择第二种, 那么我们来看看监听器里面有哪些参数需要外部修改

  • ui.board 要把这个改成动画的view

其他参数都不需要外部修改, 因为某东导航栏的动画都是一模一样的, 圆弧的起点, 终点, 高度, 都没有任何变化

我们就定义一个变量, 来引用当前的动画view

let currentArcView=null;

2s慢动画效果

\

颜色应该加一个渐渐透明, 那么我们就再加一个 ValueAnimator, 用来管理透明度

let animator = new AnimatorSet();
let animatorSweepAngle = ValueAnimator.ofInt(0, 100);
let animatorAlpha = ValueAnimator.ofInt(0, 255);
animator.playTogether([animatorSweepAngle, animatorAlpha]);

\

但是问题来了, 慢慢画的效果出来了, 最后透明全都看不见了

因此, 我们这种修改背景, 模拟动画的方式需要改改

var drawable = new android.graphics.drawable.Drawable({
    draw: function (canvas) {
        paint.setColor(colors.argb(currentAlpha, red, green, blue));
        canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint); //画弧,
    },
});
currentArcView.setBackgroundDrawable(drawable);

怎么改呢?

我们给画笔设置shader, 我们做一个 LinearGradient, 搞成渐变色那种, 从左往右, 渐渐透明

public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) 
let linearGradient = new LinearGradient(0, 150, 300, 150, colorArr, positions, Shader.TileMode.CLAMP);
let paint = new Paint();
paint.setShader(linearGradient);
ui.board.on("draw", function (canvas) {
    canvas.drawColor(colors.BLACK);
    canvas.drawRect(0, 0, 300, 300, paint);
});

渐渐透明的紫色

\

接下来, 我们就用shader的方式来解决动画问题, 透明度的修改就没必要了, 因为透明度已经被包含在shader里面了

// animator.playTogether([animatorSweepAngle, animatorAlpha]);
animator.playTogether([animatorSweepAngle]);

渐变的动画效果


新的问题又出现了, 我点击的是 酒水饮料, 可他还是在 个护清洁 上面做动画

这个明显是因为没有及时更新点击的控件, 我们更新即可


新的问题又来了, 点击那个控件, 那个控件就做动画, 上一个控件没有清理动画

那么, 我们需要给数据添加一个状态属性, 勾选或者没有勾选

上面是在模拟器测试的, 我们去手机上看看效果

\

手机上字都显示不全

除了这个问题, 还有一些别的问题, 下次再解决

环境

手机:小米11pro
MIUI: 13.0.12
Android版本: 12
Autojs版本: 9.2.6

名人名言

思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程\

声明

部分内容来自网络 本教程仅用于学习, 禁止用于其他用途\

微信公众号 牙叔教程