ListView的convertView丢失了链接跨度

5
当滚动ListView时,为什么链接会丢失?从调试中可以明显看到,在convertView上第二次没有添加标签TextView
这是适配器的getView调用的代码片段。
    ...
    String body = MyItemDetails.getBody(); // String to linkify

    final Spannable spannable = MyCustomUri.addHashtagSpans(context, body);
    viewHolder.textView.setText(spannable);

    viewHolder.textView.setTextIsSelectable(true); // adds additional spans
    viewHolder.textView.setMovementMethod(ArrowKeyMovementMethod.getInstance());
    viewHolder.textView.setAutoLinkMask(Linkify.WEB_URLS);
    ...

MyCustomUri.addHashtagSpans() 创建了一个 SpannableString,其中包含继承自 URLSpanMyCustomSpan

问题在于,当我在 ListView 中上下滚动时,链接会丢失。然而,当屏幕第一次打开时,它设置正确。

现在,我通过禁用对 convertView 的重用来进行了一次不怎么好的修复 :( 有什么更好的解决办法吗?


请发布您的getView方法的完整代码(如果您正在扩展CursorAdapter,则包括bindView和newView)。 - Alexander Mikhaylov
4个回答

1
你好 使用这个自定义类。
public class MyCustomSpannable extends ClickableSpan {
String Url;
Context mContext;

public MyCustomSpannable(String Url, Context context) {
    this.Url = Url;
    mContext = context;
}

@Override
public void updateDrawState(TextPaint ds) {
    // Customize your Text Look if required
    ds.setColor(mContext.getResources().getColor(R.color.red_text));
    ds.setFakeBoldText(true);
    // ds.setStrikeThruText(true);
    ds.setTypeface(CommonFunctios.getfontNormal(mContext));
    // ds.setUnderlineText(true);
    // ds.setShadowLayer(10, 1, 1, Color.WHITE);
    // ds.setTextSize(15);
}

@Override
public void onClick(View widget) {
}

public String getUrl() {
    return Url;
}
}

在适配器中,将您的代码替换为以下内容。
String text = holder.txt_terms.getText().toString();
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(text);
MyCustomSpannable customSpannable = new MyCustomSpannable(text,
            mcontext) {

        @Override
        public void onClick(View widget) {
            Log.e("on click", "message");
            ((OpticalOffersActivity) mcontext).callDialogBox(position);
        }
    };
    stringBuilder.setSpan(customSpannable, 0, text.length(),
            Spannable.SPAN_INCLUSIVE_INCLUSIVE);

    holder.txt_terms.setText(stringBuilder, BufferType.SPANNABLE.SPANNABLE);
    holder.txt_terms.setMovementMethod(LinkMovementMethod.getInstance());

希望它能对你有所帮助。

1
if(convertView==null)
    {

        convertView.setTag(holder);
    }
else
    {
            holder = (ViewHolder)convertView.getTag();
    }




 ...
        String body = MyItemDetails.getBody(); // String to linkify

        final Spannable spannable = MyCustomUri.addHashtagSpans(context, body);
        viewHolder.textView.setText(spannable);

        viewHolder.textView.setTextIsSelectable(true); // adds additional spans
        viewHolder.textView.setMovementMethod(ArrowKeyMovementMethod.getInstance());
        viewHolder.textView.setAutoLinkMask(Linkify.WEB_URLS);
        ...

那个可跨度的代码必须放在if-else循环之外的getView()方法中,就像我在上面的代码中所做的那样。

没错,我已经将可扩展代码放在if-else块之外。问题出现在convertView被重用时。第二次没有添加MyCustomUri跨度。 - ViliusK

1
这里涉及到几个问题,让我逐一解决。你直接提出的问题(链接消失)是由于在手动添加文本跨度时,TextView中的自动链接行为可能不起作用,最好不要使用它。移除setAutoLinkMask()触发器,链接消失问题就会消失。
相反,你可以使用Linkify直接将同样的网络链接行为添加到文本跨度中。然而,这只是你问题的一部分。你选择的MovementMethod并不真正兼容可点击的链接。现在它(部分地)在你的代码中工作的原因是因为自动链接掩码导致视图的MovementMethod在幕后被秘密调整为LinkMovementMethod,然后在视图被回收后重置。我通常使用的模式(应用于你的代码示例)是:
final Spannable spannable = MyCustomUri.addHashtagSpans(context, body);
Linkify.addLinks(spannable, Linkify.WEB_URLS);
viewHolder.textView.setText(spannable);

addLinkMovementMethod(textView);

addLinkMovementMethod()是一个辅助方法,代码如下:

private void addLinkMovementMethod(TextView t) {
    MovementMethod m = t.getMovementMethod();

    if ((m == null) || !(m instanceof LinkMovementMethod)) {
        if (t.getLinksClickable()) {
            t.setMovementMethod(LinkMovementMethod.getInstance());
        }
    }
}

如果没有必要,这样可以避免在每次视图回收时重置值。前面的代码块将为您提供适当的链接,且不会消失...

然而,从您调用的方法来看,我猜测您还尝试使列表中的链接文本可选择(例如调用 setTextIsSelectable() 并选择 ArrowKeyMovementMethod)。由于我上面讨论过的 MovementMethod 问题,这变得有点棘手。为了创建一个支持链接点击和文本选择的 MovementMethod,我将引导您查看这个现有的 SO 帖子,其中包含所需定制的示例代码: Can a TextView be selectable AND contain links?


这看起来是个很有前途的解决方案。我会试一下。 - ViliusK

1

当TextView的数据被写入包中以进行保留时,一些可扩展信息可能会丢失。

请参见TextView.onSaveInstanceState(), TextView.onRestoreInstanceState(), 和 TextView.SavedState

确定Android将保留什么内容通常会非常令人沮丧。我经常只需在我的视图上调用 setSaveEnabled(false) 以禁用基本小部件的不可预测行为。

此外,视图持有者模式仅真正用于保留视图/布局实例层次结构。这样可以避免您每次调用 getView() 时都要填充或查找视图。在从 getView() 中呈现视图时,更新视图的数据始终是您的责任。

您无需完全禁用视图持有者模式,而只需在每个 getView() 中简单地更新文本即可,就像您可能已经在做的那样。


尝试使用setSaveEnabled(false),但没有帮助 :( - ViliusK
是的,那只是一个旁注。它不会直接解决问题。 - Kyle Ivey
重点是你不能总是依赖Android来保留你的视图状态,应该养成自己做的习惯。 - Kyle Ivey
那么有没有一种方法可以重置 TextView 的状态? - ViliusK
数据通过SavedState和视图回收机制在配置更改中的持久性是两种非常不同的机制。这些都与ListView在getView()中重用视图元素的方式无关。 - devunwired
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接