You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Android ListView中Marquee Text无法滚动,寻求解决方案

嘿,这个问题我太熟了!ListView的子项TextView默认拿不到焦点,而Marquee(跑马灯)效果必须要TextView获得焦点才能触发,这就是为啥单独用没问题,放ListView里就失效的原因。下面给你几个亲测有效的解决方案,你可以根据自己的情况选:

方案一:给TextView抢焦点的权限

直接在你的列表项XML里,给需要滚动的TextView加上这些属性:

android:focusable="true"
android:focusableInTouchMode="true"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"

另外,别忘了给列表项的根布局加个属性,防止根布局抢了TextView的焦点:

android:descendantFocusability="blocksDescendants"

这个属性会让根布局的子控件优先获取焦点,这样TextView就能顺利触发跑马灯了。

方案二:自定义TextView(最稳定的方案)

如果方案一偶尔出现失效的情况,比如滑动ListView后停止滚动,那可以自定义一个TextView,强制让它“认为”自己一直有焦点:

public class MarqueeTextView extends TextView {
    public MarqueeTextView(Context context) {
        super(context);
        initMarquee();
    }

    public MarqueeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initMarquee();
    }

    public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initMarquee();
    }

    private void initMarquee() {
        setSingleLine(true);
        setEllipsize(TextUtils.TruncateAt.MARQUEE);
        setMarqueeRepeatLimit(-1); // 设置为-1就是无限循环
    }

    @Override
    public boolean isFocused() {
        // 强制返回true,让TextView一直处于"有焦点"的状态
        return true;
    }
}

之后在你的列表项XML里,把原生TextView换成这个自定义的:

<com.your.package.name.MarqueeTextView
    android:id="@+id/tv_scroll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="你的超长滚动文本内容" />

这个方案基本不会有适配问题,不管ListView怎么复用View,跑马灯都会正常工作。

方案三:在Adapter里手动触发

有时候ListView的View复用会导致跑马灯状态丢失,你可以在Adapter的getView()方法里,每次绑定数据时手动开启滚动:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        convertView = LayoutInflater.from(mContext).inflate(R.layout.your_list_item_layout, parent, false);
        holder = new ViewHolder();
        holder.scrollTv = convertView.findViewById(R.id.tv_scroll);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    // 绑定数据
    String content = mDataList.get(position);
    holder.scrollTv.setText(content);
    // 手动设置selected为true,触发跑马灯
    holder.scrollTv.setSelected(true);

    return convertView;
}

static class ViewHolder {
    TextView scrollTv;
}

setSelected(true)和焦点的作用类似,能触发Marquee效果,而且在View复用场景下很管用。

最后再提几个必注意的点:

  • 一定要保证TextView是单行显示,不管是用android:singleLine="true"还是android:maxLines="1",多行文本是不会触发跑马灯的。
  • 文本长度必须超过TextView的显示宽度,如果文本本身就能完全显示,跑马灯自然不会启动。
  • 如果用了自定义TextView,记得不要在XML里重复设置冲突的属性,比如android:ellipsize="end"会覆盖Marquee的设置。

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

火山引擎 最新活动