1

ImageViewとTextViewを含むListViewがあります。私はArrayAdapterをサブクラス化して、サブクラス化されたAsyncTaskを介してインターネットから画像をロードできるようにしています。これまでのところすべて良い。

問題は、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を使ってみてください。私は最近、この簡単で軽量なライブラリを使用するようにコードをリファクタリングしました。2 レベルのキャッシュを簡単に提供し、複数のターゲットを同時に更新し、セットアップが非常に簡単でした。画像を表示するリクエストが約 1/20 減少するようです。ライブラリのせいである可能性のあるこの潜んでいるバグを除けば、それは優れています。

于 2011-11-17T23:11:41.987 に答える
1

これは厄介なほど複雑な問題です。2 番目のスレッド (あなたの場合は AsyncTask) でのよりスマートな更新に加えて、2 層のキャッシュ (メモリとディスク キャッシュ) を使用してパフォーマンスをスムーズにするところまで行きました。同じ画像を 2 回ダウンロードしないようにするなど、細かい点がたくさんあります (これは、複数のリスト アイテムに同じ会社のロゴがある場合に問題になります)。ただし、誤った画像が表示されるという主な問題は、ImageView に関連付けられている現在の URL を追跡し、最終的な画像を設定する前に AsyncTask に渡されたものと同じであることを確認するだけで修正できます。おそらく、その情報を HashMap に保存できます。私は実際にこれらすべてを、ImageView を拡張してすべて再利用可能にする WebImageView クラスに抽象化しました。

于 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;
}       

}

import java.io.IOException; java.io.InputStream をインポートします。java.net.HttpURLConnection をインポートします。java.net.MalformedURLException をインポートします。java.net.URL をインポートします。import 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 をインポートします。

public class 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 に答える