2

私は、カスタムリストビュー(リスト要素ごとの画像+タイトル+簡単な説明)にニュースを表示することを含む、比較的基本的なニュースリーダーアプリを構築しています。

私の質問は、サーバーからダウンロードした画像を保存して、リストビューに添付するにはどうすればよいですか?画像は比較的小さく、通常は.jpeg形式で200X200になります。

ビットマップの代わりにデフォルトの「ic_launcher」アイコンを使用すると、ローエンドの電話での遅延にすでに気付いているので、「効率的に行う方法」ほど問題ではありません。

アプリが起動してニュースを同期したり、キャッシュしたりするときに、ファイルとして、または他のニュースデータと一緒にニュースデータベースに保存する方が速いでしょうか...?

これについてはどうすればよいですか?

4

2 に答える 2

2

ImageManagerクラスを介してSoftReferenceを使用することをお勧めします。

ListAdpater getView()メソッドで、ImageManagerのdisplayImage()メソッドを呼び出します。

ImageManagerコーディング例:

public class ImageManagerExemple {

private static final String LOG_TAG = "ImageManager";

private static ImageManagerExemple instance = null;

public static ImageManagerExemple getInstance(Context context) {
    if (instance == null) {
        instance = new ImageManagerExemple(context);
    } 
    return instance;        
}   

private HashMap<String, SoftReference<Bitmap>> imageMap = new HashMap<String, SoftReference<Bitmap>>();

private Context context;
private File cacheDir;

private ImageManagerExemple(Context context) {
    this.context = context;
    // Find the dir to save cached images
    String sdState = android.os.Environment.getExternalStorageState();
    if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
        File sdDir = android.os.Environment.getExternalStorageDirectory();      
        cacheDir = new File(sdDir,"data/yourappname");
    } else {
        cacheDir = context.getCacheDir();
    }
    if(!cacheDir.exists()) {
        cacheDir.mkdirs();
    }
}


/**
 * Display web Image loading thread
 * @param imageUrl picture web url
 * @param imageView target
 * @param imageWaitRef picture during loading
 */
public void displayImage(String imageUrl, ImageView imageView, Integer imageWaitRef) {
    String imageKey = imageUrl;     
    imageView.setTag(imageKey);
    if(imageMap.containsKey(imageKey) && imageMap.get(imageKey).get() != null) {
        Bitmap bmp = imageMap.get(imageKey).get();
        imageView.setImageBitmap(bmp);
    } else {
        queueImage(imageUrl, imageView);
        if(imageWaitRef != null)
            imageView.setImageResource(imageWaitRef);
    }
}

private void queueImage(String url, ImageView imageView) {
    ImageRef imgRef=new ImageRef(url, imageView);
    // Start thread
    Thread imageLoaderThread = new Thread(new ImageQueueManager(imgRef));
    // Make background thread low priority, to avoid affecting UI performance
    imageLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
    imageLoaderThread.start();
}

private Bitmap getBitmap(String url) {
    String filename = String.valueOf(url.hashCode());
    File f = new File(cacheDir, filename);
    try {
        // Is the bitmap in our cache?
        Bitmap bitmap = BitmapFactory.decodeFile(f.getPath());
        if(bitmap != null) return bitmap;
        // Nope, have to download it
        bitmap = ImageServerUtils.pictureUrlToBitmap(url);
        // save bitmap to cache for later
        writeFile(bitmap, f);
        return bitmap;
    } catch (Exception ex) {
        ex.printStackTrace();
        Log.e(LOG_TAG, ""+ex.getLocalizedMessage());
        return null;
    }  catch (OutOfMemoryError e) {
        Log.e(LOG_TAG, "OutOfMemoryError : "+e.getLocalizedMessage());
        e.printStackTrace();
        return null;
    }
}

private void writeFile(Bitmap bmp, File f) {
    if (bmp != null && f != null) {
        FileOutputStream out = null;

        try {
            out = new FileOutputStream(f);
            //bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
            bmp.compress(Bitmap.CompressFormat.JPEG, 80, out);
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally { 
            try { if (out != null ) out.close(); }
            catch(Exception ex) {} 
        }
    }
}


private class ImageRef {
    public String imageUrl;
    public ImageView imageView;

    public ImageRef(String imageUrl, ImageView i) {
        this.imageUrl=imageUrl;
        this.imageView=i;
    }
}

private class ImageQueueManager implements Runnable {
    private ImageRef imageRef;
    public ImageQueueManager(ImageRef imageRef) {
        super();
        this.imageRef = imageRef;
    }
    @Override
    public void run() {
        ImageRef imageToLoad = this.imageRef;
        if (imageToLoad != null) {
            Bitmap bmp = getBitmap(imageToLoad.imageUrl);
            String imageKey = imageToLoad.imageUrl;
            imageMap.put(imageKey, new SoftReference<Bitmap>(bmp));
            Object tag = imageToLoad.imageView.getTag();

            // Make sure we have the right view - thread safety defender
            if (tag != null && ((String)tag).equals(imageKey)) {
                BitmapDisplayer bmpDisplayer = new BitmapDisplayer(bmp, imageToLoad.imageView);                         
                Activity a = (Activity)imageToLoad.imageView.getContext();                          
                a.runOnUiThread(bmpDisplayer);
            } 
        } 
    }
}

//Used to display bitmap in the UI thread
private class BitmapDisplayer implements Runnable {
    Bitmap bitmap;
    ImageView imageView;

    public BitmapDisplayer(Bitmap b, ImageView i) {
        bitmap=b;
        imageView=i;
    }
    @Override
    public void run() {
        if(bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } 
    }
}
于 2012-07-02T14:50:23.893 に答える
1

途切れることなくスムーズなListViewスクロールを実現する秘訣は、ユーザーがスクロールしている間、ListViewをいかなる方法、形状、形式でも更新しないことです。Afaik、これは基本的にiOSがListViewをスムーズに取得する方法です。ユーザーが指を置いている間は、iOS(および一般的なUI)への変更を許可しません。

すべてのビットマップ読み込みコードをそのままにして、ListViewを変更するコードをコメントアウトするだけで、バックグラウンドでのビットマップの実際の読み込みがパフォーマンスにまったく影響を与えないことがわかります。問題は、UIスレッドがビューの更新とスクロールを同時に実行できないことです。

OnScrollListenerユーザーがリストビューをスクロールしている間、リストビューへのすべての更新をブロックするを使用して、同じことを実現できます。ユーザーが停止するとすぐに、保留中のすべての更新を忍び込むことができます。パフォーマンスを向上させるnotifyDataSetChangedには、ListViewのビューを使用せずに繰り返し、実際に変更されたビューのみを更新するようにしてください。

于 2012-07-02T14:04:21.420 に答える