1

我有一个包含 ImageView 和 TextView 的 ListView。我正在对 ArrayAdapter 进行子类化,以便可以通过子类化的 AsyncTask 从 Internet 加载图像。到目前为止一切都很好。

问题是,如果我尝试使用 convertView,我会遇到图像被短暂回收到错误行的问题。(这是一家公司的标志,所以这……不好。)

但是,如果我不使用 convertView,则用户滚动时图像会丢失。因此,如果他们向下滚动并向后滚动,则从互联网重新加载图像(这显然不利于数据使用、电池寿命等)

有没有一种简单的方法来解决这个问题,这样它就不会每次都从互联网加载,或者移动图像?

这是我目前使用的:

public class SupplierAdapter extends ArrayAdapter<Supplier>{
    int resource;
    String response;
    Context context;

    public SupplierAdapter(Context context, int resource, List<Supplier> suppliers) {
        super(context, resource, suppliers);
        this.resource = resource;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
    LinearLayout view;
        Supplier s = getItem(position);

        if(convertView == null){
            view = new LinearLayout(getContext());
            String inflater = Context.LAYOUT_INFLATER_SERVICE;
            LayoutInflater vi;
            vi = (LayoutInflater) getContext().getSystemService(inflater);
            vi.inflate(resource, view, true);
        } else {
            view = (LinearLayout) convertView;
        }

        ImageView iv = (ImageView) view.findViewById(R.id.thumbnail);
        // s.getThumbnail returns a URL
        new DownloadImageTask(iv).execute(s.getThumbnail()); 

        TextView titleText =(TextView) view.findViewById(R.id.titleText);
        titleText.setText(s.getTitle());
        titleText.setTag(s.getId());

        return view;
    }
}

非常感谢所有帮助:)

4

4 回答 4

2

约翰,尝试使用raptureinvenice.comWebImageView。我最近重构了我的代码以使用这个简单而轻量级的库,到目前为止,一切都很好。它很容易提供两级缓存,同时更新多个目标,并且非常容易设置。看起来它似乎减少了大约 1/20 的显示图像请求。除了这个可能是库的错误的潜伏错误之外,它非常好。

于 2011-11-17T23:11:41.987 回答
1

这是一个烦人的复杂问题。我什至拥有两层缓存(内存和磁盘缓存),以在第二个线程(在您的情况下为 AsyncTask)中更智能的更新之上平滑性能。有很多小细节,比如尽量不要两次下载相同的图像(如果您为多个列表项使用相同的公司徽标,这是一个问题)。不过,显示错误图像的主要问题可以简单地通过跟踪与 ImageView 关联的当前 URL 是什么来解决,并确保在设置最终图像之前与传递给 AsyncTask 的 URL 相同。您也许可以将该信息存储在 HashMap 中。实际上,我只是将所有这些东西抽象到一个 WebImageView 类中,该类扩展了 ImageView 以使其全部可重用。

于 2011-11-17T22:56:19.497 回答
1

对于 RAM 图像缓存,您可以使用SoftReference. 所以代码会是这样的:

ImageView iv = (ImageView) view.findViewById(R.id.thumbnail);
// s.getDrawable returns a SoftReference to Drawable
SoftReference<Drawable> thumbRef = s.getDrawable();
Drawable thumb = thumbRef.get();
if (thumb == null) {
    new DownloadImageTask(s).execute(); 
} else {
    iv.setDrawable(thumb);
}

DownloadImageTask将获取图像,包装成SoftReference,设置对集合的引用并通知适配器底层数据已更改。当然,设计可能会更有效,但这只是一个粗略的计划。

于 2011-11-17T22:56:38.203 回答
1

如果您只需要下载几张图片并且不需要显示大图,则可以使用以下代码。它将位图存储在内存中。效果很好是图像不是太大。

我将代码更改为您的情况:

导入android.graphics.Bitmap;

公共类供应商{

// Data
private String  mText;
private Bitmap  mImage;
private String  mImageUrl;

// Flags
private boolean mIsLoading;

public Supplier() {
    mText       = "test";
    mImage      = null;
    mImageUrl   = "image_url";
    mIsLoading  = false;
}

public Supplier setLoadingStatus(boolean pIsLoading){
    mIsLoading = pIsLoading;
    return this;
}

public boolean isLoading(){
    return mIsLoading;
}

public Supplier setImageUrl(String pImageUrl){
    mImageUrl = pImageUrl;
    return this;
}

public String getImageUrl(){
    return mImageUrl;
}

public Supplier setText(String pText){
    mText = pText;
    return this;
}

public String getText(){
    return mText;
}

public Supplier setImageBitmap(Bitmap bmp){
    mImage = bmp;
    return this;
}

public Bitmap getImageBitmap(){
    return mImage;
}       

}

导入 java.io.IOException;导入 java.io.InputStream;导入 java.net.HttpURLConnection;导入 java.net.MalformedURLException;导入 java.net.URL;导入 java.util.ArrayList;

导入android.R;导入android.content.Context;导入android.graphics.Bitmap;导入android.graphics.BitmapFactory;导入android.os.Handler;导入android.os.Message;导入 android.view.LayoutInflater;导入android.view.View;导入android.view.ViewGroup;导入 android.widget.BaseAdapter;导入android.widget.ImageView;导入 android.widget.TextView;

公共类 TestAdapter 扩展 BaseAdapter{

protected static final int MSG_IMAGE_DOWNLOADED = 0;

// Constants
private final String TAG = "TestAdapter";

private ArrayList<Supplier> mItems;
private Context mContext;
private LayoutInflater mLf;
private Handler mHandler;


public TestAdapter(Context pContex) {
    mContext    = pContex;
    mLf         = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mItems      = new ArrayList<Supplier>();
    mHandler    = new Handler(){
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_IMAGE_DOWNLOADED:
                if(null != msg.obj){
                    mItems.get(msg.arg1).setImageBitmap((Bitmap)msg.obj)
                                        .setLoadingStatus(false);
                    notifyDataSetChanged();
                }
                break;

            default:
                break;
            }
        };
    };
}

public TestAdapter addItem(Supplier pItem){
    mItems.add(pItem);
    return this;
}

@Override
public int getCount() {
    return mItems.size();
}

@Override
public Supplier getItem(int position) {
    return mItems.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder vh;

    if(null == convertView){
        convertView     = mLf.inflate(R.layout.your_resource, parent, false);
        vh              = new ViewHolder();
        vh.mTextView    = (TextView)convertView.findViewById(R.id.your_textview_from_resource);
        vh.mImage       = (ImageView)convertView.findViewById(R.id.yout_imageview_from_resource);
        convertView.setTag(vh);
    }else{
        vh = (ViewHolder)convertView.getTag();
    }       

    vh.mTextView.setText(mItems.get(position).getText());
    if(mItems.get(position).getImageBitmap() == null && !mItems.get(position).isLoading()){
        // download image
        downloadImage(mItems.get(position).getImageUrl(), position);
        // set a flag to know that the image is downloading and it is not need to 
        // start another download if the getView method is called again.
        mItems.get(position).setLoadingStatus(true);
    }else{
        vh.mImage.setImageBitmap(mItems.get(position).getImageBitmap());
    }
    return null;
}

private void downloadImage(String pImageUrl, int pItemPosition){
    final int cItemPosition = pItemPosition;
    final String cImageUrl  = pImageUrl; 

    Thread tGetImage = new Thread(new Runnable() {          
        @Override
        public void run() {             
            Message msg = new Message();
            msg.what = MSG_IMAGE_DOWNLOADED;

            BitmapFactory.Options options = new BitmapFactory.Options();                                                                                            
            Bitmap  bmImg;      
            URL     myFileUrl = null; 

            try {
                myFileUrl= new URL(cImageUrl);
            } catch (MalformedURLException e) {
                e.printStackTrace();                        
            }                       
            try {
                HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
                conn.setDoInput(true);
                conn.connect();
                InputStream is  = conn.getInputStream();
                bmImg           = BitmapFactory.decodeStream(is, null, options);

                is.close();
                conn.disconnect();          
                msg.obj     = bmImg;                    
            } catch (IOException e) {
                e.printStackTrace();                        
            }                       
            msg.arg1 = cItemPosition;
            mHandler.sendMessage(msg);              
        }
    });
    tGetImage.start();
}

private class ViewHolder{
    public TextView     mTextView;
    public ImageView    mImage;
}

}

该代码未经测试,但应该可以工作。

于 2011-11-17T23:03:10.323 回答