【问题标题】:Getting Bitmap from vector drawable从矢量图中获取位图
【发布时间】:2023-03-09 01:05:01
【问题描述】:

在我的应用程序中,我必须为通知设置一个大图标。 LargeIcon 必须是 Bitmap,而我的 drawables 是矢量图(Android 中的新功能,请参阅this link) 问题是,当我尝试解码矢量图像资源时,返回 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,结果不为空(我得到正确的位图) 我有什么遗漏吗?

【问题讨论】:

标签: android bitmap android-drawable android-vectordrawable


【解决方案1】:

已检查 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;
}

更新:

项目等级:

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

模块分级:

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

【讨论】:

  • AppCompatDrawableManager 被标记为 @RestrictTo(LIBRARY_GROUP) 所以它是内部的,你不应该使用它(它的 API 可以更改,恕不另行通知)。请改用ContextCompat
  • 在这条线上不起作用Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referenceBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
  • @mradzinski 很好,它正在工作。我已经编辑了答案以更好地反映最佳解决方案。
  • 我对此解决方案有疑问。对于我使用:AppCompatResources 而不是 ContextCompat 修复它: Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
  • 这对我很有用。在 API 28 上运行没有问题
【解决方案2】:

如果您愿意将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"
}

请参阅here 了解将依赖项添加到项目的最新说明。

请注意,这适用于Drawable任何 子类,如果DrawableBitmapDrawable,它将使用底层Bitmap 的快捷方式。

【讨论】:

  • 这是 Kotlin 最简单的解决方案。
  • 对我来说,VectorDrawable 工作得很好。
  • 这是最好的选择.. 默认 androidx 提供功能
【解决方案3】:

您可以使用以下方法:

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

【讨论】:

  • 希望@liltof 回来并将其标记为答案。需要注意的一件事是,这两种方法都需要 targetAPi 包装器——但 android studio 会告诉你。
  • 我花了大约两天时间尝试这样做,现在正在考虑它的 svg 文件问题。谢谢!
  • 在这条线上不起作用Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referenceBitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
【解决方案4】:

根据之前的答案,可以这样简化,以匹配 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
    }
}

在 Lollipop 之前,它将使用 VectorDrawableCompat,而在 Lollipop 上,它将使用 VectorDrawable。

编辑

我根据@user3109468 的评论编辑了条件

编辑 2 (10/2020)

至少从 API 21 开始,您现在可以使用它来代替上面的代码(我没有尝试过以前的 API 版本):

AppCompatResources.getDrawable(context, R.drawable.your_drawable)

【讨论】:

  • VectorDrawable 的可绘制实例 ||可绘制 instanceof VectorDrawableCompat 应该交换边。当 VectorDrawable 不存在时导致 Class Not Found,此时应首先检查 VectorDrawableCompat,因为它确实存在。
  • VertorDrawable的类型检查可以描述为(Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP &amp;&amp; drawable instanceof VectorDrawable)
【解决方案5】:

向@Alexey 致敬

这是使用Context 扩展的Kotlin 版本

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)

【讨论】:

    【解决方案6】:

    在 API 16 上测试 - 带有 Vector Drawable 的 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;
    }   
    

    【讨论】:

      【解决方案7】:

      对于vector drawable这里给出cup代码帮助我们,但是记住如果drawable没有找到NULL它可能是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;
      }
      

      【讨论】:

        【解决方案8】:

        使用以下代码将图像转换为正确的纵横比(例如,通知图标):

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

        【讨论】:

          【解决方案9】:

          如果您的vector 图像intrinsicWidthintrinsicHeight 很小,并且您尝试将位图显示为大视图,那么您会看到结果是模糊的。

          在这种情况下,您可以为位图提供新的宽度/高度以获得更好的图像(或者您可以增加 xml 中的矢量大小,但提供 desireWidthdesireHeight 可能更灵活)。

          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
          }
          

          希望对你有帮助

          【讨论】:

            【解决方案10】:
            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));
            

            【讨论】:

              【解决方案11】:

              如果您希望能够将输出缩放到所需的输出大小,请尝试以下 sn-p:

              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)
              

              【讨论】:

                【解决方案12】:

                这将为您提供所需大小的位图。此外,它还允许您根据每个图像保持或不保持透明度,以便在不需要它的图像中获得更好的性能。

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

                【讨论】:

                  【解决方案13】:

                  创建矢量到位图的独立乐趣

                   //vectorToBitmapMarker
                      private fun fromVectorToBitmap(id: Int, color: Int): BitmapDescriptor
                      {
                          val vectorDrawable: Drawable? = ResourcesCompat.getDrawable(resources, id, null)
                  
                          if (vectorDrawable == null)
                          {
                              d("VTOB","Resource not found!")
                              return BitmapDescriptorFactory.defaultMarker()
                          }
                  
                          val bitmap = Bitmap.createBitmap(
                              vectorDrawable.intrinsicWidth,
                              vectorDrawable.intrinsicHeight,
                              Bitmap.Config.ARGB_8888)
                  
                          val canvas = Canvas(bitmap)
                          vectorDrawable.setBounds(0,0,canvas.width, canvas.height)
                          DrawableCompat.setTint(vectorDrawable, color)
                          vectorDrawable.draw(canvas)
                  
                          return BitmapDescriptorFactory.fromBitmap(bitmap)
                  
                      }
                  

                  现在在 onMapReady() -> .icon() 中进行更改

                  mMap.addMarker(
                         MarkerOptions().position(goa)
                             .title("Marker in Goa")
                             .draggable(true)
                             .icon(fromVectorToBitmap(R.drawable.location, Color.parseColor("#FF0560"))))
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-02-09
                    • 1970-01-01
                    • 2018-05-18
                    相关资源
                    最近更新 更多