Confronto tra due drawable in Android


90

Come confrontare due drawables, sto facendo così ma non ho alcun successo

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

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

Risposte:


150

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

getConstantState () non funziona bene

C'è un altro modo per confrontare:

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

mRemeberPwdè un ImageViewin questo esempio. Se stai usando a TextView, usa getBackground().getConstantStateinvece.


3
Questa soluzione funziona ed è migliore in quanto evita di convertire Drawable in Bitmap e confrontare.
Braj

2
Non sempre: WallpaperManager.getInstance (this) .getFastDrawable (). GetConstantState () è null.
paulgavrikov

Ci scusiamo per aver accettato tardi come risposta e aver cambiato la mia risposta in quanto questa sembra un'opzione migliore (e anche più voti positivi :)). Quindi controllando questo come risposta.
Roshan Jha

Grazie, questa è la risposta migliore
satyres

8
Questo codice funziona alla grande su dispositivi inferiori a 5.0 ma sto ricevendo errori utilizzandolo su un dispositivo 5.0, qualcuno può confermare che questo metodo funziona o meno su Android 5.0 o versioni successive
Phil3992

41

Affidarsi da getConstantState()soli può portare a falsi negativi .

L'approccio che ho adottato è provare a confrontare ConstantState in prima istanza, ma ripiegare su un confronto Bitmap se il controllo fallisce.

Questo dovrebbe funzionare in tutti i casi (comprese le immagini che non sono risorse) ma nota che è affamato di memoria.

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

Questo è vero al 100% e richiede più voti positivi! Gente, per favore prova il tuo codice con Drawable conosciuti prima di fare subito affidamento sul getConstantState()confronto
Patrick

Buoni risultati! Ma secondo la documentazione di BitmapDrawable.getBitmap (), getBitmap () potrebbe essere nullo, quindi potresti voler controllare anche questo
PhilLab

Questa risposta è vera e l'ho verificata dopo aver eseguito il debug di diverse ore con la codifica solo di getConstantState ().
Raghav Satyadev

Per essere sicuri, setBoundse drawsu una copia invece dell'originale stackoverflow.com/a/25462223/1916449
arekolek

Questo non funziona con BitmapDrawables colorati (vedere setTintMode / setTint / setTintList). Le bitmap possono essere identiche byte per byte ma con proprietà di tinta diverse. Poiché Android SDK non fornisce getter per le proprietà della tinta, potrebbe non esserci un modo per farlo funzionare con i drawable colorati.
Theo

12

La mia domanda era solo per confrontare due drawable, ho provato ma non sono riuscito a ottenere alcun metodo che confronta direttamente due drawable, tuttavia per la mia soluzione ho cambiato drawable in bitmap e quindi confrontando due bitmap e questo funziona.

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

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

questo è quello che ti stavo suggerendo, di confrontare il drawable in base al tipo di drawable.
jeet

1
Si potrebbe anche voler confrontare bitmap come questo: stackoverflow.com/a/7696320/317889
HGPB

2
Questo è molto pesante, quindi considera di riciclare Bitmap o finirai in un OutOfMemoryError!
paulgavrikov

2
Perché puoi confrontare le bitmap con l'uguaglianza del puntatore (==)? Mi aspetto che sia necessario Bitmap.equals ().
Ellen Spertus

@espertus Hai ragione. Ho usato lo stesso in questione per gli oggetti disegnabili, non so perché in risposta ho scelto == per l'oggetto bitmap. Comunque grazie per aver sottolineato questa base.
Roshan Jha

9

per SDK 21+

questo funziona in SDK -21

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

per SDK +21 android 5. imposta l'id drawable su imageview con tag

img.setTag(R.drawable.xxx);

e confronta in questo modo

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

questa soluzione è per chi vuole confrontare drawableid di imageviewcon id di drawable.xxx.


In realtà funziona, ma sono un po 'scioccato perché non ci sono altre possibilità T_T!
errore 1337

4

La soluzione per Android 5:

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

4

getDrawable (int) è ora deprecato. Uso getDrawable (context, R.drawable.yourimageid)

Per confrontare due sfondi

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

2
Questo ha funzionato alla grande per correggere un bug strano su Android 5. Nel mio codice, il drawable effettivo è stato restituito context.getResources().getDrawable(R.drawable.***)su Android 6+ ma non su Android 5. Con questa piccola modifica posso confrontare i drawable in background tra tutte le versioni di Android in modo impeccabile
Jose_GD

3

magari provalo in questo modo:

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

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

oppure prepara un metodo che accetta due argomenti disegnabili e restituisce booleano. In quel metodo puoi convertire drawable in byte e confrontare,

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

controlla la risposta aggiornata. se ancora non funziona, controlla i tuoi drawables. Oppure prova a passare gli stessi drawable per verificare la funzionalità del codice
waqaslam

sì, dopo non aver ottenuto il successo, penso lo stesso per la conversione dei drawables in bitmap e poi in byte, fammi provare quello, grazie per i tuoi sforzi
Roshan Jha

non funziona, l'hai provato? potrebbe esserci qualcosa che non va, non possiamo confrontare direttamente due drawables?
Roshan Jha

l'hai provato passando lo stesso disegnabile per e.g R.drawable.abcentrambi i parametri?
waqaslam

ciao waqas, controlla ancora una volta il tuo metodo e poi dì di nuovo se funziona o no, potrebbe essere possibile che sto facendo qualcosa di sbagliato ma non ha funzionato, ma poi cambia la definizione della mia domanda su come confrontare due drawables. disegnabile con bitmap e quindi i byte confronteranno bitmap e byte che non è un mio requisito. se controlliamo i metodi disegnabili, esiste il metodo .equals (oggetto), quindi ho pensato che dovrebbe funzionare direttamente, ma non lo ha fatto.Bene puoi controllare la mia risposta di seguito sto convertendo drawable in bitmap e quindi confrontando due bitmap e funziona.
Roshan Jha

2

Ok, penso di aver trovato la soluzione definitiva per questo. A causa di AppCompat e dei suoi amici, il disegnabile fornito a volte è gonfiato in forme diverse, quindi non è sufficiente getResources().getBitmap(R.drawable.my_awesome_drawable).

Quindi, per ottenere un'istanza disegnabile dello stesso tipo e forma fornita dalla vista, si può fare questo:

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

Questo è utile quando si fanno i test. Tuttavia, non consiglierei di farlo in produzione. Se necessario, sarebbe auspicabile una cache aggiuntiva per evitare di riflettere troppo.

Per i test Expresso puoi usarlo abbastanza bene:

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

o

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

Prima di dover dichiarare questa classe di supporto:

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

Ho già risposto sull'argomento simile qui: Ottieni l'ID di un disegnabile in ImageView . L'approccio si basa sull'assegnazione di tag a una vista con un ID risorsa specificato nel file personalizzato LayoutInflater. L'intero processo è automatizzato da una semplice libreria TagView .

Di conseguenza, puoi confrontare due drawable solo in base ai loro ID:

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

0

Espandendo la risposta di @vaughandroid, il seguente Matcher funziona per un Vector Drawable colorato. Devi fornire la tinta utilizzata per il 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

Confronta 2 disegnabili:

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

Se non riesci a trovare Drawable.toBitmap(...)qui è Drawable.kt


-1

se si desidera confrontare direttamente due drawable, utilizzare il codice seguente

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

Quando si utilizza il equals()metodo, viene utilizzato per confrontare i contenuti. dovresti provare ==a confrontare due oggetti.

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

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

allora potrebbe essere possibile che non lo siano ==, lo sono! =
Lucifero

ma si riferiscono alla stessa immagine dalle risorse
Roshan Jha

Devo solo controllare se sono uguali o no? Non c'è alcun metodo disponibile per questo?
Roshan Jha

Sì, ho capito la tua richiesta, per favore vieni nella chat room
Lucifer

1
== Confronta se questo è lo stesso oggetto, che non è il 99,99999999% delle volte.
paulgavrikov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.