0

これが高速に行われ、大量の RAM を使用しない方法で、drawableフォルダから に画像をロードするにはどうすればよいですか?ListView

4

2 に答える 2

15

注: この投稿は非推奨です。リストの作成にはRecyclerViewを使用してください


drawableフォルダに保存されているいくつかの(かなり大きな)画像をロードして遊んでいましたListView。この投稿では、私が得た結果を共有したいと思います. たぶん(そうであることを願っています)これで誰かの時間を大幅に節約できます。いくつかの Android 4 以降のデバイスで投稿しているコードをテストしましたが、非常にスムーズに動作し、使用される RAM の量は比較的少ないと言えます。いくつかの説明は次のとおりです。

  • 私たちはBaseAdapter
  • 画像は、AsyncTask
  • この種のアダプタでは一般的であるため、ArrayList<>パラメータ化されObjectsたカスタム クラスを使用します。私のアプリでは、このクラスはWeaponと呼ばれます
  • 画面サイズに応じて画像をスケーリングします
  • TextView各リスト行にフォントを適用します

このコードを任意の目的に自由に使用し、任意の方法で変更してください。私が求めている唯一のことは、何かが機能しないと主張する前に、コードを適切にテストすることです。それはうまくいきます、信じてください。

コピー、ペースト、編集の間違いに気付いた場合 (この小さなチュートリアルには関係のないコードをいくつか削除したため)、フィードバックをお待ちしています。

getView()コードを投稿する前に、メソッドのロジックを示す小さな状態図を次に示します。

ここに画像の説明を入力

クラスのコードAdapterは以下のとおりです。コメントで必要なすべてを説明しようとしました。

public class WeaponAdapter extends BaseAdapter implements View.OnClickListener {

private ArrayList<Weapon> items;
private LayoutInflater inflater = null;
private WeaponHolder weaponHolder;
private Weapon wp;
private Context c;
private Bitmap bmp;

/*--- a simple View Holder class ---*/
static class WeaponHolder {
    public TextView text;
    public ImageView image, addFav;
    public AsyncImageSetter mImageLoader;

}

/*--- Context and all weapons of specified class are passed here ---*/

public WeaponAdapter(ArrayList<Weapon> items, Context c) {
    this.items = (ArrayList<Weapon>) items;
    inflater = LayoutInflater.from(c);
    this.c = c;
}

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

}

@Override
public Weapon getItem(int position) {
    return items.get(position);
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    /*--- initialize our Weapon Object ---*/
    wp = items.get(position);

    if (convertView == null) {
        /*--- no View is available. Inflate our list item layout and init the Views we need ---*/
        convertView = inflater.inflate(R.layout.category_row, null);
        weaponHolder = new WeaponHolder();
        weaponHolder.text = (TextView) convertView
                .findViewById(R.id.tvCatText);
        weaponHolder.image = (ImageView) convertView
                .findViewById(R.id.imgCatImage);
        weaponHolder.addFav = (ImageView) convertView
                .findViewById(R.id.imgAddFav);
        convertView.setTag(weaponHolder);
    } else {
        weaponHolder = (WeaponHolder) convertView.getTag();
        /*--- if convertView is not null, cancel the current loading operation to 
         * improve performance and decrease RAM usage ---*/
        weaponHolder.mImageLoader.cancel();
    }
    /*--- load the image in background ---*/
    weaponHolder.mImageLoader = new AsyncImageSetter(c, weaponHolder.image,
            wp.getImage(), bmp, weaponHolder.text);
    weaponHolder.mImageLoader.execute();
    weaponHolder.text.setText(wp.getName());
    weaponHolder.addFav.setOnClickListener(this);
    return convertView;

}

@Override
public void onClick(View v) {


        // do any stuff here



}
   }

AsyncTaskバックグラウンドで画像をロードして設定するのは次のとおりです。

注:私の Weapon クラスには、対応する WeapongetImage()の resId を返すメソッドがあります。この部分は、自分に合った方法で変更できます。drawableObject

public class AsyncImageSetter extends AsyncTask<Void, Void, Bitmap> {

private ImageView img;
private int image_resId;
private Bitmap bmp;
private Context c;
private boolean cancel = false;
private int sampleSize;
private TextView txtGunName;
private Typeface font;


public AsyncImageSetter(Context c, ImageView img, int image_ResId,
        Bitmap bmp, TextView txtGunName) {

    this.img = img;
    this.image_resId = image_ResId;
    this.bmp = bmp;
    this.c = c;
    this.txtGunName = txtGunName;

}

public void cancel() {
    cancel = true;
}

@Override
protected void onPreExecute() {
    /*--- we hide the Views from the user until the content is ready. This will prevent
     * the user from seeing an image being "transformed" into the next one (as a result of
     * View recycling) on slow devices. 
     */
    img.setVisibility(View.GONE);
    txtGunName.setVisibility(View.GONE);
    font = Typeface.createFromAsset(c.getAssets(), "b_reg.otf");
    super.onPreExecute();
}

@Override
protected Bitmap doInBackground(Void... params) {

    if (!cancel) {
        try {
            return decodeAndScale(bmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return null;
}

@Override
protected void onPostExecute(Bitmap result) {

    img.setVisibility(View.VISIBLE);
    try {
        img.setImageBitmap(result);
    } catch (Exception e) {
        /*--- show an error icon in case something went wrong ---*/
        img.setImageResource(R.drawable.ic_warn);
    }

    txtGunName.setVisibility(View.VISIBLE);
    txtGunName.setTypeface(font);
    super.onPostExecute(result);
}

private Bitmap decodeAndScale(Bitmap bmp) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = setSampleSize();

    return BitmapFactory.decodeResource(c.getResources(), image_resId,
            options);

}

private int setSampleSize() {

    // TODO add multiple screens check
/*--- modify this method to match your needs ---*/
    if (GetSettings.getScreenWidth((Activity) c) >= 320) {
        /*--- physical width >= 480px ---*/
        sampleSize = 2;
    }

    return sampleSize;
}}

クラスのgetScreenWidth()メソッドを使用していることに気付いたかもしれません。GetSettingsそのコードは非常に単純で、デバイスの画面幅を表す dp 値を返します。

    public static int getScreenWidth(Activity a) {

    Display display = a.getWindowManager().getDefaultDisplay();
    DisplayMetrics outMetrics = new DisplayMetrics();
    display.getMetrics(outMetrics);

    float density = a.getResources().getDisplayMetrics().density;
    float dpWidth = outMetrics.widthPixels / density;

    return (int) dpWidth;

}

以上で、この投稿が誰かの役に立てば幸いです。乾杯。

PS何かがうまくいかないことが確実な場合は、私が使用しているものとは異なる内部アプリ構造が原因である可能性が最も高い. この場合、次の手順を実行することをお勧めします。

  1. 新しい質問をして、適切にフォーマットされたコードと LogCat 出力を追加できるようにします
  2. 投稿にコメントを追加して通知してください。何が問題なのか教えていただけると幸いです
于 2013-10-19T17:45:07.910 に答える
1

それに応じて画像を設定するには、リストビューのスクロール状態を使用します。

  1. リストビューがフリングされているときは、同時に画像を正しく設定しないでください。状態がアイドル状態になったら、コードを使用して画像を設定してください。このようにして、表示されていないビューのメモリ割り当てを回避します。リスト ビューがスクロールされているときに画像を設定しようとすると、メモリ不足エラーが発生する可能性があります。
  2. また、ドローアブルがビューに収まる適切なサイズであることを確認してください。ドローアブルからの画像の取得は常に高速であり、ここでの主な懸念はメモリ使用量です。
  3. また、リスト アイテムとして設定するドローアブルの配列を作成します。そのため、アダプタを設定している間に、リスト アイテムに使用するドローアブルがすでにいっぱいになり、条件に基づいてドローアブルをリスト ビューに取り込まないでください。
于 2013-10-19T18:06:57.803 に答える