1

1つ編集:

ジョセフの答えに基づいて行われた変更:

bytesToDrawable(byte [] imageBytes):

以下を変更しました:BitmapDrawable(Bitmapビットマップ)の代わりにBitmapDrawable(Resources res、Bitmapビットマップ)を使用:

return new BitmapDrawable(ApplicationConstants.ref_currentActivity.getResources(),BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options));

これはその変更の結果です:わずかに異なる問題:

ここに画像の説明を入力してください

質問:

ビットマップドローアブルに新しいコンストラクターを使用していて、必要なターゲット密度に合わせて画像をスケーリングする場合でも、calculateSampleSizeメソッドを使用する必要がありますか?


元の質問:

よう皆、

私のアプリケーションはモジュールベースであるため、そのモジュールに固有の画像は、それらを含むjar(モジュール)からのみ読み込まれ、メインアプリケーションからは読み込まれません。

各モジュールには独自のModularImageLoaderがあります。これにより、基本的に、jarで見つかった画像の名前に基づいてDrawableをフェッチできます。

コンストラクターは、zipFile(モジュールA)とファイル名のリスト(zipから「.png」で終わるファイル)を受け取ります。

実施した調査:

私は以下を使用しました:ビットマップを効率的にロードするための開発者ページへのリンク

当初は密度ごとにサイズが設定された画像を作成していましたが、現在は96x96のサイズの画像アイコンが1セットだけあります。

画面密度がxhdpi未満の場合は、96x96画像の小さいサンプルサイズをロードします-36x36(ldpiの場合)、48x48(mdpiの場合)、72x72(hdpiの場合)。それ以外の場合は、96x96の画像を返します。(メソッドcalculateSampleSize()およびbytesToDrawable()を見てください)

コードを使用すると、概念を理解しやすくなると思います。ここにModularImageLoaderがあります。

コード:

public class ModularImageLoader
{
    public static HashMap<String, Drawable> moduleImages = new HashMap<String, Drawable>();
    public static int reqHeight = 0;
    public static int reqWidth = 0;
    public ModularImageLoader(ZipFile zip, ArrayList<String> fileNames)
    {
         float sdpi = ApplicationConstants.ref_currentActivity.getResources().getDisplayMetrics().density;
         if(sdpi == 0.75)
         {
            reqHeight = 36;
            reqWidth = 36;
         }
         else if(sdpi == 1.0)
         {
            reqHeight = 48;
            reqWidth = 48;
         }
         else if (sdpi == 1.5)
         {
            reqHeight = 72;
            reqWidth = 72;
         }
         else if (sdpi == 2.0)
         {
            reqHeight = 96;
            reqWidth = 96;
          }
          String names = "";
          for(String fileName : fileNames)
          {
            names += fileName + " ";
          }
          createByteArrayImages(zip, fileNames);
     }

public static Drawable findImageByName(String imageName)
{
    Drawable drawableToReturn = null;
    for (Entry<String, Drawable> ent : moduleImages.entrySet())
    {
        if(ent.getKey().equals(imageName))
        {
            drawableToReturn = ent.getValue();
        }
    }
    return drawableToReturn;
}
private static void createByteArrayImages(ZipFile zip, ArrayList<String> fileNames)
{
    InputStream in = null;
    byte [] temp = null;
    int nativeEndBufSize = 0;
    for(String fileName : fileNames)
    {
        try
        {
            in = zip.getInputStream(zip.getEntry(fileName));
            nativeEndBufSize = in.available();
            temp = toByteArray(in,nativeEndBufSize);

            // get rid of .png
            fileName = fileName.replace(".png", "");
            fileName = fileName.replace("Module Images/", "");
            moduleImages.put(fileName, bytesToDrawable(temp));
        }
        catch(Exception e)
        {
            System.out.println("getImageBytes() threw an exception: " + e.toString());
            e.printStackTrace();
        }
    }
    try
    {
        in.close();
    }
    catch (IOException e)
    {
        System.out.println("Unable to close inputStream!");
        e.toString();
        e.printStackTrace();
    }
}
public static byte[] toByteArray(InputStream is, int length) throws IOException 
{
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    int l;
    byte[] data = new byte[length];
    while ((l = is.read(data, 0, data.length)) != -1) 
    {
      buffer.write(data, 0, l);
    }
    buffer.flush();
    return buffer.toByteArray();
}
public static Drawable bytesToDrawable(byte[] imageBytes)
{
    try
    {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        int imageHeight = options.outHeight;
        int imageWidth = options.outWidth;

        BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options);
        String imageType = options.outMimeType;
        Log.d("ImageInfo : ", "Height:" + imageHeight +",Width:" +imageWidth + ",Type:" + imageType);

        options.inJustDecodeBounds = false;
        //Calculate sample size
        options.inSampleSize = calculateSampleSize(options);
        return new BitmapDrawable(BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options));
    }
    catch(Exception e)
    {
        Message.errorMessage("Module Loading Error", "The images in this module are too large to load onto cell memory. Please contact your administrator",
                "Source of error: ModularImageLoader - bytesToDrawable method", e.toString());
        return null;
    }
}
public static int calculateSampleSize(BitmapFactory.Options options)
{
    // raw height and width of the image itself
    int sampleSize = 1;
    int height = options.outHeight;
    int width = options.outWidth;
    if(height > reqHeight || width > reqWidth)
    {
        if(width > height)
        {
            sampleSize = Math.round((float)height / (float)reqHeight);
        }
        else
        {
            sampleSize = Math.round((float)width / (float)reqWidth);
        }
    }
    return sampleSize;
}
}

問題:

以下の画像は、実行中の4つのエミュレーターを示しています。これらは、それらの仕様と、EclipseAVDでの設定方法です。

LDPI:密度120、スキンQVGA MDPI:密度160、スキンHVGA HDPI:密度240、スキンWVGA800 XHDPI:密度320、スキン800x1280

問題を示す画像:

ここに画像の説明を入力してください

質問:

コードに基づいて-XHDPIウィンドウで、連絡先の画像が非常に小さいのはなぜですか?ニュース画像も96x96です(メインアプリケーションからロードされたものを除いて、res> XHDPIの下にあります)。問題は、MDPI画面とHDPI画面では正常に読み込まれることですが、それ以外の場合は奇妙です。何か案は?

4

4 に答える 4

6

BitmapFactory.Options で密度情報を指定すると、BitmapFactory は画像をスケーリングできます。これを行うと、ModularImageLoader のカスタム スケーリング コードを削除できるはずです。

inDensity と inTargetDensity を次のように指定します。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inDensity = DisplayMetrics.DENSITY_MEDIUM;
options.inTargetDensity = activityRef.getResources().getDisplayMetrics().densityDpi;
options.inScaled = true;
return BitmapFactory.decodeStream(openByteStream(), null, options);

一部のスケーリング オプションを無視する BitmapFactory.decodeByteArray に明らかにバグがあるため、バイト配列を ByteArrayInputStream でラップし、上記のように BitmapFactory.decodeStream を使用する必要がある場合があります ( http://code.google.com/p/androidを参照)。 /issues/detail?id=7538 )。

于 2012-11-21T23:54:07.210 に答える
4

BitmapDrawable(Resources res, Bitmap bitmap)ドローアブルのターゲット密度が適切に設定されるようにするコンストラクターを使用する必要があります。使用しているコンストラクターは非推奨です。

画面を見るとLDPI、連絡先の画像が実際には少し小さすぎ、HDPI画面では少し小さすぎます。画面上でのみMDPI完全に正しく見えます (デフォルトのターゲット密度が であるためMDPI)。

于 2012-11-20T22:59:37.573 に答える
2

を使用して目的の高さと幅を作成し、dpそれをに変換してpx、拡大縮小された画像に適したサイズを取得できます。

画像を32x32dpにしたいとします。

int reqWidth = dpToPx(context, 32);
int reqHeight = dpToPx(context, 32);

public static int dpToPx(Context context, int dp) {
    return (int) (dp * (context.getResources().getDisplayMetrics().densityDpi / 160f) + 0.5f);
}
于 2012-11-20T23:06:23.370 に答える
0

ldpi - 36x36 mdpi - 48x48 hdpi - 72x72 xhdpi - 96x96

ビットマップ ファクトリはサンプル サイズを整数で処理するため、これらがすべて互いの純粋な倍数であるとよいでしょう。したがって、サンプル サイズは整数である必要があります (完全に正確であるためには、末尾に小数はありません)。

ソリューション:

サンプリングを開始する前は、「すべての」画面タイプに対して 1 つの画像があり、その画像が押された状態の場合は、2 つの別々の画像がありました。

したがって、1 つの画像に対して、実際にはアプリケーションで 4 つ必要でした。その画像が押された状態である場合、1 つの画像には 8 つの画像が必要になります。

私の主な目的は、画像の数を減らすことでした。これにより、ビットマップ ヒープの割り当てが過負荷になり、メモリ不足の例外がスローされる可能性がなくなりました。ビットマップ サイズが完全に妥当な場合に、この例外がスローされるのを見てきました。 (ヒープ上の画像の数とそれぞれの画像サイズに関係があると思います(間違っている場合は修正してください..))そしてもちろん、モジュールのサイズを削減したかったのです。

だから私はこれに決めました:各画像に2つの画像を用意します.1つはサイズ72で、もう1つはサイズ96です.必要に応じて ldpi および mdpi。

72/2 = 36 96/2 = 48

このようにして、すべての画像に対して2つの画像しか持たず、最悪の場合、その画像に押された状態があった場合、4つの画像を持つことになります。これにより、イメージ サイズ バンクがほぼ 50% 削減され、モジュールが大幅に小さくなります。モジュール サイズが 525 kb から約 329 kb に変更されていることに気付きました。

これはまさに私が目指していたものでした。

みんな助けてくれてありがとう!ご不明な点がございましたら、お気軽にコメントを残してください。できるだけ早くご連絡いたします。

于 2012-11-26T14:39:19.607 に答える