oreo - iconos adaptativos android o
¿Cómo cambian los lanzadores la forma de un icono adaptativo, incluida la eliminación del fondo? (3)
Fondo
A partir de Android O, las aplicaciones pueden tener íconos adaptables, que son 2 capas de elementos dibujables: primer plano y un fondo. El fondo es una máscara que se convierte en una forma de la elección del lanzador / usuario, mientras que el sistema operativo también tiene una forma predeterminada.
Aquí hay un ejemplo de lo que Nova Launcher permite hacer:
Como puede ver, no solo permite elegir qué forma utilizar, sino también evitar una forma (en "preferir los iconos heredados").
Aquí hay algunos enlaces al respecto:
- https://www.youtube.com/watch?v=5MHFYfXno9c
- https://medium.com/@ianhlake/vectordrawable-adaptive-icons-3fed3d3205b5
El problema
Si bien sé cómo crear una instancia de AdaptiveIconDrawable y soy consciente del asistente que ayuda a crear una para la aplicación actual, no entiendo cómo, dado una instancia de AdaptiveIconDrawable, los lanzadores cambian la forma.
No solo eso, pero recuerdo que vi uno o dos lanzadores que permiten no tener ninguna forma.
Lamentablemente no puedo encontrar información sobre esta parte, tal vez porque esta es una característica relativamente muy nueva. Ni siquiera hay una palabra clave para esto aquí en StackOverflow.
Lo que he intentado
Intenté leer sobre iconos adaptativos, pero no pude encontrar una referencia al lado del receptor.
Sé que tiene los 2 elementos dibujables dentro de ella:
- https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html#getBackground()
- https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html#getForeground()
Sé, al menos, cómo obtener una instancia de AdaptiveIconDrawable de una aplicación de terceros (suponiendo que tenga una):
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
Las preguntas
Dada una instancia de AdaptiveIconDrawable, ¿cómo la configura para que tenga una forma circular, un rectángulo, un rectángulo redondeado, un desgarre, etc.?
¿Cómo elimino la forma y sigo teniendo un tamaño válido del icono (usando su dibujo dibujable en primer plano)? El tamaño oficial de un ícono de la aplicación para lanzadores es de 48 dp, mientras que los oficiales para AdaptiveIconDrawable internal drawables son 72dp (primer plano), 108dp (fondo). Supongo que esto significaría tomar el dibujo de primer plano, cambiar su tamaño de alguna manera y convertirlo en un mapa de bits.
¿En qué caso exactamente es útil usar
IconCompat.createWithAdaptiveBitmap()
? Se escribió que "si está creando un acceso directo dinámico utilizando un mapa de bits, puede encontrar la biblioteca de apoyo 26.0.0-beta2 IconCompat.createWithAdaptiveBitmap () útil para asegurarse de que su mapa de bits esté enmascarado correctamente para que coincida con otros iconos adaptables". , pero no entiendo en qué casos es útil.
EDITAR: para crear un mapa de bits de la parte delantera del icono adaptable, mientras se redimensiona a un tamaño adecuado, creo que esta podría ser una buena solución:
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)
Podría ser posible evitar tener 2 mapas de bits al mismo tiempo, pero creo que está bien.
Sobre la creación de una forma dibujable de varios tipos, todavía no estoy seguro de cómo hacerlo. La única solución que he visto en las respuestas a continuación es usar un rectángulo redondeado o un círculo, pero hay otras formas (por ejemplo, la lágrima) que pueden venir a la mente.
No entiendo cómo, dada una instancia de AdaptiveIconDrawable, los lanzadores cambian la forma.
Los lanzadores son solo aplicaciones, por lo que simplemente dibujan el fondo en la forma que desean (o el usuario seleccionado) y luego dibujan el primer plano en la parte superior.
No tengo un proyecto de muestra propio, pero Nick Butcher hizo un gran proyecto de muestra y una serie de publicaciones en el blog: github.com/nickbutcher/AdaptiveIconPlayground .
Dada una instancia de AdaptiveIconDrawable, ¿cómo la configura para que tenga una forma circular, un rectángulo, un rectángulo redondeado, un desgarre, etc.?
La forma más sencilla es rasterizar el dibujo y dibujar el mapa de bits con un sombreado como se hace en AdaptiveIconView de Nick:
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)
¿Cómo elimino la forma y sigo teniendo un tamaño válido del icono (usando su dibujo dibujable en primer plano)? El tamaño oficial de un ícono de la aplicación para lanzadores es de 48 dp, mientras que los oficiales para AdaptiveIconDrawable internal drawables son 72dp (primer plano), 108dp (fondo). Supongo que esto significaría tomar el dibujo de primer plano, cambiar su tamaño de alguna manera y convertirlo en un mapa de bits.
Si no quieres un fondo, simplemente no lo dibujes. Usted está en control total. El tamaño realmente no importa, porque generalmente se sabe cuán grandes deben dibujarse los iconos. La documentación indica que el primer plano y el fondo deben ser 108dp, para que pueda simplemente reducir la escala de su dibujo. Si el primer plano / el fondo usan gráficos vectoriales, entonces el tamaño realmente no importa, ya que puedes dibujarlos como quieras.
Si rasteriza el primer plano, entonces puede hacer un dibujo personalizado como se ve arriba, o elegir Canvas#drawBitmap(...)
, que también ofrece múltiples opciones para dibujar un Bitmap, incluyendo pasar una matriz de transformación, o simplemente algunos límites.
Si no rasteriza su dibujo, también puede usar drawable.setBounds(x1, y1, x2, y2)
, donde puede establecer los límites en los que debe dibujarse el dibujo. Esto también debería funcionar.
¿En qué caso exactamente es útil usar IconCompat.createWithAdaptiveBitmap ()? Se escribió que "si está creando un acceso directo dinámico utilizando un mapa de bits, puede encontrar la biblioteca de apoyo 26.0.0-beta2 IconCompat.createWithAdaptiveBitmap () útil para asegurarse de que su mapa de bits esté enmascarado correctamente para que coincida con otros iconos adaptables". , pero no entiendo en qué casos es útil.
setIcon(Icon icon)
tiene un setIcon(Icon icon)
en el que necesita pasarlo. (Y lo mismo se aplica para las versiones compatibles)
Parece que se usa Icon para tener control sobre el tipo de mapa de bits que se pasa como un icono. En este momento no pude encontrar ningún otro uso para Icon. No creo que uses esto al crear un lanzador.
Más información reflejando el último comentario.
¿Envuelves la clase AdaptiveIconDrawable con tu propio dibujo? Solo quiero convertirlo de alguna manera a algo que pueda usar, tanto a ImageView como a Bitmap, y deseo controlar la forma, utilizando todas las formas que he mostrado en la captura de pantalla anterior. ¿Cómo lo haría?
Si sigue los enlaces anteriores, puede ver un AdaptiveIconView
personalizado que dibuja el AdaptiveIconDrawable, por lo que realizar una vista personalizada es definitivamente una opción, pero todo lo que se menciona se puede mover con la misma facilidad a un Drawable personalizado, que luego también podría usar con un básico. ImageView.
Puede lograr varios fondos diferentes utilizando los métodos disponibles en Canvas
junto con un BitmapShader
como se muestra arriba, por ejemplo, adicionalmente a drawRoundRect
tendríamos
canvas.drawCircle(centerX, centerY, radius, backgroundPaint) // circle
canvas.drawRect(0f, 0f, width, height, backgroundPaint) // rect
canvas.drawPath(path, backgroundPaint) // more complex shapes
Para cambiar entre las formas de fondo, puede usar cualquier cosa desde if / else, sobre la composición, hasta la herencia, y simplemente dibujar la forma que desee.
Dado que Launcher es solo una actividad, puedes dibujar cualquier cosa. Puedes dibujar íconos de aplicaciones como ponis que se ejecutan en hermosas nubes animadas. Este es tu mundo, que obedece solo a tus reglas.
Además ... No hay magia en el mundo de la programación. Si te enfrentas a la magia, solo usa los descompiladores (con Java es muy fácil), encuentra el código responsable de la magia, documenta y escribe una excelente publicación en el blog sobre cómo funciona esta magia.
Dada una instancia de AdaptiveIconDrawable, ¿cómo la configura para que tenga una forma circular, un rectángulo, un rectángulo redondeado, un desgarre, etc.?
Puede usar AdaptiveIconDrawable.getBackground () y agregarle cualquier máscara. En realidad, puede hacer cualquier cosa que desee con el ícono, AdaptiveIconDrawable es solo una forma, donde puede dividir el primer plano y el fondo de manera fácil, sin filtros complicados ni redes neuronales. Agrega paralaje, animaciones y muchos más efectos, ahora tienes 2 capas para ello.
Los lanzadores tienen muchas menos restricciones que las aplicaciones, por lo que pueden usar otros enfoques, pero una solución ha sido bien presentada en github.com/nickbutcher/AdaptiveIconPlayground Nick Butcher.
La clase en la que probablemente te interese es la AdaptiveIconView que presenta versiones adaptadas del icono al crear un ráster de cada capa con el fondo como un mapa de bits de lienzo y luego dibujar esas capas como rectángulos redondeados para implementar el recorte.
El repositorio vinculado será mucho más informativo e incluye ejemplos de cómo transformar la capa para efectos de movimiento, etc., pero aquí está el pseudocódigo básico para "adaptar un icono" en una vista de imagen:
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)
}