รับบิตแมปจากเวกเตอร์ drawable


132

ในแอปพลิเคชันของฉันฉันต้องตั้งไอคอนขนาดใหญ่สำหรับการแจ้งเตือน LargeIcon ต้องเป็นบิตแมปและ drawables ของฉันคือภาพเวกเตอร์ (ฟีเจอร์ใหม่ใน 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 สำหรับตัวอย่างผลลัพธ์ไม่เป็นโมฆะ (ฉันได้รับบิตแมปที่ถูกต้อง) มีบางสิ่งที่ฉันขาดไปหรือไม่?


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

UPDATE:

ระดับโครงการ:

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 version เริ่มตั้งแต่ 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 แก้ไขได้: Drawable drawable = AppCompatResources.getDrawable (บริบท, 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 จะกลับมาและทำเครื่องหมายว่านี่เป็นคำตอบ สิ่งหนึ่งที่ควรทราบก็คือทั้งสองวิธีต้องการ targetAPi wrapper - แต่ android studio จะบอกคุณว่า
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เป็นก็จะลัดการใช้งานพื้นฐานBitmapDrawableBitmap


นี่เป็นทางออกที่ง่ายที่สุดสำหรับ 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
    }
}

ใน pre-Lollipop มันจะใช้ VectorDrawableCompat และบน Lollipop ก็จะใช้ VectorDrawable

แก้ไข

ฉันแก้ไขเงื่อนไขตามความคิดเห็นของ @ user3109468


1
อินสแตนซ์ของ drawable VectorDrawable || อินสแตนซ์ drawable ของ VectorDrawableCompat ควรมีการสลับข้าง ผลลัพธ์ใน Class ไม่พบเมื่อ VectorDrawable ไม่มีอยู่เมื่อ VectorDrawableCompat ควรถูกตรวจสอบก่อนเพราะมันมีอยู่
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 ด้วย Vector Drawables

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.