Comparando dois drawables no Android

90

Como comparar dois drawables, estou fazendo assim, mas não estou tendo sucesso

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

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

Respostas:

150

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

getConstantState () não funciona bem

Existe outra maneira de comparar:

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

mRemeberPwdé um ImageViewneste exemplo. Se você estiver usando um TextView, use em seu getBackground().getConstantStatelugar.

Mejonzhan
fonte
3
Esta solução funciona e é melhor, pois evita a conversão de Drawable em Bitmap e comparação.
Braj
2
Nem sempre: WallpaperManager.getInstance (this) .getFastDrawable (). GetConstantState () é nulo.
paulgavrikov
Desculpe por tardar em aceitar isso como resposta e mudar minha própria resposta, pois esta parece a melhor opção (e mais votos positivos também :)). Então, verificando isso como resposta.
Roshan Jha
Obrigado, esta é a melhor resposta
sátiros
8
Este código funciona muito bem em dispositivos anteriores a 5.0, mas estou recebendo erros ao usá-lo em um dispositivo 5.0. Alguém pode confirmar que esse método funciona ou não no Android 5.0 ou superior
Phil3992
41

Depender getConstantState()sozinho pode resultar em falsos negativos .

A abordagem que usei é tentar comparar ConstantState na primeira instância, mas recorrer a uma comparação de bitmap se a verificação falhar.

Isso deve funcionar em todos os casos (incluindo imagens que não são recursos), mas observe que consome muita memória.

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;
}
Vaughandroid
fonte
Isso é 100% verdadeiro e precisa de mais votos positivos! Pessoal, por favor, testem seu código com Drawables conhecidos antes de confiar imediatamente na getConstantState()comparação
Patrick
Boas descobertas! Mas de acordo com a documentação de BitmapDrawable.getBitmap (), getBitmap () pode ser nulo, então você pode querer verificar isso também
PhilLab
Esta resposta é verdadeira e eu verifiquei isso depois de depurar várias horas com a codificação de apenas getConstantState ().
Raghav Satyadev
Para ser seguro, setBoundse drawem uma cópia em vez do stackoverflow.com/a/25462223/1916449
arekolek
Isso não funciona com BitmapDrawables colorido (consulte setTintMode / setTint / setTintList). Os bitmaps podem ser byte a byte idênticos, mas com propriedades de tonalidade diferentes. Como o Android SDK não fornece getters para as propriedades tint, pode não haver uma maneira de fazê-lo funcionar com drawables tingidos.
Theo
12

Minha pergunta era apenas para comparar dois drawables, tentei, mas não consegui nenhum método que comparasse diretamente dois drawables, no entanto, para minha solução, mudei o drawable para bitmap e depois comparei dois bitmaps e está funcionando.

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

if(bitmap == bitmap2)
    {
        //Code blcok
    }
Roshan Jha
fonte
isso é o que eu estava sugerindo, para comparar drawable de acordo com o tipo de drawable.
jeet
1
Você também pode querer comparar bitmaps como este: stackoverflow.com/a/7696320/317889
HGPB
2
Isso é muito pesado, então considere reciclar Bitmaps ou você acabará em um OutOfMemoryError!
paulgavrikov
2
Por que você pode comparar bitmaps com igualdade de ponteiro (==)? Eu esperaria que Bitmap.equals () fosse necessário.
Ellen Spertus
@espertus Você está certo. Eu usei o mesmo em questão para objetos drawable não sei por que mudei para == para objeto bitmap em resposta. De qualquer forma, obrigado por apontar este básico.
Roshan Jha
9

para SDK 21+

isso funciona no SDK -21

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

para SDK +21 android 5. definir id drawable para imageview com tag

img.setTag(R.drawable.xxx);

e compare assim

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

esta solução é para quem deseja comparar drawableid de imageviewcom id de drawable.xxx.

esconder
fonte
Na verdade, isso funciona, mas estou meio chocado porque não há outra possibilidade T_T!
error1337
4

A solução para Android 5:

 if(image.getDrawable().getConstantState().equals(image.getContext().getDrawable(R.drawable.something).getConstantState()))
Tiago santos
fonte
4

getDrawable (int) agora está obsoleto. Use getDrawable (context, R.drawable.yourimageid)

Para comparar dois fundos

Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());
RAJESH KUMAR ARUMUGAM
fonte
2
Isso funcionou muito bem para corrigir um bug estranho no Android 5. No meu código, o drawable real foi retornado context.getResources().getDrawable(R.drawable.***)no Android 6+, mas não no Android 5. Com essa pequena mudança, posso comparar drawables em segundo plano em todas as versões do Android perfeitamente
Jose_GD
3

talvez tente desta forma:

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

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

ou prepare um método que receba dois argumentos drawable e retorne booleano. Nesse método, você pode converter drawable em bytes e comparar,

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
fonte
verifique a resposta atualizada. se ainda não funcionar, verifique seus drawables. Ou tente passar os mesmos drawables para verificar a funcionalidade do código
waqaslam
sim, depois de não obter sucesso, estou pensando o mesmo para converter drawables em bitmap e depois em byte, deixe-me tentar aquele, obrigado por seus esforços
Roshan Jha
não está funcionando, você testou? pode haver algo errado, não podemos comparar diretamente dois drawables?
Roshan Jha
você tentou passar o mesmo drawable para os e.g R.drawable.abcdois parâmetros?
waqaslam
olá waqas, verifique seu método mais uma vez e diga novamente se está funcionando ou não, pode ser possível que eu esteja fazendo algo errado, mas isso não funcionou, mas então muda a definição da minha pergunta de como comparar dois drawables. drawable para bitmap e bytes irá comparar bitmap e bytes que não são meus requisitos. se verificarmos os métodos drawable, então há o método .equals (objeto), então pensei que deveria funcionar diretamente, mas não funcionou. Bem, você pode verificar minha resposta abaixo estou convertendo drawable em bitmap e comparando dois bitmaps e está funcionando.
Roshan Jha
2

Ok, acho que encontrei a solução definitiva para isso. Por causa de AppCompat e amigos, o drawable fornecido às vezes é inflado em diferentes formas, portanto, não é suficiente getResources().getBitmap(R.drawable.my_awesome_drawable).

Portanto, para obter uma instância drawable do mesmo tipo e forma fornecida pela visualização, pode-se fazer o seguinte:

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

Isso é útil ao fazer testes. No entanto, eu não recomendaria fazer isso na produção. Se necessário, um cache extra seria desejável para evitar fazer muita reflexão.

Para testes do Expresso, você pode usar isso muito bem:

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

ou

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

Antes, você terá que declarar esta classe auxiliar:

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

}
pablisco
fonte
1

Use getTag () e setTag () para comparação

Pooja Akshantal
fonte
0

Já respondi sobre um tópico semelhante aqui: Obtenha o ID de um drawable em ImageView . A abordagem é baseada na marcação de uma visualização com um ID de recurso especificado no customLayoutInflater . Todo o processo é automatizado por uma biblioteca simples TagView .

Como resultado, você pode comparar dois drawables apenas por seus ids:

TagViewUtils.getTag(view, ViewTag.VIEW_BACKGROUND.id) == R.drawable.twt_hover
Bogdan Kornev
fonte
0

Expandindo a resposta de @vaughandroid, o Matcher a seguir funciona para um Drawable de vetor colorido. Você deve fornecer a tonalidade que foi usada para o 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) {

        }
    };
}
Jeremiah Eikenberg
fonte
0

Compare 2 drawable:

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

Se você não consegue encontrar Drawable.toBitmap(...)aqui é Drawable.kt

Benny
fonte
-1

se você deseja comparar diretamente dois drawable, use o seguinte código

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.
}
Addon.mahesh
fonte
-2

Quando você está usando o equals()método, ele é usado para comparar o conteúdo. você deve tentar ==comparar dois objetos.

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

  if( fDraw == sDraw )
  {
   // Coming
  }
}
Lúcifer
fonte
então pode ser possível que eles não sejam ==, eles são! =
Lúcifer
mas eles se referem à mesma imagem de recursos
Roshan Jha
Só preciso verificar se eles são iguais ou não? Não há método disponível para isso?
Roshan Jha
Sim, atendi ao seu pedido, por favor, venha para a sala de chat
Lucifer
1
== Compara se este é o mesmo objeto, o que não é n 99,99999999% das vezes.
paulgavrikov