Porównanie dwóch rysunków w systemie Android


Odpowiedzi:


150

Zaktualizuj https://stackoverflow.com/a/36373569/1835650

getConstantState () nie działa dobrze

Istnieje inny sposób porównania:

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

mRemeberPwdjest ImageVieww tym przykładzie. Jeśli używasz TextView, użyj getBackground().getConstantStatezamiast tego.


3
To rozwiązanie działa i jest lepsze, ponieważ pozwala uniknąć konwersji Drawable na Bitmap i porównania.
Braj

2
Nie zawsze: WallpaperManager.getInstance (this) .getFastDrawable (). GetConstantState () ma wartość null.
paulgavrikov

Przepraszam za późne zaakceptowanie tego jako odpowiedź i zmianę własnej odpowiedzi, ponieważ wydaje się to lepsza opcja (i więcej głosów pozytywnych :)). Więc sprawdzam to jako odpowiedź.
Roshan Jha,

Dzięki, to najlepsza odpowiedź
satyres

8
Ten kod działa świetnie na urządzeniach niższych niż 5.0, ale dostaję błędy podczas używania go na urządzeniu 5.0, czy ktoś może potwierdzić, że ta metoda działa, czy nie na
Androidzie

41

Poleganie getConstantState()wyłącznie na sobie może skutkować fałszywymi negatywami .

Podejście, które zastosowałem, polega na próbie porównania ConstantState w pierwszej kolejności, ale w przypadku niepowodzenia tego sprawdzenia należy wrócić do porównania Bitmap.

Powinno to działać we wszystkich przypadkach (w tym obrazy, które nie są zasobami), ale pamiętaj, że jest głodne pamięci.

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

To jest w 100% prawdziwe i wymaga więcej głosów pozytywnych! Ludzie, przetestuj swój kod za pomocą znanych Drawables, zanim natychmiast oprzyj się na getConstantState()porównaniu
avalancha

Dobre wyniki! Ale zgodnie z dokumentacją BitmapDrawable.getBitmap (), getBitmap () może być zerowe, więc warto to również sprawdzić
PhilLab

Ta odpowiedź jest prawdziwa i sprawdziłem to po kilku godzinach debugowania z kodowaniem tylko getConstantState ().
Raghav Satyadev,

Dla bezpieczeństwa setBoundsi drawna kopii zamiast oryginalnego stackoverflow.com/a/25462223/1916449
arekolek

Nie działa to z przyciemnionymi BitmapDrawables (zobacz setTintMode / setTint / setTintList). Mapy bitowe mogą mieć identyczny bajt po bajcie, ale mogą mieć różne właściwości tinty. Ponieważ zestaw SDK systemu Android nie zapewnia żadnych metod pobierających dla właściwości odcienia, może nie istnieć sposób, aby działał z przyciemnianymi elementami rysunkowymi.
Theo

13

Moje pytanie dotyczyło po prostu porównania dwóch rysunków, próbowałem, ale nie mogłem uzyskać żadnej metody, która bezpośrednio porównuje dwa rysunki, jednak w moim rozwiązaniu zmieniłem drawable na bitmap, a następnie porównałem dwie bitmapy i to działa.

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

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

to właśnie sugerowałem, aby porównać elementy do rysowania w zależności od ich rodzaju.
jeet

1
Możesz także porównać bitmapy w następujący sposób: stackoverflow.com/a/7696320/317889
HGPB

2
Jest to bardzo ciężkie, więc rozważ recykling bitmap, albo skończysz w OutOfMemoryError!
paulgavrikov

2
Dlaczego można porównać mapy bitowe z równością wskaźników (==)? Spodziewałbym się, że Bitmap.equals () będzie potrzebna.
Ellen Spertus

@espertus Masz rację. Użyłem tego samego w przypadku obiektów do rysowania. Nie wiem, dlaczego zmieniłem na == dla obiektu bitmapowego w odpowiedzi. Dzięki za wskazanie tego podstawowego.
Roshan Jha

9

dla SDK 21+

działa to w SDK -21

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

dla SDK +21 android 5. ustaw drawable id na imageview z tagiem

img.setTag(R.drawable.xxx);

i porównaj w ten sposób

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

to rozwiązanie jest dla tych, którzy chcą porównać drawableid imageviewz id z drawable.xxx.


Właściwie to działa, ale jestem trochę zszokowany, dlaczego nie ma innej możliwości T_T!
błąd 1337

4

Rozwiązanie dla Androida 5:

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

4

getDrawable (int) jest teraz przestarzałe. Użyj getDrawable (context, R.drawable.yourimageid)

Porównanie dwóch środowisk

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

2
To zadziałało jak urok, aby naprawić dziwny błąd w systemie Android 5. W moim kodzie rzeczywisty element do rysowania został zwrócony context.getResources().getDrawable(R.drawable.***)w systemie Android 6+, ale nie był w systemie Android 5. Dzięki tej niewielkiej zmianie mogę bezbłędnie porównywać elementy do rysowania w tle we wszystkich wersjach Androida.
Jose_GD

3

może spróbuj w ten sposób:

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

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

lub przygotuj metodę, która pobiera dwa wyciągalne argumenty i zwraca wartość logiczną. W tej metodzie możesz przekonwertować ciągnione na bajty i porównać,

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

sprawdź zaktualizowaną odpowiedź. jeśli nadal nie działa, sprawdź swoje przedmioty do rysowania. Lub spróbuj przekazać te same rysunki, aby zweryfikować funkcjonalność kodu
waqaslam

tak, po niepowodzeniu myślę tak samo o konwersji rysunków do bitmapy, a następnie do bajtów, spróbuję tego, dziękuję za twoje wysiłki
Roshan Jha

nie działa, czy to przetestowałeś? może coś jest nie tak, czy nie możemy bezpośrednio porównać dwóch rysunków?
Roshan Jha

czy próbowałeś przekazać to samo możliwe do wyciągnięcia dla e.g R.drawable.abcobu parametrów?
waqaslam

cześć waqas, sprawdź jeszcze raz swoją metodę, a następnie powiedz jeszcze raz, czy działa, czy nie, może być możliwe, że robię coś źle, ale to nie zadziałało, ale wtedy zmienia się definicja mojego pytania, jak porównać dwa elementy do rysowania. drawable do bitmapy, a następnie bajty porównają bitmapę i bajty, to nie jest moje wymaganie. jeśli sprawdzimy metody rysowalne, to jest metoda. equals (obiekt), więc pomyślałem, że powinna działać bezpośrednio, ale tak się nie stało. poniżej konwertuję do rysowania na bitmapę, a następnie porównuję dwie mapy bitowe i działa.
Roshan Jha

2

Ok, myślę, że znalazłem najlepsze rozwiązanie tego problemu. Ze względu na AppCompat i znajomych dostarczony element do rysowania jest czasami zawyżony w różnych formach, więc nie wystarczy getResources().getBitmap(R.drawable.my_awesome_drawable).

Tak więc, aby uzyskać możliwą do rysowania instancję tego samego typu i formy, jak zapewnia widok, można to zrobić:

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

Jest to przydatne podczas wykonywania testów. Jednak nie polecałbym tego robić na produkcji. Jeśli potrzebujesz, dodatkowe buforowanie byłoby pożądane, aby uniknąć zbytniego zastanawiania się.

W przypadku testów Expresso można to całkiem przyjemnie wykorzystać:

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

lub

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

Zanim będziesz musiał zadeklarować tę klasę pomocniczą:

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

Odpowiedziałem już na podobny temat tutaj: Uzyskaj identyfikator do rysowania w ImageView . Podejście opiera się na oznaczaniu widoku określonym identyfikatorem zasobu w pliku niestandardowym LayoutInflater. Cały proces jest zautomatyzowany dzięki prostej bibliotece TagView .

W rezultacie możesz porównać dwie rysunki tylko po ich identyfikatorach:

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

0

Rozszerzając odpowiedź od @vaughandroid, następujący Matcher działa dla przyciemnionego Vector Drawable. Musisz podać odcień, który został użyty dla 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

Porównaj 2 do rysowania:

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

Jeśli nie możesz znaleźć Drawable.toBitmap(...)tutaj, to Drawable.kt


-1

jeśli chcesz bezpośrednio porównać dwa możliwe do rysowania, użyj następującego kodu

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

Kiedy używasz equals()metody, służy ona do porównywania zawartości. powinieneś spróbować ==porównać dwa obiekty.

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

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

to może być możliwe, że nie są ==, są! =
Lucyfer

ale odnoszą się do tego samego obrazu z zasobów
Roshan Jha,

Muszę tylko sprawdzić, czy są równe, czy nie? Nie ma żadnej dostępnej metody?
Roshan Jha

tak, spełniłem twoje wymagania, przyjdź na czat
Lucifer,

1
== Porównuje, jeśli jest to ten sam obiekt, który nie jest n w 99,999999999% przypadków.
paulgavrikov
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.