web-dev-qa-db-ja.com

ランチャーは、背景の削除を含め、アダプティブアイコンの形状をどのように変更しますか?

バックグラウンド

Android Oから始まり、アプリはアダプティブアイコンを持つことができます。これは、描画可能な2層のフォアグラウンドとバックグラウンドです。バックグラウンドは、ランチャー/ユーザーが選択した形状になるマスクです。 OSにもデフォルトの形状があります。

以下は、Nova Launcherが実行できることの例です。

enter image description here

ご覧のように、使用する形状を選択できるだけでなく、形状をまったく回避することもできます(「レガシーアイコンを優先する」)。

ここにいくつかのリンクがあります:

問題

AdaptiveIconDrawable インスタンスを作成する方法を知っていますが、現在のインスタンスを作成するのに役立つウィザードを知っていますアプリでは、AdaptiveIconDrawableインスタンスを指定すると、ランチャーがどのように形状を変更するのかわかりません。

それだけでなく、形を変えられないランチャーを1、2個見たのを覚えています。

悲しいことに、この部分に関する情報は見つかりません。これは比較的新しい機能であるためか、と思われます。 StackOverflowには、そのキーワードもありません。

私が試したこと

アダプティブアイコンについて読みましたが、受信側への参照が見つかりませんでした。

私はそれがその中に2つのドローアブルを持っていることを知っています:

少なくとも、サードパーティのアプリから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内部ドローアブルの公式サイズは72 dp(前景)、108 dp(背景)です。これは、フォアグラウンドのドローアブルを取得し、何らかの方法でサイズを変更して、ビットマップに変換することを意味すると思います。

  3. どちらの場合にIconCompat.createWithAdaptiveBitmap()を使用するのが正確ですか? 「ビットマップを使用して動的ショートカットを作成している場合、サポートライブラリ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つのビットマップを同時に使用しないようにすることもできますが、これは問題ないと思います。

さまざまな種類の形のドローアブルの作成については、まだどうすればよいかわかりません。以下の回答で私が見た唯一の解決策は、角丸四角形または円を使用することですが、頭に浮かぶ可能性がある他の形状(たとえば、涙)があります。

23

AdaptiveIconDrawableインスタンスを指定した場合、ランチャーがどのように形状を変更するのかわかりません。

ランチャーは単なるアプリです。したがって、ランチャーは必要な(またはユーザーが選択した)形で背景を描画してから、前面に前景を描画します。

私自身のサンプルプロジェクトはありませんが、Nick Butcherが素晴らしいサンプルプロジェクトと一連のブログ投稿を作成しました: AdaptiveIconPlayground


AdaptiveIconDrawableインスタンスが与えられた場合、円形、長方形、角丸長方形、引き裂きなどの形にするにはどうすればよいですか?

最も簡単な方法は、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内部ドローアブルの公式サイズは72 dp(前景)、108 dp(背景)です。これは、フォアグラウンドのドローアブルを取得し、何らかの方法でサイズを変更して、ビットマップに変換することを意味すると思います。

背景が必要ない場合は、描画しないでください。あなたは完全にコントロールしています。通常、アイコンを描画する大きさはわかっているため、サイズは重要ではありません。ドキュメントには、前景と背景を108 dpにする必要があると記載されているため、図面を簡単に縮小できます。フォアグラウンド/バックグラウンドでベクターグラフィックスを使用する場合、好きな大きさで描画できるため、サイズは実際には重要ではありません。

フォアグラウンドをラスタライズする場合は、上記のようにカスタム描画を行うか、Canvas#drawBitmap(...)を選択します。これには、変換マトリックスまたは単に境界を渡すなど、ビットマップを描画するための複数のオプションも用意されています。

ドローアブルをラスタライズしない場合は、drawable.setBounds(x1, y1, x2, y2)を使用して、ドローアブル自体を描画する場所の境界を設定することもできます。これも機能するはずです。

この場合、IconCompat.createWithAdaptiveBitmap()を使用することが正確に役立ちますか? 「ビットマップを使用して動的ショートカットを作成している場合、サポートライブラリ26.0.0-beta2のIconCompat.createWithAdaptiveBitmap()が他のアダプティブアイコンと一致するようにビットマップが正しくマスクされていることを確認するのに役立つ場合があります。」 、しかし私はそれがどの場合に役立つのかわかりません。

_ShortCutInfo.Builder_ には、それを渡す必要があるsetIcon(Icon icon)メソッドがあります(これは互換バージョンにも当てはまります)。

アイコンは、アイコンとして渡されるビットマップの種類を制御するために使用されているようです。現在、Iconの他の使用法は見つかりませんでした。ランチャーを作成するときにこれを使用するとは思わない。


最後のコメントを反映した詳細情報

独自のドローアブルで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から、合成、継承まで、何でも使用でき、好きな形を描くことができます。

1
David Medenjak

ランチャーはアプリケーションよりも制限がはるかに少ないため、他のアプローチを使用できますが、1つのソリューションがNick Butcherの Adaptive Icon Playground にうまく表示されています。

おそらく興味があるクラスは Adaptive Icon View です。これは、背景をキャンバスビットマップとして各レイヤーのラスターを作成し、それらのレイヤーを角丸四角形として描画することで、アイコンのアダプティブバージョンをレンダリングします。クリッピングを実装します。

リンクされたレポははるかに有益であり、移動効果などのためにレイヤーを変換する方法の例が含まれますが、画像ビューで「アイコンを適応させる」ための基本的な擬似コードは次のとおりです。

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)
}
0
Nick Cardoso