Comparing two drawables in android
AndroidAndroid Problem Overview
How to compare two drawables, I am doing like this but not having any success
public void MyClick(View view)
{
Drawable fDraw = view.getBackground();
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if(fDraw.equals(sDraw))
{
//Not coming
}
}
Android Solutions
Solution 1 - Android
Update https://stackoverflow.com/a/36373569/1835650
getConstantState() works not well
There is another way to compare:
mRememberPwd.getDrawable().getConstantState().equals
(getResources().getDrawable(R.drawable.login_checked).getConstantState());
mRemeberPwd
is an ImageView
in this example. If you're using a TextView
, use getBackground().getConstantState
instead.
Solution 2 - Android
Relying on getConstantState()
alone can result in false negatives.
The approach I've taken is to try comparing the ConstantState in the first instance, but fall back on a Bitmap comparison if that check fails.
This should work in all cases (including images which aren't resources) but note that it is memory hungry.
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;
}
Solution 3 - Android
My question was for just comparing two drawables, I tried but could not get any method that directly compare two drawables,however for my solution i changed drawable to bitmap and then comparing two bitmaps and that is working.
Bitmap bitmap = ((BitmapDrawable)fDraw).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable)sDraw).getBitmap();
if(bitmap == bitmap2)
{
//Code blcok
}
Solution 4 - Android
for SDK 21+
this works in SDK -21
mRememberPwd.getDrawable().getConstantState().equals
(getResources().getDrawable(R.drawable.login_checked).getConstantState())
for SDK +21 android 5. set drawable id to imageview with tag
img.setTag(R.drawable.xxx);
and compare like this
if ((Integer) img.getTag() == R.drawable.xxx)
{
....your code
}
this solution is for who want to compare drawable
id of imageview
with id of drawable.xxx
.
Solution 5 - Android
perhaps try it in this way:
public void MyClick(View view)
{
Drawable fDraw = view.getBackground();
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if(fDraw.hashCode() == sDraw.hashCode())
{
//Not coming
}
}
or prepare a method which takes two drawable arguments and return boolean. In that method you may convert drawable into bytes and compare,
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;
}
Solution 6 - Android
The solution for Android 5:
if(image.getDrawable().getConstantState().equals(image.getContext().getDrawable(R.drawable.something).getConstantState()))
Solution 7 - Android
getDrawable(int) is Now Deprecated. Use getDrawable(context,R.drawable.yourimageid)
To Compare Two Backgrounds
Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());
Solution 8 - Android
Ok, I think I've found the ultimate solution for this. Because of AppCompat and friends, the drawable provided is sometimes inflated in different forms so it's not enough to do getResources().getBitmap(R.drawable.my_awesome_drawable)
.
So, in order to get a drawable instance of the same type and form as provided by the view one can do this:
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);
}
}
This is useful when doing tests. However, I would not recommend doing this in production. If you need to, extra caching would be desirable to avoid doing too much reflection.
For Expresso tests you can use this quite nicely:
onView(withDrawable(R.drawable.awesome_drawable))
.check(matches(isDisplayed()));
or
onView(withId(R.id.view_id))
.check(matches(withDrawable(R.drawable.awesome_drawable)));
Before you'll have to declare this helper class:
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);
}
}
}
}
Solution 9 - Android
> Use getTag() and setTag() for comparison
Solution 10 - Android
Compare 2 drawable:
drawable1.constantState == drawable2.constantState
|| drawable1.toBitmap().sameAs(drawable2.toBitmap())
If you can not find Drawable.toBitmap(...)
here is it Drawable.kt
Solution 11 - Android
I already answered on the similar topic here: Get the ID of a drawable in ImageView.
The approach is based on tagging a view with a specified resource id in the custom LayoutInflater
. Whole process is automated by a simple library TagView.
As a result, you can compare two drawables just by their ids:
TagViewUtils.getTag(view, ViewTag.VIEW_BACKGROUND.id) == R.drawable.twt_hover
Solution 12 - Android
Expanding on the answer from @vaughandroid the following Matcher works for a Vector Drawable that is tinted. You have to provide the tint that was used for the 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) {
}
};
}
Solution 13 - Android
if You want to directly compare two drawable then use following 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.
}
Solution 14 - Android
When you are using equals()
method it is used to compare the contents. you should try ==
for comparing two objects.
public void MyClick(View view)
{
Drawable fDraw = view.getBackground();
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if( fDraw == sDraw )
{
// Coming
}
}