Comparaison de deux drawables sous Android

90

Comment comparer deux drawables, je fais comme ça mais sans succès

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
la source

Réponses:

150

Mettre à jour https://stackoverflow.com/a/36373569/1835650

getConstantState () ne fonctionne pas bien

Il existe une autre façon de comparer:

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

mRemeberPwdest un ImageViewdans cet exemple. Si vous utilisez unTextView , utilisez à la getBackground().getConstantStateplace.

Mejonzhan
la source
3
Cette solution fonctionne et c'est mieux car elle évite de convertir Drawable en Bitmap et de comparer.
Braj
2
Pas toujours: WallpaperManager.getInstance (this) .getFastDrawable (). GetConstantState () est nul.
paulgavrikov
Désolé d'avoir accepté tardivement cette réponse et d'avoir changé ma propre réponse car cela semble une meilleure option (et plus de votes positifs également :)). Donc vérifier ceci comme réponse.
Roshan Jha
Merci, c'est la meilleure réponse
satyres
8
Ce code fonctionne très bien sur les appareils inférieurs à 5,0 mais j'obtiens des erreurs en l'utilisant sur un appareil 5.0, quelqu'un peut-il confirmer que cette méthode fonctionne ou non sur Android 5.0 ou supérieur
Phil3992
41

S'appuyer getConstantState()seul peut entraîner de faux négatifs .

L'approche que j'ai adoptée consiste à essayer de comparer le ConstantState dans un premier temps, mais à revenir sur une comparaison Bitmap si cette vérification échoue.

Cela devrait fonctionner dans tous les cas (y compris les images qui ne sont pas des ressources) mais notez que la mémoire est gourmande.

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
la source
C'est 100% vrai et nécessite plus de votes positifs! Les gens, veuillez tester votre code avec des Drawables connus avant de vous fier immédiatement à la getConstantState()comparaison
Patrick
Bonnes découvertes! Mais selon la documentation de BitmapDrawable.getBitmap (), getBitmap () peut être nul, vous voudrez peut-être vérifier cela également
PhilLab
Cette réponse est vraie et j'ai vérifié cela après plusieurs heures de débogage avec le codage de getConstantState ().
Raghav Satyadev
Pour être sûr, setBoundset drawsur une copie au lieu de l'original stackoverflow.com/a/25462223/1916449
arekolek
Cela ne fonctionne pas avec les BitmapDrawables teintés (voir setTintMode / setTint / setTintList). Les bitmaps peuvent être identiques octet pour octet mais avec des propriétés de teinte différentes. Étant donné que le SDK Android ne fournit aucun getters pour les propriétés de teinte, il peut ne pas être possible de le faire fonctionner avec des dessinables teintés.
Theo
12

Ma question était de simplement comparer deux drawables, j'ai essayé mais je n'ai pas pu obtenir de méthode qui compare directement deux drawables, mais pour ma solution, j'ai changé drawable en bitmap, puis j'ai comparé deux bitmaps et cela fonctionne.

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

if(bitmap == bitmap2)
    {
        //Code blcok
    }
Roshan Jha
la source
c'est ce que je vous suggérais, pour comparer drawable selon le type de drawable.
jeet
1
Vous pouvez également comparer des bitmaps comme celui-ci: stackoverflow.com/a/7696320/317889
HGPB
2
C'est très lourd, alors pensez à recycler les Bitmaps ou vous vous retrouverez dans une OutOfMemoryError!
paulgavrikov
2
Pourquoi pouvez-vous comparer des bitmaps avec l'égalité de pointeur (==)? Je m'attendrais à ce que Bitmap.equals () soit nécessaire.
Ellen Spertus
@espertus Vous avez raison. J'ai utilisé la même chose en question pour les objets dessinables je ne sais pas pourquoi je me suis tourné vers == pour l'objet bitmap en réponse. Tous les moyens merci d'avoir souligné cette base.
Roshan Jha
9

pour SDK 21+

cela fonctionne dans le SDK -21

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

pour SDK +21 android 5. définissez l'ID drawable sur imageview avec tag

img.setTag(R.drawable.xxx);

et comparer comme ça

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

cette solution est pour ceux qui veulent comparer drawableid of imageviewavec id of drawable.xxx.

cacher
la source
En fait, cela fonctionne, mais je suis un peu choqué de voir qu'il n'y a pas d'autre possibilité T_T!
error1337
4

La solution pour Android 5:

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

getDrawable (int) est désormais obsolète. Utilisez getDrawable (context, R.drawable.yourimageid)

Pour comparer deux arrière-plans

Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());
RAJESH KUMAR ARUMUGAM
la source
2
Cela a fonctionné comme un charme pour corriger un bug étrange sur Android 5. Dans mon code, le dessin réel a été renvoyé avec context.getResources().getDrawable(R.drawable.***)Android 6+ mais n'était pas sur Android 5. Avec ce petit changement, je peux comparer parfaitement les dessinables d'arrière-plan de toutes les versions d'Android.
Jose_GD
3

peut-être essayez-le de cette façon:

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

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

ou préparez une méthode qui prend deux arguments dessinables et retourne boolean. Dans cette méthode, vous pouvez convertir drawable en octets et comparer,

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
la source
vérifiez la réponse mise à jour. si cela ne fonctionne toujours pas, vérifiez vos drawables. Ou essayez de passer les mêmes drawables pour vérifier la fonctionnalité du code
waqaslam
oui, après avoir échoué, je pense la même chose pour la conversion de drawables en bitmap puis en octet, laissez-moi essayer celui-là, merci pour vos efforts
Roshan Jha
ne fonctionne pas, l'avez-vous testé? peut-être qu'il y a quelque chose qui ne va pas, ne pouvons-nous pas comparer directement deux drawables?
Roshan Jha
avez-vous essayé en passant le même dessinable pour e.g R.drawable.abcles deux paramètres?
waqaslam
bonjour waqas, vérifiez à nouveau votre méthode et dites à nouveau si elle fonctionne ou non, il est peut-être possible que je fasse quelque chose de mal mais cela n'a pas fonctionné, mais cela change la définition de ma question: comment comparer deux dessinables. drawable à bitmap, puis les octets compareront le bitmap et les octets qui ne sont pas mes besoins.Si nous vérifions les méthodes dessinables, il y a la méthode .equals (objet), donc j'ai pensé que cela devrait fonctionner directement, mais ce n'est pas le cas.Eh bien, vous pouvez vérifier ma réponse ci-dessous, je convertis drawable en bitmap, puis je compare deux bitmaps et cela fonctionne.
Roshan Jha
2

Ok, je pense avoir trouvé la solution ultime pour ça. En raison d'AppCompat et de ses amis, le dessin fourni est parfois gonflé sous différentes formes, il ne suffit donc pas de le fairegetResources().getBitmap(R.drawable.my_awesome_drawable) .

Donc, pour obtenir une instance dessinable du même type et de la même forme que celle fournie par la vue, on peut faire ceci:

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);
    }
}

Ceci est utile lors des tests. Cependant, je ne recommanderais pas de faire cela en production. Si vous en avez besoin, une mise en cache supplémentaire serait souhaitable pour éviter de trop réfléchir.

Pour les tests Expresso, vous pouvez utiliser ceci très bien:

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

ou

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

Avant de devoir déclarer cette classe d'assistance:

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
la source
1

Utilisez getTag () et setTag () pour la comparaison

Pooja Akshantal
la source
0

J'ai déjà répondu sur le même sujet ici: Obtenez l'ID d'un dessinable dans ImageView . L'approche est basée sur le marquage d'une vue avec un identifiant de ressource spécifié dans le fichier personnalisé LayoutInflater. L'ensemble du processus est automatisé par une simple bibliothèque TagView .

En conséquence, vous pouvez comparer deux drawables uniquement par leurs identifiants:

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

En développant la réponse de @vaughandroid, le Matcher suivant fonctionne pour un Vector Drawable teinté. Vous devez fournir la teinte qui a été utilisée pour le 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) {

        }
    };
}
Jérémie Eikenberg
la source
0

Comparez 2 tirables:

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

Si vous ne trouvez pas Drawable.toBitmap(...)ici, c'est Drawable.kt

Benny
la source
-1

si vous souhaitez comparer directement deux dessinables, utilisez le code suivant

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

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

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

Lorsque vous utilisez la equals()méthode, elle est utilisée pour comparer le contenu. vous devriez essayer ==de comparer deux objets.

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

  if( fDraw == sDraw )
  {
   // Coming
  }
}
Lucifer
la source
alors il est possible qu'ils ne soient pas ==, ils le sont! =
Lucifer
mais ils se réfèrent à la même image à partir des ressources
Roshan Jha
J'ai juste besoin de vérifier s'ils sont égaux ou non? Aucune méthode disponible pour cela?
Roshan Jha
oui, j'ai votre demande, veuillez venir à la salle de chat
Lucifer
1
== Compare s'il s'agit du même objet, ce qui n'est pas 99,99999999% du temps.
paulgavrikov