发射器如何更改自适应图标的形状,包括删除背景?

背景

从Android O开始,应用程序可以拥有自适应图标,即2层可绘图:前景和背景。 背景是一个掩码,它可以成为启动器/用户选择的形状,而OS也有一个默认的形状。

以下是Nova Launcher允许执行的示例:

在此处输入图像描述

正如您所看到的,它不仅可以选择使用哪种形状,还可以避免形状(在“更喜欢的旧图标”中)。

以下是一些关于它的链接:

  • https://www.youtube.com/watch?v=5MHFYfXno9c
  • https://medium.com/@ianhlake/vectordrawable-adaptive-icons-3fed3​​d3205b5

问题

虽然我知道如何创建一个AdaptiveIconDrawable实例,并且我知道有助于为当前应用程序创建一个实例的向导,但是在给定一个AdaptiveIconDrawable实例的情况下,启动器会改变形状。

不仅如此,我记得我看到一个或两个发射器不允许任何形状。

可悲的是,我无法find有关此部分的任何信息,可能是因为这是一个相对非常新的function。 StackOverflow上甚至没有关键字。

我试过的

我尝试阅读自适应图标,但找不到对接收方的引用。

我知道它里面有2个drawable:

  • https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html#getBackground()
  • https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html#getForeground()

我知道,至少,如何从第三方应用程序中获取AdaptiveIconDrawable实例(假设它有一个):

PackageManager pm = context.getPackageManager(); Intent launchIntentForPackage = pm.getLaunchIntentForPackage(packageName); String fullPathToActivity = launchIntentForPackage.getComponent().getClassName(); ActivityInfo activityInfo = pm.getActivityInfo(new ComponentName(packageName, fullPathToActivity), 0); int iconRes = activityInfo.icon; Drawable drawable = pm.getDrawable(packageName, iconRes, activityInfo.applicationInfo); // will be AdaptiveIconDrawable, if the app has it 

问题

  1. 给定一个AdaptiveIconDrawable实例,如何将其塑造成圆形,矩形,圆角矩形,撕裂等?

  2. 如何删除形状并仍然具有图标的有效大小(使用其中的前景可绘制)? 发射器的应用程序图标的官方大小为48 dp,而AdaptiveIconDrawable内部drawable的官方大小为72dp(前景),108dp(背景)。 我想这意味着将前景绘制,以某种方式resize,并转换为位图。

  3. 在哪种情况下,使用IconCompat.createWithAdaptiveBitmap()是否有用? 有人写道:“如果您使用Bitmap构建动态快捷方式,您可能会发现支持库26.0.0-beta2的IconCompat.createWithAdaptiveBitmap()可用于确保正确屏蔽您的位图以匹配其他自适应图标。” ,但我不知道哪些情况对它有用。


编辑:为了从自适应图标的前景部分创建一个位图,同时调整到适当的大小,我认为这可能是一个很好的解决方案:

 val foregroundBitmap = convertDrawableToBitmap(drawable.foreground) val targetSize = convertDpToPixels(this, ...).toInt() val scaledBitmap = ThumbnailUtils.extractThumbnail(foregroundBitmap, targetSize, targetSize, ThumbnailUtils.OPTIONS_RECYCLE_INPUT) fun convertDrawableToBitmap(drawable: Drawable?): Bitmap? { if (drawable == null) return null if (drawable is BitmapDrawable) { return drawable.bitmap } val bounds = drawable.bounds val width = if (!bounds.isEmpty) bounds.width() else drawable.intrinsicWidth val height = if (!bounds.isEmpty) bounds.height() else drawable.intrinsicHeight val bitmap = Bitmap.createBitmap(if (width <= 0) 1 else width, if (height <= 0) 1 else height, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) drawable.setBounds(0, 0, canvas.width, canvas.height) drawable.draw(canvas) drawable.bounds = bounds; return bitmap } fun convertDpToPixels(context: Context, dp: Float): Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.resources.displayMetrics) 

可能同时避免使用2个位图,但我认为这是可以的。

关于各种types的塑形抽屉的创作,我仍然不知道该怎么做。 我在下面的答案中看到的唯一解决方案是使用圆角矩形或圆形,但还有其他形状(例如撕裂)可以浮现在脑海中。

Solutions Collecting From Web of "发射器如何更改自适应图标的形状,包括删除背景?"

在给定AdaptiveIconDrawable实例的情况下,我不知道如何更改形状。

启动器只是应用程序,因此它们只是以他们想要的形状(或用户选择的)绘制背景,然后在前面绘制前景。

我没有自己的示例项目,但是Nick Butcher做了一个很棒的示例项目和一系列博客文章: AdaptiveIconPlayground 。


给定一个AdaptiveIconDrawable实例,如何将其塑造成圆形,矩形,圆角矩形,撕裂等?

最简单的方法是使用着色器光栅化drawable并绘制位图,就像在Nick的AdaptiveIconView中完成一样:

 private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG) private val background: Bitmap // ... background = Bitmap.createBitmap(layerSize, layerSize, Bitmap.Config.ARGB_8888) backgroundPaint.shader = BitmapShader(background, CLAMP, CLAMP) // < rasterize drawable onto `background` > // draw desired shape(s) canvas.drawRoundRect(0f, 0f, iconSize.toFloat(), iconSize.toFloat(), cornerRadius, cornerRadius, backgroundPaint) 

如何删除形状并仍然具有图标的有效大小(使用其中的前景可绘制)? 发射器的应用程序图标的官方大小为48 dp,而AdaptiveIconDrawable内部drawable的官方大小为72dp(前景),108dp(背景)。 我想这意味着将前景绘制,以某种方式resize,并转换为位图。

如果你不想要背景,就不要画它。 你完全掌控了。 大小并不重要,因为您通常知道应该绘制多大的图标。 文档说明前景和背景应该是108dp,因此您可以简单地缩小绘图。 如果前景/背景使用矢量图形,那么尺寸确实无关紧要,因为您可以随心所欲地绘制它们。

如果你光栅化前景,那么你可以如上所示进行自定义绘制,或者选择Canvas#drawBitmap(...) ,它还提供了多个绘制位图的选项,包括传入变换矩阵或简单的一些边界。

如果你没有光栅化你的drawable,你也可以使用drawable.setBounds(x1, y1, x2, y2) ,你可以在其中设置drawable应该绘制自己的边界。 这也应该有效。

在哪种情况下,使用IconCompat.createWithAdaptiveBitmap()是否有用? 有人写道:“如果您使用Bitmap构建动态快捷方式,您可能会发现支持库26.0.0-beta2的IconCompat.createWithAdaptiveBitmap()可用于确保正确屏蔽您的位图以匹配其他自适应图标。” ,但我不知道哪些情况对它有用。

ShortCutInfo.Builder有一个setIcon(Icon icon)方法,您需要将其传入。(同样适用于compat版本)

似乎Icon用于控制作为图标传入的Bitmap的types。 现在我找不到Icon的任何其他用法。 我不认为你在创建启动器时会使用它。


反映最后评论的更多信息

你用自己的drawable包装AdaptiveIconDrawable类吗? 我只是想以某种方式将它转换为我可以使用的东西,对于ImageView和Bitmap,我希望使用我在上面截图中显示的所有形状来控制形状。 我该怎么办?

如果您按照上面的链接,您可以看到绘制AdaptiveIconDrawable的自定义AdaptiveIconView,因此执行自定义视图绝对是一个选项,但所提到的所有内容都可以轻松地移动到自定义Drawable中,然后您也可以使用它ImageView的。

您可以通过使用Canvas上可用的方法以及BitmapShader来实现各种不同的背景,如上所示,例如,除了drawRoundRect我们将拥有

 canvas.drawCircle(centerX, centerY, radius, backgroundPaint) // circle canvas.drawRect(0f, 0f, width, height, backgroundPaint) // rect canvas.drawPath(path, backgroundPaint) // more complex shapes 

要在背景形状之间切换,您可以使用if / else,over composition,inheritance等任何内容,只需绘制您喜欢的形状。

发射器的限制比应用程序少得多,因此他们可能会使用其他方法,但在Nick Butcher的Adaptive Icon Playground中已经很好地展示了一种解决方案。

您可能感兴趣的类是自适应图标视图 ,它通过创建每个图层的栅格来渲染图标的改编版本,背景为canvas位图,然后将这些图层绘制为圆角矩形以实现剪裁。

链接的repo将提供更多信息,并包括如何为运动效果等转换图层的示例,但这里是图像视图中“调整图标”的基本伪代码:

 setIcon() { //erase canvas first... canvas.setBitmap(background) drawable.setBounds(0, 0, layerSize, layerSize) drawable.draw(canvas) } onDraw() { //draw shadow first if needed... canvas.drawRoundRect(..., cornerRadius, backgroundPaint) canvas.drawRoundRect(..., cornerRadius, foregroundPaint) } 

由于Launcher只是一个Activity,你可以绘制任何东西。 您可以绘制应用程序图标,如在美丽的animation云上运行的小马。 这是你的世界,只遵守你的规则。

进一步……编程世界没有魔力。 如果你面对魔术,只需使用反编译器(使用Java非常容易),find负责魔法的代码,记录它并写一篇关于这个魔法如何工作的精彩博客文章。

给定一个AdaptiveIconDrawable实例,如何将其塑造成圆形,矩形,圆角矩形,撕裂等?

您可以使用AdaptiveIconDrawable.getBackground()并向其添加任何掩码。 实际上,你可以用图标做任何你想做的事情,AdaptiveIconDrawable就是这样,你可以轻松地分割前景和背景,没有复杂的filter或神经网络。 添加视差,animation和更多效果,现在你有2层。