ベクタードローアブルからビットマップを取得する


132

私のアプリケーションでは、通知に大きなアイコンを設定する必要があります。LargeIconはビットマップである必要があり、ドローアブルはベクター画像です(Androidの新機能、このリンクを参照)。ベクター画像であるリソースをデコードしようとすると、nullが返されます。

これがコードのサンプルです:

if (BitmapFactory.decodeResource(arg0.getResources(), R.drawable.vector_menu_objectifs) == null)
        Log.d("ISNULL", "NULL");
    else
        Log.d("ISNULL", "NOT NULL");

このサンプルでは、​​R.drawable.vector_menu_objectifsを「通常の」画像、たとえばpngに置き換えた場合、結果はnullではありません(正しいビットマップを取得します)何か不足していますか?


1
持っていた似問題ではなく、解決策はなく、回避策:stackoverflow.com/questions/33548447/...
より

回答:


231

APIで確認済み:17、21、23

public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = (DrawableCompat.wrap(drawable)).mutate();
    }

    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
            drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

更新:

プロジェクトグラドル:

dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0-alpha5'
    }

モジュールグラドル:

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.3'
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
        vectorDrawables.useSupportLibrary = true
    }
    ...
}
...

ありがとうございました。以前の回答が23から始まるSDKバージョンで機能しない
Paha

2
これは私にとってはうまくいきます。問題なくAPI 15で実行
vera

4
AppCompatDrawableManager@RestrictTo(LIBRARY_GROUP)内部であり、使用しないでください(APIは予告なく変更される可能性があります)。使用するContextCompat代わりに。
mradzinski 2017

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referenceこの行では機能しませんBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25 '30

5
このソリューションに問題がありました。私が使用する場合:ContextCompatの代わりにAppCompatResourcesを修正しました:Drawable drawable = AppCompatResources.getDrawable(context、drawableId);
Mike T

64

次の方法を使用できます。

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static Bitmap getBitmap(VectorDrawable vectorDrawable) {
    Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
            vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    vectorDrawable.draw(canvas);
    return bitmap;
}

私は時々それと組み合わせる:

private static Bitmap getBitmap(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof VectorDrawable) {
        return getBitmap((VectorDrawable) drawable);
    } else {
        throw new IllegalArgumentException("unsupported drawable type");
    }
}

2
うまくいけば、@ liltofが戻ってきて、これを回答としてマークします。注意すべきことの1つは、どちらのメソッドもtargetAPiラッパーを必要とすることですが、android studioはそれを通知します。
robertotomás2016年

1
私はこれを行うために約2日間を費やして、svgファイルの問題を考えています。ありがとうございました!
sparkly_frog

1
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referenceこの行では機能しませんBitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

45

あなたが使用して喜んでいる場合はAndroidのKTXを Kotlinのためにあなたは、拡張メソッドを使用することができますDrawable#toBitmap()他の回答と同じ効果を達成するために:

val bitmap = AppCompatResources.getDrawable(requireContext(), drawableId).toBitmap() 

または

val bitmap = AppCompatResources.getDrawable(context, drawableId).toBitmap() 

これと他の便利な拡張メソッドを追加するには、モジュールレベルに以下を追加する必要があります build.gradle

repositories {
    google()
}

dependencies {
    implementation "androidx.core:core-ktx:1.2.0"
}

プロジェクトに依存関係を追加する最新の手順については、こちらをご覧ください。

これはのすべてのサブクラスで機能しDrawableDrawableaのBitmapDrawable場合は、基になるを使用するショートカットになりますBitmap


これはKotlinにとって最もシンプルなソリューションです。
Adam Hurwitz

1
私にとっては、で完全に問題なく動作しVectorDrawableます。
ubuntudroid

これが最良のオプションです。デフォルトのandroidxが機能を提供します
Reshma

27

以前の回答に基づいて、VectorDrawableとBitmapDrawableの両方を一致させ、少なくともAPI 15と互換性を持たせるように簡略化できます。

public static Bitmap getBitmapFromDrawable(Context context, @DrawableRes int drawableId) {
    Drawable drawable = AppCompatResources.getDrawable(context, drawableId);

    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof VectorDrawableCompat || drawable instanceof VectorDrawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    } else {
        throw new IllegalArgumentException("unsupported drawable type");
    }
}

次に、gradleファイルに追加する必要があります。

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

Lollipop以前はVectorDrawableCompatを使用し、LollipopではVectorDrawableを使用します。

編集

@ user3109468のコメントに従って条件を編集しました


1
VectorDrawableの描画可能なインスタンス|| VectorDrawableCompatのドローアブルインスタンスは、サイドを入れ替える必要があります。VectorDrawableCompatが存在するため、最初にVectorDrawableCompatをチェックする必要がある場合、VectorDrawableが存在しないときにクラスが見つかりません。
Warrick

VertorDrawableの型チェックは次のように記述できます(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
chmin

6

@Alexeyへの称賛

これはKotlin拡張機能を使用したバージョンですContext

fun Context.getBitmapFromVectorDrawable(drawableId: Int): Bitmap? {
    var drawable = ContextCompat.getDrawable(this, drawableId) ?: return null

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = DrawableCompat.wrap(drawable).mutate()
    }

    val bitmap = Bitmap.createBitmap(
            drawable.intrinsicWidth,
            drawable.intrinsicHeight,
            Bitmap.Config.ARGB_8888) ?: return null
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)

    return bitmap
}

での使用例Activity

val bitmap = this.getBitmapFromVectorDrawable(R.drawable.ic_done_white_24dp)

1

API 16でテスト-ベクター型ドローアブルを備えたJellyBean

public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
    Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = (DrawableCompat.wrap(drawable)).mutate();
    }

    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
            drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}   

1

次のコードを使用して、正しいアスペクト比で画像を変換します(たとえば、通知アイコン用)。

public static Bitmap getBitmapFromVector(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    int width = drawable.getIntrinsicWidth();
    int height = drawable.getIntrinsicHeight();
    Bitmap bitmap;
    if (width < height) {    //make a square
        bitmap = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888);
    } else {
        bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0,
            drawable.getIntrinsicWidth(),    //use dimensions of Drawable
            drawable.getIntrinsicHeight()
    );
    drawable.draw(canvas);
    return bitmap;
}

0

あなたの場合はvector、画像intrinsicWidthintrinsicHeight小さく、あなたは大きなビューにビットマップを表示しようと、あなたは結果がぼやけているが表示されます。

その場合は、ビットマップに新しい幅/高さを指定して、より良い画像を取得できます(または、xmlでベクターサイズを大きくできますが、desireWidthを指定desireHeightすると、より柔軟になる場合があります)。

private fun getBitmap(drawableId: Int, desireWidth: Int? = null, desireHeight: Int? = null): Bitmap? {
    val drawable = AppCompatResources.getDrawable(context, drawableId) ?: return null
    val bitmap = Bitmap.createBitmap(
        desireWidth ?: drawable.intrinsicWidth,
        desireHeight ?: drawable.intrinsicHeight,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)
    return bitmap
}

それが役に立てば幸い


0
Drawable layerDrawable = (Drawable) imageBase.getDrawable();
Bitmap bitmap = Bitmap.createBitmap(layerDrawable.getIntrinsicWidth(),
        layerDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);  
imageTeste.setImageBitmap(addGradient(bitmap));

0

出力を希望の出力サイズにスケーリングできるようにするには、次のスニペットを試してください。

fun getBitmapFromVectorDrawable(context: Context, drawableId: Int, outputSize: OutputSize? = null): Bitmap? {
    var drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = DrawableCompat.wrap(drawable).mutate()
    }

    var targetBitmap: Bitmap
    if (outputSize != null) {
        targetBitmap = Bitmap.createBitmap(outputSize.width,
                outputSize.height, Bitmap.Config.ARGB_8888)
    } else {
        targetBitmap = Bitmap.createBitmap(drawable.intrinsicWidth,
            drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    }

    val canvas = Canvas(targetBitmap)
    val scaleX =  targetBitmap.width.toFloat()/drawable.intrinsicWidth.toFloat()
    val scaleY =  targetBitmap.height.toFloat()/drawable.intrinsicHeight.toFloat()
    canvas.scale(scaleX, scaleY)
    drawable.draw(canvas)

    return targetBitmap
}

class OutputSize(val width: Int, val height: Int)

0

これにより、ビットマップが必要なサイズになります。また、各画像に応じて透明度を維持するかしないかを設定できるため、画像を必要としない場合のパフォーマンスが向上します。

public static Bitmap drawableToBitmap(Resources res, int drawableId,
        int width, int height, boolean keepAlpha) {
    Drawable drawable = res.getDrawable(drawableId);
    Bitmap bmp = createBitmap(width, height, keepAlpha ?
            Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
    Canvas cvs = new Canvas(bmp);
    drawable.setBounds(0, 0, width, height);
    drawable.draw(cvs);
    return bmp;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.