Androidで2つのドローアブルを比較する


回答:


150

https://stackoverflow.com/a/36373569/1835650を更新します

getConstantState()がうまく機能しない

比較する別の方法があります:

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

mRemeberPwdであるImageView。この例では。を使用している場合はTextViewgetBackground().getConstantState代わりにを使用してください。


3
このソリューションは機能し、Drawableをビットマップに変換して比較することを回避するため、より優れています。
ブラジ2013

2
常にではない:WallpaperManager.getInstance(this).getFastDrawable()。getConstantState()はnullです。
ポールガヴリコフ2013年

これを回答として受け入れ、自分の回答を変更することは申し訳ありません。答えとしてこれを確認してください。
Roshan Jha

おかげで、これが最良の答えです
satyres 2014

8
このコードは5.0未満のデバイスで問題なく機能しますが、5.0デバイスで使用するとエラーが発生します。この方法がAndroid 5.0以上で機能するかどうかは誰でも確認できます
Phil3992

41

getConstantState()単独で依存すると、偽陰性になる可能性があります

私が取ったアプローチは、最初のインスタンスで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でコードをテストしてください
Patrick

良い発見!ただし、BitmapDrawable.getBitmap()のドキュメントによると、getBitmap()がnullになる可能性があるため、これも確認する必要があるかもしれません
PhilLab

この答えは真実であり、私は数時間デバッグした後、getConstantState()のみのコーディングでそれを確認しました。
Raghav Satyadev 2017

安全で、するために、setBoundsそしてdraw代わりに、元のコピーにstackoverflow.com/a/25462223/1916449
arekolek

これは、色付きのBitmapDrawablesでは機能しません(setTintMode / setTint / setTintListを参照)。ビットマップはバイトごとに同じにすることができますが、色合いのプロパティは異なります。Android SDKはティントプロパティのゲッターを提供しないため、ティントされたドローアブルで動作させる方法がない場合があります。
Theo

12

私の質問は、2つのドローアブルを比較することだけでしたが、2つのドローアブルを直接比較する方法はありませんでしたが、私のソリューションでは、ドローアブルをビットマップに変更してから2つのビットマップを比較し、それが機能しています。

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

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

ドローアブルのタイプに応じてドローアブルを比較するために、私はあなたにそれを提案しました。
jeet

1
次のようなビットマップを比較することもできます:stackoverflow.com/a/7696320/317889
HGPB

2
これは非常に重いので、ビットマップのリサイクルを検討しないと、OutOfMemoryErrorが発生します。
ポールガヴリコフ2013年

2
なぜビットマップをポインタ等価(==)と比較できるのですか?Bitmap.equals()が必要になると思います。
Ellen Spertus、2014

@espertusそうです。私はドローアブルオブジェクトに同じ問題を使用しましたが、ビットマップオブジェクトを==にした理由がわかりません。この基本を指摘してくれてありがとう。
Roshan Jha

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 of imageviewとid of を比較したい人のためのものdrawable.xxxです。


実際にこれはうまくいきますが、なぜ他の可能性がないのかとちょっとショックですT_T!
エラー1337

4

Android 5のソリューション:

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

4

getDrawable(int)は非推奨になりました。getDrawable(context、R.drawable.yourimageid)を使用してください

2つの背景を比較するには

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

2
これはAndroid 5の奇妙なバグを修正するための魅力のように機能しました。私のコードでは、実際のドローアブルはcontext.getResources().getDrawable(R.drawable.***)Android 6以降では返されましたが、Android 5では返されませんでした。
Jose_GD 2018

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

または、2つの描画可能な引数を取り、ブール値を返すメソッドを準備します。その方法では、ドローアブルをバイトに変換して比較できます。

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

はい、成功しませんでした。ドローアブルをビットマップに変換してからバイトに変換することについても同じように考えています。試してみましょう。努力をありがとう
Roshan Jha

動作しません、テストしましたか?何か問題があるかもしれませんが、2つのドローアブルを直接比較できませんか?
Roshan Jha

e.g R.drawable.abc両方のパラメーターとして同じドローアブルを渡してみましたか?
waqaslam

こんにちはワカ、あなたのメソッドをもう一度チェックして、それが機能しているかどうかをもう一度教えてください、私が何か間違ったことをしているのにそれが機能しなかった可能性があるかもしれませんが、2つのドローアブルを比較する方法についての私の質問の定義を変更します。ビットマップにドローアブルすると、バイトはビットマップとバイトを比較しますが、これは私の要件ではありません。ドローアブルメソッドをチェックすると、メソッド.equals(object)が存在するため、直接動作するはずだと思いましたが、そうではありませんでした。以下では、ドローアブルをビットマップに変換し、2つのビットマップを比較しています。
Roshan Jha

2

わかりました、私はこれのための究極の解決策を見つけたと思います。AppCompatとその仲間のために、提供されるドローアブルはさまざまな形式で膨らまされる場合があるため、十分ではありません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

私はすでにここで同様のトピックについて答えました:ImageViewでドローアブルのIDを取得します。このアプローチは、カスタムでリソースIDを指定してビューにタグを付けることに基づいていますLayoutInflater。単純なライブラリTagViewによってプロセス全体が自動化されますます。

その結果、IDだけで2つのドローアブルを比較できます。

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

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

2つのドローアブルを直接比較する場合は、次のコードを使用します

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()、内容を比較するために使用される方法を。==2つのオブジェクトを比較してみてください。

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

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

その場合、それらは==ではない可能性があります。==、それらは!=
Lucifer

しかし、彼らはリソースからの同じ画像を参照しています
Roshan Jha

それらが等しいかどうかを確認する必要があるだけですか?そのために利用できる方法はありませんか?
Roshan Jha

ya、私はあなたの要件を得た、チャットルームに来てください
ルシファー

1
==これが同じオブジェクトであるかどうかを比較します。これは99.99999999%の時間ではありません。
ポールガヴリコフ2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.