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




