为 RecyclerView 缓存 Spannable 字符串

Cache Spannable Strings for RecyclerView

我有一个 Recyclerview,它包含很多消息,类似于聊天屏幕。

在每个列表项中,我有一个 TextView,我在其中设置了 Spannable 个字符串。字符串包含图像和文本,有时只是文本。当有大量字符需要用图像替换时,我必须用 Drawables 替换字符串中的一些字符的方法会变得非常昂贵,一个明显的 jitter/lag 开始出现 onScroll .我还将昂贵的方法放在 AsyncTask 中,这大大提高了 RecyclerView 的速度。

问题:

是否可以缓存 Spannable 对象及其所有元数据?如果用户向上滚动一两个屏幕然后向下滚动,我想尝试避免每次在屏幕上呈现行时构建 Spannables

如果缓存是可能的,我什至会更进一步实现一个 "Pre-Cache" 方法,该方法在单独的线程上构建和缓存 Spannable,因此如果用户上下滚动,它将只是从缓存加载而不是构建它(如果存在)会导致 "faster" 或 "smoother" 用户体验。

可绘制对象

可绘制对象来自 FilePaths,作为 ImageSpan 插入到 spannable 字符串中,然后在 AsyncTask 中设置为 TextView

首先,我建议您查看 RecyclerViewsetItemViewCacheSize(int) 方法。接下来,如果您的 Spannable 中有很多可绘制对象,也许值得检查一下这些可绘制对象的大小,它们可能太大了。另一个想法是在将项目传递给 bindViewHolder.

之前预编译整个项目列表

我能够实现我想要的。

-1。创建了 class LruSpannableCache 扩展到 LruCache

并覆盖 sizeOf 方法

@override
protected int sizeOf(Object key, Object value)
{
    //The cache size will be measured in kilobytes rather than number of items.
    return ((CacheSpannable)value).size() / 1024;
}

-2。创建了一个对象 CacheSpannable

public class CacheSpannable
{
    private int key;
    private ISpannable text;

    // Getters and Setters for key and text...

    public int size()
    {
        return text.length(); //Can also add the byte length for `key`
    }
}

-3。最后用 addget 缓存的 spannables

的静态方法创建了一个 class
private static LruCache spannableCache;
private static final int cacheSize = Runtime.getRuntime().maxMemory() / 1024 / 6;

public static void addSpannable(int key, CacheSpannable value)
{
    if (spannableCache == null) spannableCache = new LruSpannableCache(cacheSize);

    if (getSpannable(key) == null)
    {
        spannableCache.put(key, value);
    }
}

public static CacheSpannable getSpannable(int key)
{
    if (spannableCache == null) spannableCache = new LruSpannableCache(cacheSize);

    return (CacheSpannable)spannableCache.get(key);
}

然后要使用它,我只需检查 Spannable 是否被缓存(getSpannable(key) != null),然后将其设置为 TextView,如果没有,则构建它,添加到缓存并然后将其设置为 TextView.

下一步是实施磁盘缓存以无限期保留 spannables