自定义TextView实现首尾相接跑马灯效果

需求:TextView内容不满一行的时候不滚动,超过一行的时候缓慢滚动到最后,然后添加空白间隔继续滚动

(我们项目用的是三个View一块滚动,停止应该是不一块停止的,毕竟三条内容应该不一样长,所以就需要个监听是否都停止了,如果都停止了,就一块开启继续滚动)

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;

import androidx.appcompat.widget.AppCompatTextView;

import com.blankj.utilcode.util.ScreenUtils;
import com.umeng.commonsdk.debug.E;

public class AutoScrollTextViewNew extends AppCompatTextView {
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private boolean isScrolling = false;
    /**
     * 最大可滚动距离
     */
    private int maxScrollX;
    private String showMessage = "";
    private ValueAnimator animator;
    private ScrollStatus scrollStatus;
    /**
     * 空格
     */
    private String Space = "         ";
    /**
     * scrollX每次移动的坐标
     * lastScrollX最后移动的坐标
     */
    private int scrollX = 0,lastScrollX = 0;
    public AutoScrollTextViewNew(Context context) {
        super(context);
        init();
    }

    public AutoScrollTextViewNew(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public AutoScrollTextViewNew(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        paint.setColor(Color.parseColor("#535353"));
        paint.setTextSize(17);
        paint.setTextAlign(Paint.Align.LEFT);
        setSingleLine(true); // 设置单行模式
        // 监听布局变化,以便在布局完成后计算最大滚动距离
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (getLayout() != null) {
                    // 计算最大滚动距离
                    int textWidth = (int) getPaint().measureText(showMessage);
                    int viewWidth = getWidth() - getPaddingLeft() - getPaddingRight();
                    maxScrollX = Math.max(0, textWidth - viewWidth);

                    // 如果内容超出视图宽度,则开始滚动
                    if (maxScrollX > 0 && !isScrolling) {
                        startScrolling(maxScrollX);
                    }else {
                        scrollStatus.putScrollStatus(showMessage);
                    }
                    // 移除监听器,避免重复调用
                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            }
        });
    }

    private void startScrolling(int scrollDistance) {
        /**
         * 这里必须减掉本身的宽度,如果不减,lastScrollX会越来越大,速度就会越来越慢
         */
        if(lastScrollX>scrollDistance){
            lastScrollX = lastScrollX - scrollDistance;
        }
        /**
         * 时间不能变,时间要在scrollDistance变化之前
         */
        int time = scrollDistance/100;
        scrollDistance = lastScrollX + scrollDistance;
        // 创建ValueAnimator
        animator = ValueAnimator.ofInt(lastScrollX, scrollDistance);
        // 设置动画的持续时间
        animator.setDuration(time*1000); // 500毫秒

        // 设置插值器,这里使用默认的(线性插值器),但你可以根据需要更改
        animator.setInterpolator(new LinearInterpolator());

        // 添加更新监听器,在动画的每一帧中更新TextView的滚动位置
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                lastScrollX = scrollX;
                scrollStatus.putScrollStatus(showMessage);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        animator.addUpdateListener(animation -> {
            scrollX = (int) animation.getAnimatedValue();
            setScrollX(scrollX);
        });

        // 开始动画
        animator.start();

    }


    // 确保在视图不再需要时停止滚动
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        isScrolling = false;
        if(animator!=null){
            animator.pause();
        }
    }

    public void setShowText(String message,ScrollStatus scrollStatus){
        this.showMessage = message;
        setText(message);
        this.scrollStatus = scrollStatus;
    }

    /**
     * 设置接下来需要滚动的内容以及距离
     */
    public void setNextScrollTextMessage(){
        if(maxScrollX > 0){
            append(Space+showMessage);
            int textSize = (int) getPaint().measureText(Space);
            int textWidth = (int) getPaint().measureText(showMessage);
            int allWidth = textWidth+textSize;
            startScrolling(allWidth);
        }else {
            scrollStatus.putScrollStatus(showMessage);
        }
    }

    public interface ScrollStatus{
        void putScrollStatus(String message);
    }

}

继续滚动方法setNextScrollTextMessage

单条的滚动没问题,多条第二次同时滚动的时候应该是多少有一点点的延迟的,会造成头部不对齐问题,待优化

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

csdn_zxw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值