【问题标题】:RecyclerView Lazy Loading (Universal Image Loader)RecyclerView 延迟加载(通用图像加载器)
【发布时间】:2016-01-19 01:16:13
【问题描述】:

使用Android Universal Image Loader 和 RecyclerView 异步加载图像,我遇到了和其他人一样的错误,图像混淆了;直到它们全部加载并被缓存。

适配器代码:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.media.Image;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;

import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.utils.MemoryCacheUtils;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import romina.theftest.connectivity.ImgDownloader;

/**
 * Created by romin on 18/1/2016.
 */
public class ProductRecyclerViewAdapter extends RecyclerView.Adapter {
    private List<Product> mValues;
    private Context mContext;
    private View.OnClickListener mListener;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;
    private final String OLD_DOMAIN = "";
    private final String NEW_DOMAIN = "";

    public ProductRecyclerViewAdapter(Context mContext, View.OnClickListener mListener) {
        this.mContext = mContext;
        this.mListener = mListener;
    }

    public ProductRecyclerViewAdapter(List<Product> mValues, Context mContext, View.OnClickListener mListener) {
        this(mContext, mListener);
        this.mListener = mListener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.product_list_item, parent, false);

        Log.d(ProductRecyclerViewAdapter.class.getSimpleName(), "onCreateViewHolder");
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ViewHolder actualViewHolder = (ViewHolder) holder;
        actualViewHolder.mItem = mValues.get(position);
        actualViewHolder.mIdView.setText("" + mValues.get(position).getName());
        Log.d(ProductRecyclerViewAdapter.class.getSimpleName(), "onBindViewHolder pos " + position);

        ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
        final String finalImgURL = mValues.get(position).getImgURL().toString().replaceAll(OLD_DOMAIN, NEW_DOMAIN);
            imageLoader.displayImage(finalImgURL, actualViewHolder.mImgView);

        setAnimation(actualViewHolder.mContentView, position);
    }

    public void setDataSet(List<Product> newValues) {
        mValues = newValues;
        notifyDataSetChanged();
    }


    @Override
    public int getItemCount() {
        return mValues == null ? 0 : mValues.size();
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position) {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition) {
            Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public final View mView;
        public final TextView mIdView;
        public final ImageView mImgView;
        public final TextView mContentView;
        public Product mItem;

        public ViewHolder(View view) {
            super(view);
            mView = view;
            mIdView = (TextView) view.findViewById(R.id.product_quantity_title);
            mContentView = (TextView) view.findViewById(R.id.product_quantity_title);
            mImgView = (ImageView) view.findViewById(R.id.product_quantity_image);

            mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (null != mListener) {
                        mListener.onClick(v);
                    }
                }
            });
        }

        @Override
        public String toString() {
            return super.toString() + " '" + mContentView.getText() + "'";
        }
    }
}

我知道它必须在 onBindViewHolder 中,因为它被要求更新每个视图,但我没有正确更新 ImageView

它与库无关。在不缓存图像的情况下进行延迟加载时也会发生相同的行为。错误是因为我不知道如何更新onBindViewHolder中的ImageView

谢谢!

【问题讨论】:

  • 您好,您是如何解决问题的?请分享解决方案。我有同样的问题。当我滚动 Recyleview 时,UIL 将不同的图像与已经加载的先前图像混合/重新加载。
  • @JasbirBhinder 我做了类似于接受的答案的事情,但我现在意识到最好使用毕加索。
  • 感谢您的建议

标签: android android-studio lazy-loading android-recyclerview universal-image-loader


【解决方案1】:

您需要确保只在 App 中初始化一次 ImageLoader。 创建一个类并使用 Application 扩展它,然后放入 AndroidManifest.xml 中,如下所示:

<application
        android:name=".App"
.../>

应用类

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // UNIVERSAL IMAGE LOADER SETUP
        DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
                .resetViewBeforeLoading(true)
                .cacheOnDisk(true)
                .cacheInMemory(true)
                .imageScaleType(ImageScaleType.EXACTLY)
                .displayer(new FadeInBitmapDisplayer(300))
                .build();

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
                .defaultDisplayImageOptions(defaultOptions)
                .memoryCache(new WeakMemoryCache())
                .diskCacheSize(100 * 1024 * 1024)
                .build();

        ImageLoader.getInstance().init(config);
        // END - UNIVERSAL IMAGE LOADER SETUP
    }
}

对于你想知道的onBindViewHolder

@Override
public void onBindViewHolder(final CategoryHolder holder, final int i) {

    holder.categoryImage.setImageBitmap(null);

    if (mRow.get(i).getImage() != null && !mRow.get(i).getImage().equals("")) {
        final File image = DiskCacheUtils.findInCache(mRow.get(i).getImage(), imageLoader.getDiskCache());
        if (image!= null && image.exists()) {
            Picasso.with(getActivity()).load(image).fit().centerCrop().into(holder.categoryImage);
        } else {
            imageLoader.loadImage(mRow.get(i).getImage(), new ImageLoadingListener() {
                @Override
                public void onLoadingStarted(String s, View view) {
                    holder.categoryImage.setImageBitmap(null);
                }

                @Override
                public void onLoadingFailed(String s, View view, FailReason failReason) {

                }

                @Override
                public void onLoadingComplete(String s, View view, final Bitmap bitmap) {
                    Picasso.with(getActivity()).load(s).fit().centerCrop().into(holder.categoryImage);

                }

                @Override
                public void onLoadingCancelled(String s, View view) {

                }
            });
        }
    }else {
        holder.categoryImage.setImageBitmap(null);
    }

    holder.categoryName.setText(mRow.get(i).getName().toUpperCase());

}

【讨论】:

  • 虽然有效(谢谢!)你知道在 onBindViewHolder 中更新视图的正确方法是什么吗?我知道我迟早会面对任何其他观点的同样问题;但我看到的所有示例都是使用 TextViews,更新起来相当简单。
  • 您只需要在 Application 类中初始化一次 Imageloader。过段时间我会更新代码。
  • 毕加索和 UIL 在一起?能否请您启发我们并告诉我们您为什么同时使用 Picasso 和 UIL?
  • @k0sh 我使用 UIL 进行缓存,使用 Picasso 进行复杂的图像转换,并且内存使用量最少。如果有错误,请帮我纠正,因为我仍在探索这个领域。
  • 我没有使用毕加索,但@Keshav 的 onCreate 代码有效。
【解决方案2】:

我认为错误是由于图像加载器的多个实例造成的:

ImageLoader imageLoader = ImageLoader.getInstance(); 

尝试为适配器创建一个相同的实例。用其他类变量声明它。您通过在onBindViewHolder() 中调用它来创建图像加载器的多个实例。

【讨论】:

  • 您好,感谢您的回复。该错误不是由此引起的,但是您是对的,我不应该在该方法中获取实例。谢谢!
  • 您在多个实例上是正确的,但这不会给出任何错误。只是RecyclerView每次滚动都会调用。
猜你喜欢
  • 1970-01-01
  • 2012-09-15
  • 2013-10-24
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-04
相关资源
最近更新 更多