Mendapatkan Bitmap dari gambar vektor

133

Dalam aplikasi saya, saya harus mengatur ikon besar untuk pemberitahuan. LargeIcon harus berupa Bitmap, dan gambar saya yang bisa digambar adalah gambar vektor (fitur baru di Android, lihat tautan ini ) Masalahnya adalah ketika saya mencoba mendekodekan sumber daya yang merupakan gambar vektor, saya mendapatkan pengembalian nol.

Berikut ini contoh kode:

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

Dalam sampel ini, ketika saya mengganti R.drawable.vector_menu_objectifs dengan gambar "normal", png untuk contoh, hasilnya bukan nol (saya mendapatkan bitmap yang benar) Apakah ada sesuatu yang saya lewatkan?

liltof
sumber
1
Punya masalah serupa, bukan solusi tetapi solusi: stackoverflow.com/questions/33548447/…
Dari

Jawaban:

232

Diperiksa di 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;
}

MEMPERBARUI:

Proyek gradle:

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

Modul gradle:

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.3'
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
        vectorDrawables.useSupportLibrary = true
    }
    ...
}
...
Alexey
sumber
Terima kasih. Jawaban sebelumnya tidak berfungsi dengan versi SDK mulai dari 23
Paha
2
Ini sangat bagus untuk saya. Berjalan di API 15 tanpa masalah
vera
4
AppCompatDrawableManagerditandai sebagai @RestrictTo(LIBRARY_GROUP)internal dan Anda tidak boleh menggunakannya (API itu dapat berubah tanpa pemberitahuan). Gunakan ContextCompatsebagai gantinya.
mradzinski
tidak berfungsi Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referencepada baris iniBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25
5
Saya punya masalah dengan solusi ini. Bagi saya menggunakan: AppCompatResources, bukan ContextCompat memperbaikinya: Drawable drawable = AppCompatResources.getDrawable (konteks, drawableId);
Mike T
64

Anda dapat menggunakan metode berikut:

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

yang terkadang saya gabungkan dengan:

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");
    }
}
snodnipper
sumber
2
semoga @liltof kembali dan menandai ini sebagai jawaban. Satu hal yang perlu diperhatikan adalah kedua metode menginginkan pembungkus targetAPi - tetapi studio android akan memberi tahu Anda hal itu.
roberto tomás
1
Saya telah menghabiskan sekitar dua hari mencoba melakukan ini sekarang memikirkan masalah file svg-nya. Terima kasih!
sparkly_frog
1
tidak berfungsi Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referencepada baris iniBitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25
45

Jika Anda bersedia menggunakan KTX Android untuk Kotlin, Anda dapat menggunakan metode ekstensi Drawable#toBitmap()untuk mencapai efek yang sama dengan jawaban lainnya:

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

atau

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

Untuk menambahkan ini dan metode ekstensi lain yang berguna, Anda perlu menambahkan yang berikut ke tingkat modul Anda build.gradle

repositories {
    google()
}

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

Lihat di sini untuk instruksi terbaru untuk menambahkan ketergantungan pada proyek Anda.

Perhatikan bahwa ini akan bekerja untuk setiap subkelas Drawabledan jika Drawableada, BitmapDrawablemaka akan digunakan pintasan untuk menggunakan yang mendasarinya Bitmap.

David Rawson
sumber
Ini adalah solusi paling sederhana di sini untuk Kotlin.
Adam Hurwitz
1
Bagi saya itu berfungsi dengan baik VectorDrawable.
ubuntudroid
Ini adalah pilihan terbaik .. default androidx menyediakan fungsionalitas
Reshma
27

Berdasarkan jawaban sebelumnya dapat disederhanakan seperti itu untuk mencocokkan VectorDrawable dan BitmapDrawable dan agar kompatibel dengan setidaknya 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");
    }
}

Maka Anda harus menambahkan file gradle Anda:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

Pada pra-Lollipop akan menggunakan VectorDrawableCompat dan pada Lollipop akan menggunakan VectorDrawable.

EDIT

Saya telah mengedit kondisi setelah komentar dari @ user3109468

Eselfar
sumber
1
instance yang dapat ditarik dari VectorDrawable || drawable instanceof VectorDrawableCompat harus memiliki sisi bertukar. Hasil dalam Kelas Tidak Ditemukan ketika VectorDrawable tidak ada, ketika VectorDrawableCompat harus diperiksa terlebih dahulu karena memang ada.
Warrick
pengecekan tipe VertorDrawable dapat digambarkan sebagai(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
chmin
6

Kudos to @Alexey

Ini adalah Kotlinversi yang menggunakan ekstensi untukContext

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
}

Contoh penggunaan di Activity:

val bitmap = this.getBitmapFromVectorDrawable(R.drawable.ic_done_white_24dp)
Gunhan
sumber
1

Diuji pada API 16 - JellyBean dengan Drawables Vector

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;
}   
tomasmark79
sumber
1

Gunakan kode berikut untuk mengonversi gambar dengan rasio aspek yang benar (misalnya, untuk ikon pemberitahuan):

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;
}
antaki93
sumber
0

Jika Anda vectorgambar intrinsicWidthdan intrinsicHeightkecil dan Anda mencoba untuk menampilkan bitmap untuk tampilan besar, maka Anda akan melihat hasilnya blur.

Dalam hal ini, Anda dapat memberikan lebar / tinggi baru untuk bitmap Anda untuk mendapatkan gambar yang lebih baik (atau Anda dapat meningkatkan ukuran vektor dalam xml, tetapi berikan desireWidthdan desireHeightmungkin lebih fleksibel).

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
}

Semoga ini bisa membantu

Phan Van Linh
sumber
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));
Amine
sumber
0

Jika Anda ingin dapat menskalakan output Anda ke ukuran output yang diinginkan, coba cuplikan berikut:

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)
Hans
sumber
0

Ini memberi Anda bitmap dalam ukuran yang Anda inginkan. Selain itu, ini memungkinkan Anda untuk mempertahankan atau tidak transparansi tergantung pada setiap gambar untuk kinerja yang lebih baik dengan yang tidak membutuhkannya.

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;
}
Estid Felipe Lozano Reyes
sumber