在RecyclerView中滚动时图像闪烁 [英] Image flickering while scrolling in RecyclerView

查看:459
本文介绍了在RecyclerView中滚动时图像闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试显示在设备上找到的歌曲列表,这些歌曲直接从MediaStore请求数据.我正在使用RecyclerView和使用CursorAdapter从MediaStore获取数据的适配器. 调用适配器的onBindViewHolder时,请求将传递到CursorAdapterbindView函数,所有视觉元素均已设置.

I'm trying to display a list of songs found on the device requesting data directly from the MediaStore. I'm using a RecyclerView and an adapter that uses a CursorAdapter to get data from MediaStore. When adapter's onBindViewHolder is called, the request is passed to the bindView function of the CursorAdapter the all visual elements are set.

public class ListRecyclerAdapter3 extends RecyclerView.Adapter<ListRecyclerAdapter3.SongViewHolder> {

    // PATCH: Because RecyclerView.Adapter in its current form doesn't natively support
    // cursors, we "wrap" a CursorAdapter that will do all teh job
    // for us
    public MediaStoreHelper mediaStoreHelper;
    CustomCursorAdapter mCursorAdapter;
    Context mContext;


    public class SongViewHolder extends RecyclerView.ViewHolder {

        public TextView textItemTitle;
        public TextView textItemSub;
        public ImageView imgArt;

        public int position;
        public String album_id;
        public String path_art;
        public String path_file;

        public SongViewHolder(View v) {
            super(v);
            textItemTitle = (TextView) v.findViewById(R.id.textItemTitle);
            textItemSub = (TextView) v.findViewById(R.id.textItemSub);
            imgArt = (ImageView) v.findViewById(R.id.imgArt);
        }
    }

    private class CustomCursorAdapter extends CursorAdapter {

        public CustomCursorAdapter(Context context, Cursor c, int flags) {
            super(context, c, flags);
        }

        @Override
        public View newView(final Context context, Cursor cursor, ViewGroup parent) {

            View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.song_item, parent, false);

            final SongViewHolder holder = new SongViewHolder(v);

            v.setTag(holder);

            return v;
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            SongViewHolder holder = (SongViewHolder) view.getTag();

            holder.position = cursor.getPosition();

            holder.textItemTitle.setText(cursor.getString(cursor.getColumnIndex("title")));
            holder.textItemSub.setText(cursor.getString(cursor.getColumnIndex("artist")));

            holder.album_id = cursor.getString(cursor.getColumnIndex("album_id"));
            holder.path_file = cursor.getString(cursor.getColumnIndex("_data"));

            Picasso.with(holder.imgArt.getContext())
                    .cancelRequest(holder.imgArt);
            holder.imgArt.setImageDrawable(null);

            new DownloadImageTask(mediaStoreHelper, context, holder.imgArt).execute(holder.album_id);
        }

    }

    private class DownloadImageTask extends AsyncTask<String, String, String> {

        private MediaStoreHelper mediaStoreHelper;
        private ImageView imageView;
        private Context context;

        public DownloadImageTask(MediaStoreHelper mediaStoreHelper, Context context, ImageView imageView)
        {
            this.mediaStoreHelper = mediaStoreHelper;
            this.context = context;
            this.imageView = imageView;
        }
        @Override
        protected String doInBackground(String... ids) {
            return mediaStoreHelper.getAlbumArtPath(ids[0]);
        }

        protected void onPostExecute(String result) {
            Picasso.with(context)
                    .load(new File(result))
                    .placeholder(R.drawable.ic_music)
                    .fit()
                    .into(imageView);
        }
    }

    @Override
    public void onBindViewHolder(SongViewHolder holder, int position) {
        // Passing the binding operation to cursor loader
        mCursorAdapter.getCursor().moveToPosition(position);
        mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());
    }

    @Override
    public SongViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Passing the inflater job to the cursor-adapter
        View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
        return new SongViewHolder(v);
    }
}

问题部分是图像加载,它由两部分组成:

The problematic part is image loading with is made of two parts:

  • 使用从Cursor获得的 albumId ,我需要使用ContentResolver来获取专辑封面文件路径
  • 使用文件路径将图像加载到ImageView
  • With the albumId I got from the Cursor, I need use ContentResolver to get album art file path
  • Load the image into the ImageView using the file path

这两个段落需要在后台完成,否则滚动将变得非常缓慢.在bindView函数中,我称为AsyncTask可以完成工作,但是问题是,在快速滚动时,精心设计了多个图像请求,这就是结果:

These two passages need to be done in background otherwise the scrolling will become very laggy. In the bindView function I called a AsyncTask the does the job, but the problem is that, while scrolling fast, several image requests are elaborated and this is the result:

从代码中可以看到,我试图取消特定ImageView上待处理的毕加索请求,但这还不够.这个问题可以解决吗?

As you can see from the code I tried to cancel pending Picasso's requests on a specific ImageView, but that's not enough. Can this problem be fixed?

推荐答案

我通过在ViewHolder中添加一个包含与该项目相关的AsyncTask的字段来解决.在bindView函数中,我先设置AsyncTask.cancel(true),然后在任务内检查isCancelled(),然后使用Picasso.with(...).load(...)应用检索到的图像.这本身解决了闪烁问题.

I solved by adding a field in the ViewHolder containing the AsyncTask relative to that item. In the bindView function I fisrt set the AsyncTask.cancel(true) and, inside the task I made a check isCancelled() before applying the retrieved image using Picasso.with(...).load(...). This itself solved flickering.

bindView

if(holder.downloadImageTask != null)
      holder.downloadImageTask.cancel(true);

      holder.downloadImageTask = (DownloadImageTask) new DownloadImageTask(mediaStoreHelper, context, holder.imgArt).execute(holder.album_id);

AsyncTask

private class DownloadImageTask extends AsyncTask<String, String, String> {

        private MediaStoreHelper mediaStoreHelper;
        private ImageView imageView;
        private Context context;

        public DownloadImageTask(MediaStoreHelper mediaStoreHelper, Context context, ImageView imageView)
        {
            this.mediaStoreHelper = mediaStoreHelper;
            this.context = context;
            this.imageView = imageView;
        }
        @Override
        protected String doInBackground(String... ids) {
            return mediaStoreHelper.getAlbumArtPath(ids[0]);
        }

        protected void onPostExecute(String result) {
            if(!isCancelled())
                Picasso.with(context)
                        .load(new File(result))
                        .placeholder(R.drawable.ic_music)
                        .fit()
                        .into(imageView);
        }
    }

为了完整起见,这还会从回收的物品中删除图像并设置一个占位符.

For the sake of completeness, this also remove image from recycled items and set a placeholder.

@Override
public void onViewRecycled(SongViewHolder holder) {
    super.onViewRecycled(holder);
    Picasso.with(holder.itemView.getContext())
            .cancelRequest(holder.imgArt);
    Picasso.with(holder.itemView.getContext())
            .load(R.drawable.ic_music)
            .fit()
            .into(holder.imgArt);
}

此解决方案使我认为问题是从MediaStore检索图像到将图像实际从Picasso应用于ImageView之间的时间在AsyncTask内部.

This solution made me think that the problem was the amount of time intercurring inside the AsyncTask between image retrieval from MediaStore and the time when the image is actually applied into the ImageView from Picasso.

这篇关于在RecyclerView中滚动时图像闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆