You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义VerticalTextView滚动功能失效,附代码求助解决方案

解决VerticalTextView跑马灯滚动失效的问题

看起来你自定义的VerticalTextView因为重写了onDraw方法,跳过了原生TextView处理跑马灯(marquee)的核心逻辑,导致滚动功能失效。下面我来帮你分析问题并给出修复方案:

问题根源

你的onDraw方法直接调用getLayout().draw(canvas)绘制文本,但原生TextView的跑马灯效果依赖于内部的marquee布局计算和偏移处理,你没有处理这些逻辑,也没有调用原生的marquee相关绘制流程,所以滚动不会生效。另外,原生的marquee是为水平文本设计的,旋转后的垂直文本需要适配偏移逻辑。

修复后的VerticalTextView实现

修改你的自定义View,加入marquee的适配逻辑:

public class VerticalTextView extends TextView {
    private final boolean topDown;

    public VerticalTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 可选:从自定义属性读取topDown配置,这里默认设为false
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalTextView);
        topDown = a.getBoolean(R.styleable.VerticalTextView_topDown, false);
        a.recycle();
        
        // 确保View支持聚焦,这是触发marquee的必要条件之一
        setFocusable(true);
        setFocusableInTouchMode(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        TextPaint textPaint = getPaint();
        textPaint.setColor(getCurrentTextColor());
        textPaint.drawableState = getDrawableState();

        canvas.save();

        // 执行画布旋转,保持原有的垂直文本逻辑
        if (topDown) {
            canvas.translate(getWidth(), 0);
            canvas.rotate(90);
        } else {
            canvas.translate(0, getHeight());
            canvas.rotate(-90);
        }

        // 适配marquee的偏移:原生scrollX对应水平偏移,旋转后转为垂直方向的偏移
        int scrollOffset = getScrollX();
        if (!topDown) {
            scrollOffset = -scrollOffset;
        }
        canvas.translate(getCompoundPaddingLeft() - scrollOffset, getExtendedPaddingTop());

        // 使用marquee专用的Layout(如果当前处于跑马灯状态)
        Layout layout = getLayout();
        if (isMarqueeFloating()) {
            layout = getMarquee();
        }
        if (layout != null) {
            layout.draw(canvas);
        }

        canvas.restore();
    }

    @Override
    public boolean isFocused() {
        // 让选中状态也能触发marquee,不需要强制获得焦点
        return isSelected() || super.isFocused();
    }
}

调整使用代码

注意移除ScrollingMovementMethod(它是用于手动滚动的,和自动marquee冲突),并确保文本长度足够触发滚动:

VerticalTextView image = new VerticalTextView(context,null);
// 必须设置足够长的文本,超过View的显示宽度(旋转后的宽度即原View的高度)
image.setText("这是一段需要垂直滚动的超长测试文本,长度要超过VerticalTextView的显示范围才能触发跑马灯");
image.setEllipsize(TextUtils.TruncateAt.MARQUEE);
image.setSingleLine(true);
image.setMarqueeRepeatLimit(-1); // 无限重复滚动
image.setSelected(true); // 关键:必须设置选中状态
image.setFreezesText(true);
// 移除这行:image.setMovementMethod(new ScrollingMovementMethod());

额外注意事项

  • 文本长度必须超过VerticalTextView的显示宽度(也就是旋转后的宽度,对应原布局中的高度),否则不会触发跑马灯。
  • 确保setSelected(true)被正确设置,这是触发marquee滚动的核心开关之一。
  • 如果需要在布局中使用自定义属性,记得在res/values/attrs.xml中添加:
    <declare-styleable name="VerticalTextView">
        <attr name="topDown" format="boolean"/>
    </declare-styleable>
    

内容的提问来源于stack exchange,提问作者pavani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:31:32