W mojej aplikacji muszę ustawić dużą ikonę powiadomienia. LargeIcon musi być mapą bitową, a moje rysunki to obrazy wektorowe (nowa funkcja w Androidzie, zobacz ten link ) Problem polega na tym, że gdy próbuję zdekodować zasób, który jest obrazem wektorowym, zwracana jest wartość null.
Oto przykładowy kod:
if (BitmapFactory.decodeResource(arg0.getResources(), R.drawable.vector_menu_objectifs) == null)
Log.d("ISNULL", "NULL");
else
Log.d("ISNULL", "NOT NULL");
W tym przykładzie, kiedy zamieniam R.drawable.vector_menu_objectifs na "normalny" obraz, na przykład png, wynik nie jest zerowy (otrzymuję poprawną mapę bitową) Czy czegoś mi brakuje?
Odpowiedzi:
Sprawdzono na 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; }
AKTUALIZACJA:
Ocena projektu:
dependencies { classpath 'com.android.tools.build:gradle:2.2.0-alpha5' }
Klasa modułu:
android { compileSdkVersion 23 buildToolsVersion '23.0.3' defaultConfig { minSdkVersion 16 targetSdkVersion 23 vectorDrawables.useSupportLibrary = true } ... } ...
źródło
AppCompatDrawableManager
jest oznaczona jako@RestrictTo(LIBRARY_GROUP)
wewnętrzna i nie należy jej używać (jej API może ulec zmianie bez powiadomienia). UżyjContextCompat
zamiast tego.Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object reference
na tej liniiBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Możesz użyć następującej metody:
@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; }
które czasami łączę z:
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"); } }
źródło
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object reference
na tej liniiBitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Jeśli chcesz używać Androida KTX dla Kotlin, możesz użyć metody rozszerzenia,
Drawable#toBitmap()
aby osiągnąć ten sam efekt, co w przypadku innych odpowiedzi:val bitmap = AppCompatResources.getDrawable(requireContext(), drawableId).toBitmap()
lub
val bitmap = AppCompatResources.getDrawable(context, drawableId).toBitmap()
Aby dodać tę i inne przydatne metody rozszerzeń, musisz dodać następujące elementy do poziomu modułu
build.gradle
repositories { google() } dependencies { implementation "androidx.core:core-ktx:1.2.0" }
Zobacz tutaj najnowsze instrukcje dotyczące dodawania zależności do projektu.
Zauważ, że zadziała to dla każdej podklasy,
Drawable
a jeśliDrawable
jest aBitmapDrawable
, spowoduje to skrót do użycia bazowegoBitmap
.źródło
VectorDrawable
.Opierając się na poprzednich odpowiedziach, można to uprościć w ten sposób, aby dopasować zarówno VectorDrawable, jak i BitmapDrawable i być kompatybilnym z co najmniej 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"); } }
Następnie musisz dodać w swoim pliku gradle:
android { defaultConfig { vectorDrawables.useSupportLibrary = true } }
W wersji pre-Lollipop będzie używać VectorDrawableCompat, a na Lollipop - VectorDrawable.
EDYTOWAĆ
Zmodyfikowałem warunek po komentarzu @ user3109468
EDYCJA 2 (10/2020)
Przynajmniej od API 21 możesz teraz użyć tego zamiast powyższego kodu (nie próbowałem na poprzednich wersjach API):
AppCompatResources.getDrawable(context, R.drawable.your_drawable)
źródło
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
Uznanie dla @Alexey
Oto
Kotlin
wersja używająca rozszerzeń doContext
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 }
Przykładowe użycie w
Activity
:val bitmap = this.getBitmapFromVectorDrawable(R.drawable.ic_done_white_24dp)
źródło
Testowane na API 16 - JellyBean with 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; }
źródło
Użyj poniższego kodu, aby przekonwertować obraz z prawidłowym współczynnikiem proporcji (np. Dla ikony powiadomienia):
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; }
źródło
Jeśli twój
vector
obrazintrinsicWidth
iintrinsicHeight
jest mały i spróbujesz wyświetlić bitmapę w dużym widoku, zobaczysz, że wynikiem jest rozmycie.W takim przypadku możesz podać nową szerokość / wysokość dla mapy bitowej, aby uzyskać lepszy obraz (lub możesz zwiększyć rozmiar wektora w xml, ale podaj
desireWidth
idesireHeight
może być bardziej elastyczny).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 }
Mam nadzieję, że to pomoże
źródło
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));
źródło
Jeśli chcesz mieć możliwość skalowania wydruku do pożądanego rozmiaru, wypróbuj następujący fragment:
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)
źródło
W ten sposób uzyskasz mapę bitową w żądanym rozmiarze. Ponadto pozwala zachować lub nie przezroczystość w zależności od każdego obrazu, aby uzyskać lepszą wydajność z tymi, które jej nie potrzebują.
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; }
źródło
W przypadku wektora, który można narysować tutaj, podany kubek kodu pomaga nam, ale pamiętaj, że może być pusty, jeśli nie zostanie znaleziony drawable o wartości NULL
@Nullable public static Bitmap drawableToBitmap(Context context, int drawableId) { Drawable drawable = ContextCompat.getDrawable(context, drawableId); if (drawable != null) { int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bmp); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bmp; } return null; }
źródło