【问题标题】:Comparing two drawables in android比较android中的两个drawable
【发布时间】:2012-02-03 07:38:53
【问题描述】:

如何比较两个可绘制对象,我正在这样做但没有任何成功

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

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

【问题讨论】:

标签: android


【解决方案1】:

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

getConstantState() 效果不佳

还有一种比较方式:

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

mRemeberPwd 在此示例中是 ImageView。如果您使用的是TextView,请改用getBackground().getConstantState

【讨论】:

  • 此解决方案效果更好,因为它避免了将 Drawable 转换为位图并进行比较。
  • 并非总是如此:WallpaperManager.getInstance(this).getFastDrawable().getConstantState() 为空。
  • 很抱歉迟到了接受这个作为答案并改变了我自己的答案,因为这似乎是更好的选择(还有更多的赞成票:))。所以检查这个作为答案。
  • 谢谢,这是最好的答案
  • 此代码在低于 5.0 的设备上运行良好,但我在 5.0 设备上使用它时出错,任何人都可以确认此方法在 android 5.0 或更高版本上是否有效
【解决方案2】:

仅依靠getConstantState() 可以产生false negatives

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

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

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() 比较之前使用已知的 Drawable 测试您的代码
  • 很好的发现!但是根据 BitmapDrawable.getBitmap() 的文档,getBitmap() 可能为空,所以你可能也想检查一下
  • 这个答案是正确的,我在调试几个小时后只使用 getConstantState() 的编码进行了检查。
  • 为了安全起见,setBoundsdraw 使用副本而不是原始 stackoverflow.com/a/25462223/1916449
  • 这不适用于着色的 BitmapDrawables(参见 setTintMode / setTint / setTintList)。位图可以是相同的字节,但具有不同的色调属性。由于 Android SDK 不为 tint 属性提供任何 getter,因此可能无法使其与着色的可绘制对象一起使用。
【解决方案3】:

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

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

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

【讨论】:

  • 这就是我的建议,根据可绘制对象的类型来比较可绘制对象。
  • 您可能还想像这样比较位图:stackoverflow.com/a/7696320/317889
  • 这很重,所以考虑回收位图,否则你会遇到 OutOfMemoryError!
  • 为什么可以将位图与指针相等 (==) 进行比较?我希望 Bitmap.equals() 需要。
  • @espertus 你是对的。我对可绘制对象使用了相同的问题,不知道为什么在回答中将位图对象转向 ==。任何方式感谢您指出这一基本原则。
【解决方案4】:

适用于 SDK 21+

这适用于 SDK -21

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

适用于 SDK +21 android 5。 使用标签将drawable id设置为imageview

img.setTag(R.drawable.xxx);

然后像这样比较

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

此解决方案适用于想要将imageviewdrawable id 与drawable.xxx 的id 进行比较的人。

【讨论】:

  • 实际上这行得通,但我有点震惊为什么没有其他可能性T_T!
【解决方案5】:

或许可以这样试试:

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

【讨论】:

  • 检查更新的答案。如果仍然不起作用,请检查您的可绘制对象。或者尝试传递相同的可绘制对象来验证代码的功能
  • 是的,在没有成功之后,我想将drawables转换为位图然后转换为字节,让我试试,谢谢你的努力
  • 不工作,你测试了吗?可能有什么问题,我们不能直接比较两个drawable吗?
  • 您是否尝试过为e.g R.drawable.abc 传递相同的drawable 作为两个参数?
  • 你好 waqas,再次检查您的方法,然后再次判断它是否有效,可能是我做错了什么但没有奏效,但它改变了我的问题的定义如何比较两个可绘制对象。将可绘制对象更改为位图,然后字节将比较位图和字节,这不是我的要求。如果我们检查可绘制方法,那么有方法 .equals(object) 所以我认为它应该直接工作,但确实如此不。好吧,您可以在下面查看我的答案,我正在将可绘制对象转换为位图,然后比较两个位图并且它正在工作。
【解决方案6】:

Android 5 的解决方案:

 if(image.getDrawable().getConstantState().equals(image.getContext().getDrawable(R.drawable.something).getConstantState()))

【讨论】:

    【解决方案7】:

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

    比较两个背景

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

    【讨论】:

    • 这就像修复 Android 5 上的一个奇怪错误的魅力。在我的代码中,实际的可绘制对象在 Android 6+ 上以 context.getResources().getDrawable(R.drawable.***) 返回,但在 Android 5 上却没有。有了这个小更改我可以完美地比较所有 Android 版本的背景可绘制对象
    【解决方案8】:

    好的,我想我已经找到了最终解决方案。由于 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);
            }
         }
      }
    
    }
    

    【讨论】:

      【解决方案9】:

      比较 2 个可绘制对象:

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

      如果你找不到Drawable.toBitmap(...)这里是Drawable.kt

      【讨论】:

      • sdk 31,我使用这个toBitmap().sameAs 来比较基于两个可绘制对象的复选框选中或未选中..
      【解决方案10】:

      使用 getTag() 和 setTag() 进行比较

      【讨论】:

        【解决方案11】:

        我已经在这里回答了类似的话题:Get the ID of a drawable in ImageView。 该方法基于在自定义 LayoutInflater 中使用指定资源 id 标记视图。整个过程由一个简单的库TagView自动化。

        因此,您可以仅通过 id 比较两个可绘制对象:

        TagViewUtils.getTag(view, ViewTag.VIEW_BACKGROUND.id) == R.drawable.twt_hover
        

        【讨论】:

          【解决方案12】:

          扩展来自@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) {
          
                  }
              };
          }
          

          【讨论】:

            【解决方案13】:

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

            可绘制 fDraw = getResources().getDrawable(R.drawable.twt_hover);

            可绘制 sDraw = getResources().getDrawable(R.drawable.twt_hover);

            if (fDraw.getConstantState().equals(sDraw.getConstantState())) {
                //write your code.
            } else {
                //write your code.
            }
            

            【讨论】:

              【解决方案14】:

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

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

              【讨论】:

              • 那么他们可能不是==,他们是!=
              • 但它们从资源中引用相同的图像
              • 我只需要检查它们是否相等?没有任何可用的方法吗?
              • 是的,我有你的要求,请来聊天室
              • == 比较这是否是同一个对象,在 99.99999999% 的时间里它不是。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-06-26
              • 2012-04-04
              • 1970-01-01
              • 2013-08-26
              相关资源
              最近更新 更多