首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在RecyclerView中显示“重”图像?

如何在RecyclerView中显示“重”图像?
EN

Stack Overflow用户
提问于 2020-08-16 21:13:03
回答 1查看 812关注 0票数 1

我需要在RecyclerView中显示一些图像。当用户滚动列表时,这些图像应该被呈现为“动态”。绘制每幅图像需要很长时间: 50-500ms。在图像显示给用户之前,会显示一个进度条。

由于渲染时间长,这个部分被放置在一个AsyncTask中。

见下面的代码:

代码语言:javascript
运行
复制
class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView myImg;
        ProgressBar myProgressBar;

        public ViewHolder(View view) {
            super(itemView);
            myImg = view.findViewById(R.id.myImg);
            myProgressBar = view.findViewById(R.id.myProgressBar);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.myProgressBar.setVisibility(View.VISIBLE);
        AsyncTask.execute(() -> {  
            Bitmap bitmap = longRenderingFunction();
            holder.myImg.post(() -> {
                holder.myImg.setImageBitmap(bitmap);
                holder.myProgressBar.setVisibility(View.GONE);

如果用户正在缓慢滚动列表,例如每秒1-2个屏幕,则图像将被正确加载。有时人们会注意到进度条,它很快就消失了。

但是,如果用户滚动得非常快,图像就会出现,然后被替换,有时会被替换两次:

一般来说,在快速滚动时,很明显:

调用多个代码,并启动每个可回收项目的多个ImageView.。对于同一个项目,即使触发了一个新的onBindViewHolder,老的AsyncTask也会继续运行H 211H 112,然后AsyncTasks为相同的ViewHolder完成一个,在ImageView.>d17放置自己生成的位图后,将自己生成的位图放到ImageView.上。

我的意图是:

AsyncTasks

  • maximum:
  • 最低限度:不要从过时的setImageBitmap中尽快停止已经过时的AsyncTasks,以节省系统资源

我很想听听这个问题的一些提示或可能的解决方案。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-01 19:43:08

这是我想出的解决办法。

停止闪烁

第一部分非常简单:

提出了一个简单的指数:

代码语言:javascript
运行
复制
int loadingPosition;

onBindViewHolder开始时,保存当前位置:

代码语言:javascript
运行
复制
holder.loadingPosition = position;

在切换位图之前,将根据上次启动检查当前位置。如果一个新任务已经启动,索引已经更改,那么图像将不会被更新:

代码语言:javascript
运行
复制
if (holder.loadingPosition == position) {
    holder.myImg.setImageBitmap(bitmap);

改善性能

为了防止任务在不需要时进一步运行,引入了一个Future对象。它允许管理已启动的任务,而AsyncTask不允许:

代码语言:javascript
运行
复制
Future<?> future;

通过ExecutorService而不是AsyncTask启动异步任务

代码语言:javascript
运行
复制
holder.future = executor.submit(() -> { ...

如果仍在运行任务,则停止正在运行的任务:

代码语言:javascript
运行
复制
if ((holder.future != null) && (!holder.future.isDone()))
    holder.future.cancel(true);

完整代码

整个代码看起来是这样的:

代码语言:javascript
运行
复制
class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private ExecutorService executor = Executors.newSingleThreadExecutor();

    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView myImg;
        ProgressBar myProgressBar;
        int loadingPosition;
        Future<?> future;

        public ViewHolder(View view) {
            super(itemView);
            myImg = view.findViewById(R.id.myImg);
            myProgressBar = view.findViewById(R.id.myProgressBar);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.loadingPosition = position;
        if ((holder.future != null) && (!holder.future.isDone()))
            holder.future.cancel(true);
        holder.myProgressBar.setVisibility(View.VISIBLE);
        holder.future = executor.submit(() -> {
            Bitmap bitmap = longRenderingFunction();
            holder.myImg.post(() -> {
                if (holder.loadingPosition == position) {
                    holder.myImg.setImageBitmap(bitmap);
                    holder.myProgressBar.setVisibility(View.GONE);

也许使用这两种方法有点过分,但它提供了某种程度的保险。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63441989

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档