比较Android中的两个drawable


Answers:


150

更新 https://stackoverflow.com/a/36373569/1835650

getConstantState()效果不佳

还有另一种比较方法:

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

mRemeberPwdImageView在本实施例中。如果您使用TextView,请getBackground().getConstantState改用。


3
此解决方案有效且更好,因为它避免了将Drawable转换为Bitmap并进行比较。
Braj 2013年

2
并非总是如此:WallpaperManager.getInstance(this).getFastDrawable()。getConstantState()为null。
paulgavrikov

很抱歉延迟接受此答案并更改我自己的答案,因为这似乎是更好的选择(也支持更多投票:))。因此,将其作为答案。
Roshan Jha 2014年

谢谢,这是最好的答案
satyres 2014年

8
此代码在低于5.0的设备上很好用,但是在5.0设备上使用它会出错,任何人都可以确认此方法在android 5.0或更高版本上是否有效
Phil3992 2014年

41

依靠getConstantState()单独可导致假阴性

我采用的方法是尝试在第一个实例中比较ConstantState,但是如果该检查失败,则退回到Bitmap比较中。

这在所有情况下都应适用(包括不是资源的图像),但请注意,这会占用大量内存。

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

这是100%正确,需要更多投票!亲爱的,在立即依靠getConstantState()比较之前,请使用已知的Drawables测试您的代码
Patrick

好发现!但是根据BitmapDrawable.getBitmap()的文档,getBitmap()可能为null,因此您可能也要检查一下
PhilLab

这个答案是正确的,我已经调试了几个小时,仅使用getConstantState()进行编码。
Raghav Satyadev'7

为了安全起见,setBoundsdraw在副本而不是原始的stackoverflow.com/a/25462223/1916449
arekolek

这不适用于有色BitmapDrawables(请参见setTintMode / setTint / setTintList)。位图可以是相同的字节,但具有不同的色调属性。由于Android SDK不为着色属性提供任何吸气剂,因此可能没有办法使其与着色可绘制对象一起使用。
西奥,

12

我的问题是只比较两个可绘制对象,我试过但无法获得直接比较两个可绘制对象的任何方法,但是对于我的解决方案,我将可绘制对象更改为位图,然后比较两个位图,这是可行的。

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

if(bitmap == bitmap2)
    {
        //Code blcok
    }

多数民众赞成在我建议你,根据可绘制的类型比较可绘制。
jeet 2012年

1
您可能还需要比较这样的位图:stackoverflow.com/a/7696320/317889
HGPB 2013年

2
这非常重,因此请考虑回收位图,否则您将最终遇到OutOfMemoryError!
paulgavrikov

2
为什么可以比较具有指针相等性(==)的位图?我希望需要Bitmap.equals()。
Ellen Spertus

@espertus你是对的。我对可绘制对象使用了同样的问题,不知道为什么答案中的位图对象转向==。任何方式都感谢您指出这一基本知识。
Roshan Jha 2014年

9

适用于SDK 21+

这适用于SDK -21

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

对于SDK +21 android 5.将带有标签的可绘制ID设置为imageview

img.setTag(R.drawable.xxx);

并像这样比较

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

此解决方案适用于谁希望将drawableID imageview与的ID 进行比较drawable.xxx


其实这可行,但是我为为什么没有其他可能性T_T感到震惊。
error1337


4

getDrawable(int)现在已弃用。使用 getDrawable(context,R.drawable.yourimageid)

比较两个背景

Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());

2
这就像在Android 5上修复一个奇怪错误的一种魅力。在我的代码中,实际的drawable context.getResources().getDrawable(R.drawable.***)在Android 6+上返回,但在Android 5上却没有。通过这一小的更改,我可以完美地比较所有Android版本的背景drawable
Jose_GD

3

也许可以这样尝试:

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

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

或准备一个使用两个可绘制参数并返回布尔值的方法。在这种方法中,您可以将drawable转换为字节并进行比较,

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 2012年

是的,在未获得成功之后,我也在考虑将可绘制对象转换为位图然后转换为字节,让我尝试一下,谢谢您的努力
Roshan Jha 2012年

不起作用,您测试了吗?可能有问题,不能直接比较两个可绘制对象吗?
Roshan Jha

您是否尝试过e.g R.drawable.abc为两个参数传递相同的drawable ?
waqaslam 2012年

你好,waqas,再次检查你的方法,然后再次告诉它是否有效,可能我做错了什么但是没用,但是随后改变了我的问题的定义,即如何比较两个可绘制对象。可绘制到位图,然后字节将比较位图和字节,这不是我的要求。如果我们检查可绘制方法,则有方法.equals(object),所以我认为它应该直接工作,但没有。请检查我的回答下面,我将drawable转换为位图,然后比较两个位图,它正在工作。
Roshan Jha 2012年

2

好的,我想我已经找到了最终的解决方案。由于AppCompat和朋友的关系,所提供的drawable有时会以不同的形式膨胀,因此这样做还不够getResources().getBitmap(R.drawable.my_awesome_drawable)

因此,为了获得与视图提供的类型和形式相同的可绘制实例,可以执行以下操作:

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

这在进行测试时很有用。但是,我不建议在生产中这样做。如果需要,可能需要额外的缓存以避免过多的反射。

对于Expresso测试,您可以很好地使用它:

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

要么

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

在必须声明此帮助程序类之前,请执行以下操作:

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

}



0

通过扩展@vaughandroid的答案,以下Matcher可用于着色的Vector Drawable。您必须提供用于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) {

        }
    };
}

0

比较2个可绘制对象:

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

如果您Drawable.toBitmap(...)在这里找不到,它是Drawable.kt


-1

如果要直接比较两个可绘制对象,则使用以下代码

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.
}

-2

使用equals()方法时,它用于比较内容。您应该尝试==比较两个对象。

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

  if( fDraw == sDraw )
  {
   // Coming
  }
}

那么它们可能不是==,而是!=
路西法

但他们从资源中引用的是同一张图片
Roshan Jha 2012年

我只需要检查它们是否相等?没有任何可用的方法吗?
Roshan Jha 2012年

是的,我有您的要求,请到聊天室
Lucifer '02

1
==比较是否是同一对象,而不是99.99999999%的时间。
paulgavrikov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.