Comparando dos elementos de diseño en Android

90

Cómo comparar dos dibujables, lo hago así pero no tengo ningún éxito

public void MyClick(View view)
{
 Drawable fDraw = view.getBackground();
 Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);

  if(fDraw.equals(sDraw))
  {
   //Not coming
  }
}
Roshan Jha
fuente

Respuestas:

150

Actualizar https://stackoverflow.com/a/36373569/1835650

getConstantState () no funciona bien

Hay otra forma de comparar:

mRememberPwd.getDrawable().getConstantState().equals
            (getResources().getDrawable(R.drawable.login_checked).getConstantState());

mRemeberPwdes un ImageViewen este ejemplo. Si está usando un TextView, use getBackground().getConstantStateen su lugar.

Mejonzhan
fuente
3
Esta solución funciona y es mejor, ya que evita convertir Drawable en Bitmap y comparar.
Braj
2
No siempre: WallpaperManager.getInstance (this) .getFastDrawable (). GetConstantState () es nulo.
paulgavrikov
Perdón por aceptar esto como respuesta tarde y cambiar mi propia respuesta, ya que parece una mejor opción (y más votos positivos también :)). Así que comprobando esto como respuesta.
Roshan Jha
Gracias, esta es la mejor respuesta
satyres
8
Este código funciona muy bien en dispositivos inferiores a 5.0, pero obtengo errores al usarlo en un dispositivo 5.0, ¿alguien puede confirmar que este método funciona o no en Android 5.0 o superior?
Phil3992
41

Depender getConstantState()solo puede resultar en falsos negativos .

El enfoque que he adoptado es intentar comparar ConstantState en primera instancia, pero recurrir a una comparación de mapa de bits si esa comprobación falla.

Esto debería funcionar en todos los casos (incluidas las imágenes que no son recursos) pero tenga en cuenta que consume mucha memoria.

public static boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
    Drawable.ConstantState stateA = drawableA.getConstantState();
    Drawable.ConstantState stateB = drawableB.getConstantState();
    // If the constant state is identical, they are using the same drawable resource.
    // However, the opposite is not necessarily true.
    return (stateA != null && stateB != null && stateA.equals(stateB))
            || getBitmap(drawableA).sameAs(getBitmap(drawableB));
}

public static Bitmap getBitmap(Drawable drawable) {
    Bitmap result;
    if (drawable instanceof BitmapDrawable) {
        result = ((BitmapDrawable) drawable).getBitmap();
    } else {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        // Some drawables have no intrinsic width - e.g. solid colours.
        if (width <= 0) {
            width = 1;
        }
        if (height <= 0) {
            height = 1;
        }

        result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(result);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
    }
    return result;
}
vaughandroid
fuente
¡Esto es 100% cierto y necesita más votos a favor! Gente, por favor pruebe su código con Drawables conocidos antes de confiar inmediatamente en la getConstantState()comparación
avalancha
¡Buenos hallazgos! Pero de acuerdo con la documentación de BitmapDrawable.getBitmap (), getBitmap () puede ser nulo, por lo que es posible que desee verificar esto también
PhilLab
Esta respuesta es verdadera y lo he comprobado después de depurar varias horas con la codificación de solo getConstantState ().
Raghav Satyadev
Para estar seguro, setBoundsy drawen una copia en lugar del original stackoverflow.com/a/25462223/1916449
arekolek
Esto no funciona con BitmapDrawables teñidos (ver setTintMode / setTint / setTintList). Los mapas de bits pueden ser byte por byte idénticos pero con propiedades de tinte diferentes. Debido a que el SDK de Android no proporciona ningún captador para las propiedades de tinte, es posible que no haya una manera de hacerlo funcionar con elementos de diseño teñidos.
Theo
13

Mi pregunta era simplemente comparar dos elementos de diseño, lo intenté, pero no pude obtener ningún método que compare directamente dos elementos de diseño, sin embargo, para mi solución, cambié el elemento de diseño a mapa de bits y luego comparé dos mapas de bits y eso está funcionando.

Bitmap bitmap = ((BitmapDrawable)fDraw).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable)sDraw).getBitmap();

if(bitmap == bitmap2)
    {
        //Code blcok
    }
Roshan Jha
fuente
eso es lo que te estaba sugiriendo, comparar dibujable según tipo de dibujante.
jeet
1
Es posible que también desee comparar mapas de bits como este: stackoverflow.com/a/7696320/317889
HGPB
2
Esto es muy pesado, así que considere reciclar Bitmaps o terminará en un OutOfMemoryError.
paulgavrikov
2
¿Por qué puede comparar mapas de bits con la igualdad de puntero (==)? Esperaría que se necesitara Bitmap.equals ().
Ellen Spertus
@espertus Tienes razón. Usé lo mismo en la pregunta para los objetos dibujables, no sé por qué se convirtió en == para el objeto de mapa de bits en la respuesta. De todas formas, gracias por señalar este básico.
Roshan Jha
9

para SDK 21+

esto funciona en SDK -21

mRememberPwd.getDrawable().getConstantState().equals
        (getResources().getDrawable(R.drawable.login_checked).getConstantState())

para SDK +21 android 5. establezca la identificación dibujable en imageview con etiqueta

img.setTag(R.drawable.xxx);

y comparar así

if ((Integer) img.getTag() == R.drawable.xxx)
{
....your code
}

esta solución es para quien quiera comparar drawableid de imageviewcon id de drawable.xxx.

Esconder
fuente
En realidad, esto funciona, pero estoy un poco sorprendido de que no haya otra posibilidad, ¡T_T!
error1337
4

La solución para Android 5:

 if(image.getDrawable().getConstantState().equals(image.getContext().getDrawable(R.drawable.something).getConstantState()))
Tiago Santos
fuente
4

getDrawable (int) ahora está obsoleto. Utilice getDrawable (contexto, R.drawable.yourimageid)

Comparar dos fondos

Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());
RAJESH KUMAR ARUMUGAM
fuente
2
Esto funcionó como un encanto para corregir un error extraño en Android 5. En mi código, el dibujable real se devolvió context.getResources().getDrawable(R.drawable.***)en Android 6+ pero no en Android 5. Con este pequeño cambio, puedo comparar los dibujables de fondo en todas las versiones de Android sin problemas.
Jose_GD
3

quizás intentarlo de esta manera:

public void MyClick(View view)
{
 Drawable fDraw = view.getBackground();
 Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);

  if(fDraw.hashCode() == sDraw.hashCode())
  {
   //Not coming
  }
}

o preparar un método que tome dos argumentos dibujables y devuelva booleanos. En ese método, puede convertir dibujable en bytes y comparar,

public boolean compareDrawable(Drawable d1, Drawable d2){
    try{
        Bitmap bitmap1 = ((BitmapDrawable)d1).getBitmap();
        ByteArrayOutputStream stream1 = new ByteArrayOutputStream();
        bitmap1.compress(Bitmap.CompressFormat.JPEG, 100, stream1);
        stream1.flush();
        byte[] bitmapdata1 = stream1.toByteArray();
        stream1.close();

        Bitmap bitmap2 = ((BitmapDrawable)d2).getBitmap();
        ByteArrayOutputStream stream2 = new ByteArrayOutputStream();
        bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, stream2);
        stream2.flush();
        byte[] bitmapdata2 = stream2.toByteArray();
        stream2.close();

        return bitmapdata1.equals(bitmapdata2);
    }
    catch (Exception e) {
        // TODO: handle exception
    }
    return false;
}
waqaslam
fuente
verifique la respuesta actualizada. si aún no funciona, verifique sus elementos de diseño. O intente pasar los mismos elementos de diseño para verificar la funcionalidad del código
waqaslam
sí, después de no tener éxito, estoy pensando lo mismo para convertir los elementos de diseño a mapa de bits y luego a bytes, déjame probar ese, gracias por tus esfuerzos
Roshan Jha
no funciona, ¿lo probaste? podría haber algo mal, ¿no podemos comparar directamente dos dibujables?
Roshan Jha
¿Intentaste pasar el mismo dibujo para e.g R.drawable.abcambos parámetros?
waqaslam
hola waqas, verifique su método una vez más y luego diga nuevamente si está funcionando o no, es posible que esté haciendo algo mal pero eso no funcionó, pero luego cambia la definición de mi pregunta sobre cómo comparar dos elementos de diseño. dibujable al mapa de bits y luego los bytes compararán el mapa de bits y los bytes, ese no es mi requisito. si verificamos los métodos dibujables, entonces existe el método .equals (objeto), así que pensé que debería funcionar directamente, pero no lo hizo. a continuación, estoy convirtiendo un dibujo a mapa de bits y luego comparando dos mapas de bits y está funcionando.
Roshan Jha
2

Ok, creo que he encontrado la solución definitiva para esto. Gracias a AppCompat y sus amigos, el dibujante proporcionado a veces se infla en diferentes formas, por lo que no es suficiente getResources().getBitmap(R.drawable.my_awesome_drawable).

Entonces, para obtener una instancia dibujable del mismo tipo y forma que proporciona la vista, uno puede hacer esto:

public static Drawable drawableFrom(View view, @DrawableRes int drawableId) {
    Context context = view.getContext();
    try {
        View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
        dummyView.setBackgroundResource(drawableId);
        return dummyView.getBackground();
    } catch (Exception e) {
      return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
    }
}

Esto es útil al realizar pruebas. Sin embargo, no recomendaría hacer esto en producción. Si es necesario, sería deseable almacenamiento en caché adicional para evitar hacer demasiada reflexión.

Para las pruebas de Expresso, puede usar esto bastante bien:

onView(withDrawable(R.drawable.awesome_drawable))
  .check(matches(isDisplayed()));

o

onView(withId(R.id.view_id))
  .check(matches(withDrawable(R.drawable.awesome_drawable)));

Antes tendrás que declarar esta clase auxiliar:

public class CustomMatchers {

  public static Matcher<View> withDrawable(@DrawableRes final int drawableId) {
     return new DrawableViewMatcher(drawableId);
  }
  private static class DrawableViewMatcher extends TypeSafeMatcher<View> {

     private final int expectedId;
     private String resourceName;

     private enum DrawableExtractionPolicy {
        IMAGE_VIEW {
          @Override
          Drawable findDrawable(View view) {
             return view instanceof ImageView ? ((ImageView) view).getDrawable() : null;
          }
        },
        TEXT_VIEW_COMPOUND {
          @Override
          Drawable findDrawable(View view) {
             return view instanceof TextView ? findFirstCompoundDrawable((TextView) view) : null;
          }
        },
        BACKGROUND {
          @Override
          Drawable findDrawable(View view) {
             return view.getBackground();
          }
        };

        @Nullable
        private static Drawable findFirstCompoundDrawable(TextView view) {
          for (Drawable drawable : view.getCompoundDrawables()) {
             if (drawable != null) {
                return drawable;
             }
          }
          return null;
        }

        abstract Drawable findDrawable(View view);

     }

     private DrawableViewMatcher(@DrawableRes int expectedId) {
        this.expectedId = expectedId;
     }

     @Override
     protected boolean matchesSafely(View view) {
        resourceName = resources(view).getResourceName(expectedId);
        return haveSameState(actualDrawable(view), expectedDrawable(view));
     }

     private boolean haveSameState(Drawable actual, Drawable expected) {
        return actual != null && expected != null && areEqual(expected.getConstantState(), actual.getConstantState());
     }

     private Drawable actualDrawable(View view) {
        for (DrawableExtractionPolicy policy : DrawableExtractionPolicy.values()) {
          Drawable drawable = policy.findDrawable(view);
          if (drawable != null) {
             return drawable;
          }
        }
        return null;
     }

     private boolean areEqual(Object first, Object second) {
        return first == null ? second == null : first.equals(second);
     }

     private Drawable expectedDrawable(View view) {
        return drawableFrom(view, expectedId);
     }

     private static Drawable drawableFrom(View view, @DrawableRes int drawableId) {
        Context context = view.getContext();
        try {
          View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
          dummyView.setBackgroundResource(drawableId);
          return dummyView.getBackground();
        } catch (Exception e) {
          return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
        }
     }

     @NonNull
     private Resources resources(View view) {
        return view.getContext().getResources();
     }

     @Override
     public void describeTo(Description description) {
        description.appendText("with drawable from resource id: ");
        description.appendValue(expectedId);
        if (resourceName != null) {
          description.appendValueList("[", "", "]", resourceName);
        }
     }
  }

}
pablisco
fuente
1

Use getTag () y setTag () para comparar

Pooja Akshantal
fuente
0

Ya respondí sobre un tema similar aquí: Obtenga el ID de un elemento de diseño en ImageView . El enfoque se basa en etiquetar una vista con un ID de recurso específico en el archivo personalizado LayoutInflater. Todo el proceso está automatizado por una simple biblioteca TagView .

Como resultado, puede comparar dos elementos de diseño solo por sus identificadores:

TagViewUtils.getTag(view, ViewTag.VIEW_BACKGROUND.id) == R.drawable.twt_hover
Bogdan Kornev
fuente
0

Ampliando la respuesta de @vaughandroid, el siguiente Matcher funciona para un Vector Drawable que está teñido. Debe proporcionar el tinte que se utilizó para Drawable.

public static Matcher<View> compareVectorDrawables(final int imageId, final int tintId) {
        return new TypeSafeMatcher<View>() {

        @Override
        protected boolean matchesSafely(View target) {
            if (!(target instanceof ImageView)) {
                return false;
            }
            ImageView imageView = (ImageView) target;
            if (imageId < 0) {
                return imageView.getDrawable() == null;
            }
            Resources resources = target.getContext().getResources();
            Drawable expectedDrawable = resources.getDrawable(imageId, null);
            if (expectedDrawable == null) {
                return false;
            }

            Drawable imageDrawable = imageView.getDrawable();
            ColorFilter imageColorFilter = imageDrawable.getColorFilter();

            expectedDrawable.setColorFilter(imageColorFilter);
            expectedDrawable.setTintList(target.getResources()
                    .getColorStateList(tintId, null));

            boolean areSame = areDrawablesIdentical(imageDrawable, expectedDrawable);
            return areSame;
        }

        public boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
            Drawable.ConstantState stateA = drawableA.getConstantState();
            Drawable.ConstantState stateB = drawableB.getConstantState();
            // If the constant state is identical, they are using the same drawable resource.
            // However, the opposite is not necessarily true.
            return (stateA != null && stateB != null && stateA.equals(stateB))
                    || getBitmap(drawableA).sameAs(getBitmap(drawableB));
        }

        public Bitmap getBitmap(Drawable drawable) {
            Bitmap result;
            if (drawable instanceof BitmapDrawable) {
                result = ((BitmapDrawable) drawable).getBitmap();
            } else {
                int width = drawable.getIntrinsicWidth();
                int height = drawable.getIntrinsicHeight();
                // Some drawables have no intrinsic width - e.g. solid colours.
                if (width <= 0) {
                    width = 1;
                }
                if (height <= 0) {
                    height = 1;
                }

                result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(result);
                drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
                drawable.draw(canvas);
            }
            return result;
        }

        @Override
        public void describeTo(Description description) {

        }
    };
}
Jeremiah Eikenberg
fuente
0

Compare 2 dibujables:

drawable1.constantState == drawable2.constantState
            || drawable1.toBitmap().sameAs(drawable2.toBitmap())

Si no puede encontrar Drawable.toBitmap(...)aquí es Drawable.kt

Benny
fuente
-1

si desea comparar directamente dos dibujables, use el siguiente código

Dibujable fDraw = getResources (). GetDrawable (R.drawable.twt_hover);

Dibujable sDraw = getResources (). GetDrawable (R.drawable.twt_hover);

if (fDraw.getConstantState().equals(sDraw.getConstantState())) {
    //write your code.
} else {
    //write your code.
}
Addon.mahesh
fuente
-2

Cuando usa el equals()método, se usa para comparar el contenido. debe intentar ==comparar dos objetos.

public void MyClick(View view)
{
 Drawable fDraw = view.getBackground();
 Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);

  if( fDraw == sDraw )
  {
   // Coming
  }
}
Lucifer
fuente
entonces es posible que no lo sean ==, ¡lo son! =
Lucifer
pero se refieren a la misma imagen de los recursos
Roshan Jha
Solo necesito verificar si son iguales o no, ¿no hay ningún método disponible para eso?
Roshan Jha
ya, tengo su requisito, por favor venga a la sala de chat
Lucifer
1
== Compara si este es el mismo objeto, que no lo es el 99,99999999% del tiempo.
paulgavrikov