examples - parametros de layout android
Tinte/tenue dibujable al tacto (8)
La aplicación en la que estoy trabajando actualmente usa muchos ImageViews como botones. Los gráficos de estos botones utilizan el canal alfa para atenuar los bordes del botón y hacer que se vean irregulares. Actualmente, tenemos que generar 2 gráficos para cada botón (1 para el estado seleccionado / enfocado / presionado y el otro para el estado predeterminado no seleccionado) y usar un StateListDrawable definido en un archivo XML para cada botón.
Si bien esto funciona bien, parece un gran desperdicio ya que todos los gráficos seleccionados son simplemente versiones teñidas de los botones no seleccionados. Estos toman tiempo para producir (aunque sea poco) y ocupan espacio en la APK final. Parece que debería haber una manera fácil de hacerlo automáticamente.
La solución perfecta, parece, es usar ImageViews para cada botón y especificar en su atributo de tinte una ColorStateList. Este enfoque tiene la ventaja de que solo se necesita un ColorStateList XML para todos los botones (que comparten el mismo tinte). Sin embargo, no funciona. Como se mencionó here , ImageView lanza una excepción NumberFormatException cuando el valor proporcionado para tint es cualquier otra cosa que no sea un solo color.
Mi siguiente intento fue usar un LayerDrawable para el dibujo seleccionado. Dentro de la lista de capas, tendríamos la imagen original en la parte inferior de la pila cubierta por un rectángulo semitransparente. Esto funcionó en las partes sólidas del gráfico del botón. Sin embargo, los bordes que se suponía eran completamente transparentes, ahora eran del mismo color que la capa superior.
¿Alguien ha encontrado este problema antes y ha encontrado una solución razonable? Me gustaría seguir con los enfoques XML, pero probablemente codificaré una subclase de ImageView simple que hará el tintado requerido en el código.
Creo que esta solución es bastante simple:
final Drawable drawable = ... ;
final int darkenValue = 0x3C000000;
mButton.setOnTouchListener(new OnTouchListener() {
Rect rect;
boolean hasColorFilter = false;
@Override
public boolean onTouch(final View v, final MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
drawable.setColorFilter(darkenValue, Mode.DARKEN);
hasColorFilter = true;
break;
case MotionEvent.ACTION_MOVE:
if (rect.contains(v.getLeft() + (int) motionEvent.getX(), v.getTop()
+ (int) motionEvent.getY())) {
if (!hasColorFilter)
drawable.setColorFilter(darkenValue, Mode.DARKEN);
hasColorFilter = true;
} else {
drawable.setColorFilter(null);
hasColorFilter = false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
drawable.setColorFilter(null);
hasColorFilter = false;
break;
}
return false;
}
});
Creo que esta solución es simple!
Paso 1: crear res / drawable / dim_image_view.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/dim_image_view_normal"/> <!-- focused -->
<item android:state_pressed="true" android:drawable="@drawable/dim_image_view_pressed"/> <!-- pressed -->
<item android:drawable="@drawable/dim_image_view_normal"/> <!--default -->
</selector>
Paso 2: crear res / drawable / dim_image_view_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item><bitmap android:src="@mipmap/your_icon"/></item>
</layer-list>
Paso 3: crear res / drawable / dim_image_view_pressed.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item><bitmap android:src="@mipmap/your_icon" android:alpha="0.5"></bitmap></item>
</layer-list>
Paso 4: Intenta configurar la imagen en un diseño xml como:
android:src="@drawable/dim_image_view"
Para aquellos que han encontrado una necesidad similar, resolver esto en el código es bastante limpio. Aquí hay una muestra:
public class TintableButton extends ImageView {
private boolean mIsSelected;
public TintableButton(Context context) {
super(context);
init();
}
public TintableButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TintableButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mIsSelected = false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && !mIsSelected) {
setColorFilter(0x99000000);
mIsSelected = true;
} else if (event.getAction() == MotionEvent.ACTION_UP && mIsSelected) {
setColorFilter(Color.TRANSPARENT);
mIsSelected = false;
}
return super.onTouchEvent(event);
}
}
No está terminado, pero funciona bien como una prueba de concepto.
Puedes usar una clase que he creado https://github.com/THRESHE/TintableImageButton No es exactamente la mejor solución pero funciona.
Yo también tengo botones de aspecto irregular y necesito el tinte. Al final, solo recurrí a tener una lista de colores del estado como fondo de mi ImageButton
. Da la impresión de que el color del botón está cambiando en la prensa y es muy sencillo y menos intensivo en el cálculo. Sé que esto no es exactamente un tinte, pero si lo hace, proporciona la información visual necesaria para el usuario en lo que generalmente es un momento fugaz.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_pressed="true"
android:drawable="@android:color/darker_gray" />
<item android:drawable="@android:color/transparent" />
</selector>
Puedes combinar StateListDrawable
y LayerDrawable
para esto.
public Drawable getDimmedDrawable(Drawable drawable) {
Resources resources = getContext().getResources();
StateListDrawable stateListDrawable = new StateListDrawable();
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{
drawable,
new ColorDrawable(resources.getColor(R.color.translucent_black))
});
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, layerDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_focused}, layerDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_selected}, layerDrawable);
stateListDrawable.addState(new int[]{}, drawable);
return stateListDrawable;
}
Asumo que nuestro colors.xml se ve así
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="translucent_black">#80000000</color>
</resources>
Tu respuesta fue excelente :)
Sin embargo, en lugar de crear un objeto de botón, solo llamo a OnTouchlistener para cambiar el estado de cada vista.
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Drawable background = v.getBackground();
background.setColorFilter(0xBB000000, PorterDuff.Mode.SCREEN);
v.setBackgroundDrawable(background);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Drawable background = v.getBackground();
background.setColorFilter(null);
v.setBackgroundDrawable(background);
}
return true;
}
Da los mismos resultados aunque
Tu respuesta también fue excelente;)
Estoy modificando un poco, te dará un resultado como este:
Rect rect;
Drawable background;
boolean hasTouchEventCompleted = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(),
v.getBottom());
hasTouchEventCompleted = false;
background = v.getBackground();
background.setColorFilter(0x99c7c7c7,
android.graphics.PorterDuff.Mode.MULTIPLY);
v.setBackgroundDrawable(background);
} else if (event.getAction() == MotionEvent.ACTION_UP
&& !hasTouchEventCompleted) {
background.setColorFilter(null);
v.setBackgroundDrawable(background);
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop()
+ (int) event.getY())) {
// User moved outside bounds
background.setColorFilter(null);
v.setBackgroundDrawable(background);
hasTouchEventCompleted = true;
}
}
//Must return false, otherwise you need to handle Click events yourself.
return false;
}