Vergleich von zwei Drawables in Android

90

Wie man zwei Drawables vergleicht, mache ich so, habe aber keinen Erfolg

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
quelle

Antworten:

150

Aktualisieren Sie https://stackoverflow.com/a/36373569/1835650

getConstantState () funktioniert nicht gut

Es gibt noch einen anderen Weg zu vergleichen:

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

mRemeberPwdist ein ImageViewin diesem Beispiel. Wenn Sie a verwenden TextView, verwenden Sie getBackground().getConstantStatestattdessen.

Mejonzhan
quelle
3
Diese Lösung funktioniert und ist besser, da sie die Konvertierung von Drawable in Bitmap und den Vergleich vermeidet.
Braj
2
Nicht immer: WallpaperManager.getInstance (this) .getFastDrawable (). GetConstantState () ist null.
Paulgavrikov
Es tut mir leid, dass ich dies zu spät als Antwort akzeptiert und meine eigene Antwort geändert habe, da dies eine bessere Option zu sein scheint (und auch mehr Upvotes :)). Überprüfen Sie dies als Antwort.
Roshan Jha
Danke, das ist die beste Antwort
Satyrn
8
Dieser Code funktioniert hervorragend auf Geräten unter 5.0, aber ich
erhalte
40

Sich getConstantState()allein zu verlassen kann zu falschen Negativen führen .

Der Ansatz, den ich gewählt habe, besteht darin, zunächst den ConstantState zu vergleichen, aber auf einen Bitmap-Vergleich zurückzugreifen, wenn diese Prüfung fehlschlägt.

Dies sollte in allen Fällen funktionieren (einschließlich Bilder, die keine Ressourcen sind), aber beachten Sie, dass es speicherhungrig ist.

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
quelle
Dies ist 100% wahr und benötigt mehr Upvotes! Leute, bitte testen Sie Ihren Code mit bekannten Drawables, bevor Sie sich sofort auf den getConstantState()Vergleich verlassen
Patrick
Gute Ergebnisse! Laut der Dokumentation von BitmapDrawable.getBitmap () kann getBitmap () jedoch null sein, sodass Sie dies möglicherweise auch überprüfen möchten
PhilLab
Diese Antwort ist wahr und ich habe das nach mehrstündigem Debuggen mit der Codierung von nur getConstantState () überprüft.
Raghav Satyadev
Um sicher zu gehen setBoundsund drawauf einer Kopie anstelle des Originals stackoverflow.com/a/25462223/1916449
arekolek
Dies funktioniert nicht mit getönten BitmapDrawables (siehe setTintMode / setTint / setTintList). Die Bitmaps können Byte für Byte identisch sein, jedoch unterschiedliche Farbtöneigenschaften aufweisen. Da das Android SDK keine Getter für die Tönungseigenschaften bietet, gibt es möglicherweise keine Möglichkeit, es mit getönten Drawables zum Laufen zu bringen.
Theo
12

Meine Frage war, nur zwei Drawables zu vergleichen. Ich habe versucht, aber keine Methode zu finden, die zwei Drawables direkt vergleicht. Für meine Lösung habe ich Drawable in Bitmap geändert und dann zwei Bitmaps verglichen, und das funktioniert.

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

if(bitmap == bitmap2)
    {
        //Code blcok
    }
Roshan Jha
quelle
Das habe ich Ihnen vorgeschlagen, um Drawable nach Art des Drawable zu vergleichen.
Jeet
1
Vielleicht möchten Sie auch Bitmaps wie diese vergleichen: stackoverflow.com/a/7696320/317889
HGPB
2
Das ist sehr schwer, also erwägen Sie, Bitmaps zu recyceln, sonst geraten Sie in einen OutOfMemoryError!
Paulgavrikov
2
Warum können Sie Bitmaps mit Zeigergleichheit (==) vergleichen? Ich würde erwarten, dass Bitmap.equals () benötigt wird.
Ellen Spertus
@espertus Du hast recht. Ich habe dasselbe in Frage für zeichnbare Objekte verwendet. Ich weiß nicht, warum ich mich für ein Bitmap-Objekt als Antwort an == gewandt habe. Wie auch immer, danke, dass Sie auf dieses Grundlegende hingewiesen haben.
Roshan Jha
9

für SDK 21+

Dies funktioniert in SDK -21

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

für SDK +21 Android 5. Setzen Sie die Drawable ID auf Imageview mit Tag

img.setTag(R.drawable.xxx);

und vergleiche so

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

Diese Lösung ist für diejenigen drawablegedacht , die die ID von imageviewmit der ID von vergleichen möchten drawable.xxx.

um versteckte
quelle
Eigentlich funktioniert das, aber ich bin schockiert, warum es keine andere Möglichkeit gibt, T_T!
error1337
4

Die Lösung für Android 5:

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

getDrawable (int) ist jetzt veraltet. Verwenden Sie getDrawable (Kontext, R.drawable.yourimageid)

Zwei Hintergründe vergleichen

Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());
RAJESH KUMAR ARUMUGAM
quelle
2
Dies funktionierte wie ein Zauber, um einen seltsamen Fehler unter Android 5 zu beheben. In meinem Code wurde das eigentliche Drawable mit context.getResources().getDrawable(R.drawable.***)Android 6+ zurückgegeben, aber nicht mit Android 5. Mit dieser kleinen Änderung kann ich Hintergrund-Drawables für alle Android-Versionen fehlerfrei vergleichen
Jose_GD
3

Vielleicht versuchen Sie es so:

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

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

oder bereiten Sie eine Methode vor, die zwei zeichnbare Argumente verwendet und einen Booleschen Wert zurückgibt. In dieser Methode können Sie Drawable in Bytes konvertieren und vergleichen,

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
quelle
Überprüfen Sie die aktualisierte Antwort. Wenn es immer noch nicht funktioniert, überprüfen Sie Ihre Drawables. Oder versuchen Sie, dieselben Zeichen zu übergeben, um die Funktionalität des Codes zu überprüfen
waqaslam
Ja, nachdem ich keinen Erfolg hatte, denke ich dasselbe für das Konvertieren von Drawables in Bitmap und dann in Byte. Lassen Sie mich das versuchen. Vielen Dank für Ihre Bemühungen
Roshan Jha,
funktioniert nicht, hast du es getestet? Vielleicht stimmt etwas nicht. Können wir nicht zwei Drawables direkt vergleichen?
Roshan Jha
Haben Sie versucht, dasselbe Zeichen für e.g R.drawable.abcbeide Parameter zu übergeben?
Waqaslam
hallo waqas, überprüfe deine Methode noch einmal und sage dann noch einmal, ob sie funktioniert oder nicht, es ist möglich, dass ich etwas falsch mache, aber das hat nicht funktioniert, aber dann ändert sich die Definition meiner Frage, wie man zwei Drawables vergleicht Drawable to Bitmap und dann Bytes vergleichen dann Bitmap und Bytes, die nicht meine Anforderung sind. Wenn wir Drawable-Methoden überprüfen, gibt es die Methode .equals (Objekt), so dass ich dachte, es sollte direkt funktionieren, aber es hat nicht funktioniert. Nun, Sie können meine Antwort überprüfen Unten konvertiere ich Drawable in Bitmap und vergleiche dann zwei Bitmaps und es funktioniert.
Roshan Jha
2

Ok, ich denke, ich habe die ultimative Lösung dafür gefunden. Aufgrund von AppCompat und Freunden wird das bereitgestellte Zeichen manchmal in verschiedenen Formen aufgeblasen, sodass dies nicht ausreichtgetResources().getBitmap(R.drawable.my_awesome_drawable) .

Um eine zeichnbare Instanz des gleichen Typs und der gleichen Form wie in der Ansicht zu erhalten, können Sie Folgendes tun:

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

Dies ist nützlich, wenn Sie Tests durchführen. Ich würde dies jedoch nicht in der Produktion empfehlen. Wenn nötig, ist zusätzliches Caching wünschenswert, um zu viele Reflexionen zu vermeiden.

Für Expresso-Tests können Sie dies ganz gut verwenden:

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

oder

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

Bevor Sie diese Hilfsklasse deklarieren müssen:

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
quelle
1

Verwenden Sie zum Vergleich getTag () und setTag ()

Pooja Akshantal
quelle
0

Ich habe hier bereits zu einem ähnlichen Thema geantwortet: Holen Sie sich die ID eines Zeichens in ImageView . Der Ansatz basiert auf dem Kennzeichnen einer Ansicht mit einer angegebenen Ressourcen-ID im benutzerdefinierten Bereich LayoutInflater. Der gesamte Prozess wird durch eine einfache Bibliothek TagView automatisiert .

Infolgedessen können Sie zwei Drawables nur anhand ihrer IDs vergleichen:

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

Wenn Sie die Antwort von @vaughandroid erweitern, funktioniert der folgende Matcher für ein getöntes Vector Drawable. Sie müssen den Farbton angeben, der für das Drawable verwendet wurde.

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
quelle
0

Vergleiche 2 zeichnbare:

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

Wenn Sie Drawable.toBitmap(...)hier nicht finden können , ist es Drawable.kt

Benny
quelle
-1

Wenn Sie zwei Zeichen direkt vergleichen möchten, verwenden Sie den folgenden Code

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
quelle
-2

Wenn Sie eine equals()Methode verwenden, wird diese zum Vergleichen des Inhalts verwendet. Sie sollten versuchen ==, zwei Objekte zu vergleichen.

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

  if( fDraw == sDraw )
  {
   // Coming
  }
}
Luzifer
quelle
dann ist es möglich, dass sie nicht == sind, sie sind! =
Luzifer
aber sie beziehen sich auf das gleiche Bild aus Ressourcen
Roshan Jha
Ich muss nur überprüfen, ob sie gleich sind oder nicht - dafür steht keine Methode zur Verfügung.
Roshan Jha
ya, ich habe deine Anforderung, bitte komm in den Chatraum
Luzifer
1
== Vergleicht, ob dies dasselbe Objekt ist, was in 99,99999999% der Fälle nicht der Fall ist.
Paulgavrikov