Jak przekonwertować Drawable na bitmapę?


947

Chciałbym ustawić Drawablejako tapetę urządzenia, ale wszystkie funkcje tapet akceptują Bitmaptylko s. Nie mogę użyć, WallpaperManagerponieważ mam wersję 2.1.

Ponadto moje drawable są pobierane z sieci i nie są w nim przechowywane R.drawable.



1
proszę wybrać właściwą odpowiedź, to: stackoverflow.com/a/3035869/4548520
użytkownik25

Odpowiedzi:


1289

Ten fragment kodu pomaga.

Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
                                           R.drawable.icon_resource);

Tutaj wersja, w której obraz jest pobierany.

String name = c.getString(str_url);
URL url_value = new URL(name);
ImageView profile = (ImageView)v.findViewById(R.id.vdo_icon);
if (profile != null) {
    Bitmap mIcon1 =
        BitmapFactory.decodeStream(url_value.openConnection().getInputStream());
    profile.setImageBitmap(mIcon1);
}

1
myślę, że masz wartości URL. wtedy moja zredagowana odpowiedź powinna pomóc.
Praveen

skąd pochodzi str_url? Nie mogłem znaleźć żadnej rysowalnej funkcji związanej z łańcuchami ... dziękuję za twoją pomoc.
Rob

12
Wydaje mi się, że coś znalazłem: jeśli „draw” jest rysowalny, chcę przekonwertować go na bitmapę, to: Bitmap bitmap = ((BitmapDrawable) draw) .getBitmap (); Zrób sztuczkę!
Rob

1
@Rob: jeśli twój Drawable jest tylko BitmapDrawable. (co oznacza, że ​​Twój Drawable jest w rzeczywistości tylko
otuleniem mapy bitowej

2
Uwaga: powoduje to ogromny błąd java.lang.OutOfMemoryError z JPG
Someone Somewhere

743
public static Bitmap drawableToBitmap (Drawable drawable) {
    Bitmap bitmap = null;

    if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if(bitmapDrawable.getBitmap() != null) {
            return bitmapDrawable.getBitmap();
        }
    }

    if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
    } else {
        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;
}

41
Wygląda to na jedyną odpowiedź, która działałaby dla każdego rodzaju dającego się rysować, a także ma szybkie rozwiązanie dla dającego się rysować, który jest już BitmapDrawable. +1
Matt Wolfe

1
tylko jedna poprawka: docs mówi o BitmapDrawable.getBitmap (), że może wrócić do wartości zerowej. Mówię, że może również powrócić do recyklingu.
kellogs

16
Uważaj: getIntrinsicWidth()i getIntrinsicHieght()zwróci -1, jeśli ciągnienie ma jednolity kolor.
SD

5
Więc ... kolejny czek na ColorDrawable i mamy zwycięzcę. Poważnie, ktoś sprawia, że ​​jest to akceptowana odpowiedź.
kaay

2
W przeciwieństwie do oznaczonej odpowiedzi, odpowiada to na pytanie.
njzk2

214

Konwertuje to BitmapDrawable na Bitmap.

Drawable d = ImagesArrayList.get(0);  
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();

9
czy to naprawdę najlepszy sposób? Z pewnością dający się rysować może być innego typu, co spowodowałoby wyjątek runtimeException? Na przykład może to być ninePatchDrawble ...?
Dori

4
@Dori możesz owinąć kod w instrukcji warunkowej, aby sprawdzić, czy rzeczywiście jest to BitmapDrawableprzed przesłaniem: if (d instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable)d).getBitmap(); }
Tony Chan

367
Nie możesz uwierzyć w 64 głosów poparcia? Że kod działa oczywiście tylko wtedy, gdy djuż jestBitmapDrawable , w tym przypadku jest to trywialne, aby pobrać je jako bitmapy ... rozbije się ClassCastExceptionwe wszystkich innych przypadkach.
Matthias,

3
@ Matthias nie wspominając o tym ... samo pytanie, ten sam autor, ma 100 głosów: /
quinestor

2
Jest to tak wyspecjalizowane w trywialnym przypadku.
njzk2

141

A Drawablemoże zostać narysowane na a Canvas, a Canvasmoże być poparte przez Bitmap:

(Zaktualizowano w celu obsługi szybkiej konwersji dla BitmapDrawables i zapewnienia, że Bitmaputworzony ma prawidłowy rozmiar)

public static Bitmap drawableToBitmap (Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable)drawable).getBitmap();
    }

    int width = drawable.getIntrinsicWidth();
    width = width > 0 ? width : 1;
    int height = drawable.getIntrinsicHeight();
    height = height > 0 ? height : 1;

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

    return bitmap;
}

Co się stanie, jeśli ciąg do rysowania ma wartość zerową?
hrules6872

1
Ta metoda nie obsługuje VectorDrawable
Mahmoud


Zakładając, że otrzymujesz niepustą Drawable, dlaczego trzeba sprawdzić, czy szerokość i wysokość nie są równe 0? Ponadto, dlaczego musisz używać setBounds (), jeśli mają ten sam rozmiar?
Yoav Feuerstein

Dobre rozwiązanie! Android 8.0 / sdk 26 ApplicationInfo.loadIcon (PackageManager pm) zwraca AdaptiveIconDrawable。 Użyj kodu, aby mi pomóc w rzutowaniu AdaptiveIconDrawable na bitmapę。
burulangtu

43

METODA 1 : Możesz bezpośrednio przekonwertować na bitmapę w ten sposób

Bitmap myLogo = BitmapFactory.decodeResource(context.getResources(), R.drawable.my_drawable);

METODA 2 : Możesz nawet przekonwertować zasób na drawable i dzięki temu możesz uzyskać bitmapę w ten sposób

Bitmap myLogo = ((BitmapDrawable)getResources().getDrawable(R.drawable.logo)).getBitmap();

Dla API> 22 getDrawable metoda została przeniesiona do ResourcesCompatklasy, więc w tym celu wykonaj coś takiego

Bitmap myLogo = ((BitmapDrawable) ResourcesCompat.getDrawable(context.getResources(), R.drawable.logo, null)).getBitmap();

ResourcesCompat działa tylko wtedy, gdy drawable jest BitmapDrawable, jeśli używasz VectorDrawable, będziesz mieć CCE.
Brill Pappin

Żadne z nich nie działa z zasobem VectorDrawable . Występuje następujący błąd -android.graphics.drawable.VectorDrawable cannot be cast to android.graphics.drawable.BitmapDrawable
Adam Hurwitz

To rozwiązanie działa dobrze z Kotlin.
Adam Hurwitz


15

Więc po obejrzeniu (i korzystania) z innych odpowiedzi, wydaje się, że wszystko manipulacja ColorDrawablei PaintDrawableźle. (Zwłaszcza na Lollipop) wydawało się, że Shaderzostały poprawione, więc stałe bloki kolorów nie były obsługiwane poprawnie.

Używam teraz następującego kodu:

public static Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    // We ask for the bounds if they have been set as they would be most
    // correct, then we check we are  > 0
    final int width = !drawable.getBounds().isEmpty() ?
            drawable.getBounds().width() : drawable.getIntrinsicWidth();

    final int height = !drawable.getBounds().isEmpty() ?
            drawable.getBounds().height() : drawable.getIntrinsicHeight();

    // Now we check we are > 0
    final Bitmap bitmap = Bitmap.createBitmap(width <= 0 ? 1 : width, height <= 0 ? 1 : height,
            Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

W przeciwieństwie do innych, jeśli zadzwonisz setBoundsna Drawableprzed prosząc, aby włączyć go do bitmapy, to narysować bitmapę w odpowiednim rozmiarze!


Czy setBounds nie zrujnowałby poprzednich granic gry losowania? Czy nie lepiej jest go przechowywać i przywracać?
programista Androida

@androiddeveloper, jeśli granice zostały ustawione, po prostu i tak je wykorzystujemy. W niektórych przypadkach jest to potrzebne, gdy nie określono żadnych granic i nie ma on rzeczywistego rozmiaru (np. ColorDrawables w niektórych przypadkach). Tak więc szerokość i wysokość wynosi 0, dajemy ciągniętą 1x1 w taki sposób, że faktycznie coś rysuje. W takich przypadkach można argumentować, że możemy sprawdzić typ ColorDrawable, ale działa to w 99% przypadków. (Możesz go zmodyfikować według własnych potrzeb).
Chris.Jenkins

@ Chris.Jenkins Co jeśli nie ma granic, a teraz będą nowe? Chciałbym również zadać inne pytanie: jaki jest najlepszy sposób ustawienia rozmiaru bitmapy (nawet dla BitmapDrawable), która zostanie zwrócona?
programista Androida

Sugeruję uważne przeczytanie kodu. Jeśli Drawablenie ma ustawionych granic, używa IntrinsicWidth/Height. Jeśli oba mają wartość <= 0, ustawiamy płótno na 1px. Masz rację, jeśli Drawablenie ma granic, to minie trochę (1x1 jest w większości przypadków), ale WYMAGANE jest to dla rzeczy takich, ColorDrawablektóre NIE mają wewnętrznych rozmiarów. Gdybyśmy tego nie zrobili, wyrzuciłoby to Exception, nie można narysować 0x0 na kanwie.
Chris.Jenkins

1
mutate()tworzyłby kopię, pozostawiając oryginalny rysunkowy sam, co negowałoby problem powrotu do pierwotnych granic. Rzadko zmieniam kod na podstawie tych punktów. Jeśli wymaga tego przypadek użycia, dodaj inną odpowiedź. Sugeruję, aby utworzyć kolejne pytanie dotyczące skalowania mapy bitowej.
Chris.Jenkins

13

Może to pomoże komuś ...

Od PictureDrawable do Bitmap, użyj:

private Bitmap pictureDrawableToBitmap(PictureDrawable pictureDrawable){ 
    Bitmap bmp = Bitmap.createBitmap(pictureDrawable.getIntrinsicWidth(), pictureDrawable.getIntrinsicHeight(), Config.ARGB_8888); 
    Canvas canvas = new Canvas(bmp); 
    canvas.drawPicture(pictureDrawable.getPicture()); 
    return bmp; 
}

... wdrożony jako taki:

Bitmap bmp = pictureDrawableToBitmap((PictureDrawable) drawable);

Podobnie jak w przypadku odpowiedzi Roba, potrzebujesz określonego rodzaju Drawable, w tym przypadku a PictureDrawable.
kabuko

4
„Może to komuś pomoże ...”
Mauro

11

Oto lepsza rozdzielczość

public static Bitmap drawableToBitmap (Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable)drawable).getBitmap();
    }

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

    return bitmap;
}

public static InputStream bitmapToInputStream(Bitmap bitmap) {
    int size = bitmap.getHeight() * bitmap.getRowBytes();
    ByteBuffer buffer = ByteBuffer.allocate(size);
    bitmap.copyPixelsToBuffer(buffer);
    return new ByteArrayInputStream(buffer.array());
}

Kod z Jak czytać bity ciągnące jako InputStream


11

1) Drawable to Bitmap:

Bitmap mIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.icon);
// mImageView.setImageBitmap(mIcon);

2) bitmapa na Drawable:

Drawable mDrawable = new BitmapDrawable(getResources(), bitmap);
// mImageView.setDrawable(mDrawable);

10

Oto ładna wersja odpowiedzi Kotlin podana przez @ Chris.Jenkins tutaj: https://stackoverflow.com/a/27543712/1016462

fun Drawable.toBitmap(): Bitmap {
  if (this is BitmapDrawable) {
    return bitmap
  }

  val width = if (bounds.isEmpty) intrinsicWidth else bounds.width()
  val height = if (bounds.isEmpty) intrinsicHeight else bounds.height()

  return Bitmap.createBitmap(width.nonZero(), height.nonZero(), Bitmap.Config.ARGB_8888).also {
    val canvas = Canvas(it)
    setBounds(0, 0, canvas.width, canvas.height)
    draw(canvas)
  }
}

private fun Int.nonZero() = if (this <= 0) 1 else this

8

Android zapewnia non czysty roztwór przewin: BitmapDrawable. Aby uzyskać mapę bitową, będziemy musieli podać identyfikator zasobu R.drawable.flower_picna a, BitmapDrawablea następnie rzutować na a Bitmap.

Bitmap bm = ((BitmapDrawable) getResources().getDrawable(R.drawable.flower_pic)).getBitmap();


5

Użyj tego kodu. Pomoże ci to osiągnąć cel.

 Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.profileimage);
    if (bmp!=null) {
        Bitmap bitmap_round=getRoundedShape(bmp);
        if (bitmap_round!=null) {
            profileimage.setImageBitmap(bitmap_round);
        }
    }

  public Bitmap getRoundedShape(Bitmap scaleBitmapImage) {
    int targetWidth = 100;
    int targetHeight = 100;
    Bitmap targetBitmap = Bitmap.createBitmap(targetWidth, 
            targetHeight,Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(targetBitmap);
    Path path = new Path();
    path.addCircle(((float) targetWidth - 1) / 2,
            ((float) targetHeight - 1) / 2,
            (Math.min(((float) targetWidth), 
                    ((float) targetHeight)) / 2),
                    Path.Direction.CCW);

    canvas.clipPath(path);
    Bitmap sourceBitmap = scaleBitmapImage;
    canvas.drawBitmap(sourceBitmap, 
            new Rect(0, 0, sourceBitmap.getWidth(),
                    sourceBitmap.getHeight()), 
                    new Rect(0, 0, targetWidth, targetHeight), new Paint(Paint.FILTER_BITMAP_FLAG));
    return targetBitmap;
}

3

BitmapFactory.decodeResource()automatycznie skaluje bitmapę, więc mapa może być rozmyta. Aby zapobiec skalowaniu, wykonaj następujące czynności:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap source = BitmapFactory.decodeResource(context.getResources(),
                                             R.drawable.resource_name, options);

lub

InputStream is = context.getResources().openRawResource(R.drawable.resource_name)
bitmap = BitmapFactory.decodeStream(is);

2

jeśli używasz kotlin, użyj poniższego kodu. to zadziała

// do używania ścieżki obrazu

val image = Drawable.createFromPath(path)
val bitmap = (image as BitmapDrawable).bitmap


1
 // get image path from gallery
protected void onActivityResult(int requestCode, int resultcode, Intent intent) {
    super.onActivityResult(requestCode, resultcode, intent);

    if (requestCode == 1) {
        if (intent != null && resultcode == RESULT_OK) {             
            Uri selectedImage = intent.getData();

            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            filePath = cursor.getString(columnIndex);

            //display image using BitmapFactory

            cursor.close(); bmp = BitmapFactory.decodeFile(filepath); 
            iv.setBackgroundResource(0);
            iv.setImageBitmap(bmp);
        }
    }
}

Myślę, że źle odczytałeś pytanie. Pytanie brzmi: jak uzyskać mapę bitową z zasobu do rysowania, a nie galerii systemowej
kc ochibili

1

ImageWorker Library może konwertować bitmapę na drawable lub base64 i odwrotnie.

val bitmap: Bitmap? = ImageWorker.convert().drawableToBitmap(sourceDrawable)

Realizacja

W gradacji poziomu projektu

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

W gradacji poziomu aplikacji

dependencies {
            implementation 'com.github.1AboveAll:ImageWorker:0.51'
    }

Możesz także przechowywać i pobierać obrazy bitmapowe / rysunkowe / base64 z zewnętrznych.

Sprawdź tutaj. https://github.com/1AboveAll/ImageWorker/edit/master/README.md

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.