Отримання растрових зображень з векторного малювання


132

У своїй програмі я повинен встановити великий значок для сповіщення. LargeIcon повинен бути Bitmap, і мої малюнки - це векторні зображення (нова функція в Android, дивіться це посилання ) Проблема полягає в тому, що коли я намагаюся декодувати ресурс, який є векторним зображенням, я отримую нульове повернення.

Ось зразок коду:

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

У цьому зразку, коли я замінюю R.dravable.vector_menu_objectifs на "нормальне" зображення, png для зразка, результат не є нульовим (я отримую правильну растрову карту). Щось мені не вистачає?


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

ОНОВЛЕННЯ:

Gradle проекту:

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

Модуль gradle:

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

Дякую. Попередня відповідь не працює з версією sdk, починаючи з 23
Paha

2
Це чудово працює для мене. Працює в API 15 без проблем
віра

4
AppCompatDrawableManagerпозначений як @RestrictTo(LIBRARY_GROUP)внутрішній, і ви не повинні використовувати його (API може змінюватися без попереднього повідомлення). Використовуйте ContextCompatзамість цього.
mradzinski

не працює 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);
користувач25

5
У мене були проблеми з цим рішенням. Для мене за допомогою: AppCompatResources замість ContextCompat виправлено це: Dravable dravable = AppCompatResources.getDravable (контекст, dravableId);
Майк Т

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 повертається і позначає це як відповідь. Варто зазначити одне, що обидва методи хочуть обгортку targetAPi - але андроїд-студія вам це скаже.
roberto tomás

1
Я пробув це два дні, намагаючись зробити це зараз, думаючи про свою проблему з файлом 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);
користувач25

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"
}

Дивіться тут для останніх інструкцій для додавання залежності до проекту.

Зауважте, що це буде працювати для будь-якого підкласу, Drawableі якщо Drawableце a, BitmapDrawableце буде ярлик для використання базового Bitmap.


Це найбільш просте рішення тут для Котліна.
Адам Гурвіц

1
Для мене це прекрасно працює VectorDrawable.
ubuntudroid

Це найкращий варіант .. за замовчуванням androidx забезпечує функціональність
Reshma

27

Виходячи з попередніх відповідей, це може бути спрощено таким чином, щоб відповідати як VectorDravable і BitmapDravable, так і бути сумісним принаймні з 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 він буде використовувати VectorDravableCompat, а на Lollipop - VectorDravable.

EDIT

Я змінив умову після коментаря @ user3109468


1
мальований екземпляр VectorDravable || драйвовий екземпляр VectorDravableCompat повинен мати заміну сторін. Результати в класі не знайдено, коли VectorDravable не існує, коли VectorDravableCompat слід спочатку перевірити, оскільки він існує.
Воррік

Перевірку типу 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зображення intrinsicWidthі intrinsicHeightневелике, і ви намагаєтеся відобразити растрову карту на великий перегляд, тоді ви побачите результат розмиття.

У цьому випадку ви можете надати нову ширину / висоту для вашої растрової карти для отримання кращого зображення (або ви можете збільшити розмір вектора в 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.