Definir cor da forma do Android programaticamente

168

Estou editando para tornar a pergunta mais simples, esperando que ajude a obter uma resposta precisa.

Digamos que tenho a seguinte ovalforma:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:angle="270"
           android:color="#FFFF0000"/>
    <stroke android:width="3dp"
            android:color="#FFAA0055"/>
</shape>

Como defino a cor de forma programática, de dentro de uma classe de atividade?

Cote Mounyo
fonte
O que você define para este drawable?
Vikram
O drawable é um ovale é o plano de fundo de um ImageView.
Cote Mounyo
Se essa pergunta for muito difícil, existe uma maneira de desenhar várias imagens em uma tela e definir o produto final em camadas como plano de fundo de uma visualização?
Cote Mounyo
Você pode conseguir isso estendendo a Viewclasse e usando-a como a baseexibição em um layout que permite a sobreposição de widgets ( RelativeLayout, FrameLayout). Dentro dessa Viewclasse estendida , você pode draw multiple images onto a canvas. Mas, antes de fazer isso, dê uma olhada neste -> Link (se você ainda não o fez).
Vikram

Respostas:

266

Nota : A resposta foi atualizada para cobrir o cenário em que backgroundé uma instância ColorDrawable. Obrigado Tyler Pfaff , por apontar isso.

O drawable é um oval e é o plano de fundo de um ImageView

Obter o Drawablede imageViewusar getBackground():

Drawable background = imageView.getBackground();

Verifique contra suspeitos comuns:

if (background instanceof ShapeDrawable) {
    // cast to 'ShapeDrawable'
    ShapeDrawable shapeDrawable = (ShapeDrawable) background;
    shapeDrawable.getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    // cast to 'GradientDrawable'
    GradientDrawable gradientDrawable = (GradientDrawable) background;
    gradientDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    // alpha value may need to be set again after this call
    ColorDrawable colorDrawable = (ColorDrawable) background;
    colorDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

Versão compacta:

Drawable background = imageView.getBackground();
if (background instanceof ShapeDrawable) {
    ((ShapeDrawable)background).getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    ((GradientDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    ((ColorDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

Observe que a verificação nula não é necessária.

No entanto, você deve usar mutate()os desenhistas antes de modificá-los, se eles forem usados ​​em outros lugares. (Por padrão, os drawables carregados do XML compartilham o mesmo estado.)

Vikram
fonte
3
Obrigado por responder. (+1). Meu código está com outros erros, por isso é difícil de testar. Mas ainda assim isso pode definir a solidparte da forma. E a strokeporção?
Cote Mounyo
1
@TiGer Você deve adicionar @usernameseu comentário para garantir que uma notificação seja enviada ao usuário. A propósito, você precisará subclassificar ShapeDrawablepara definir a parte do traçado. Mais informações aqui: Link . Veja o comentário, que menciona um problema com a resposta aceita.
quer
3
android.graphics.drawable.GradientDrawable não pode ser transmitido para android.graphics.drawable.ShapeDrawable O elenco falhou em mim #
John
3
@ John Se o seu ImageView'splano de fundo estiver definido como a GradientDrawable, getBackground()não retornará a ShapeDrawable. Em vez disso, use o GradientDrawableque é retornado: GradientDrawable gradientDrawable = (GradientDrawable)imageView.getBackground();.... gradientDrawable.setColors(new int[] { color1, color2 });.
Vikram
2
Obrigado .. salvou o meu mundo.
sid_09
43

Faça assim:

    ImageView imgIcon = findViewById(R.id.imgIcon);
    GradientDrawable backgroundGradient = (GradientDrawable)imgIcon.getBackground();
    backgroundGradient.setColor(getResources().getColor(R.color.yellow));
Leonly91
fonte
1
@ user3111850 Você adicionou android:backgroundseu xml ou até mesmo uma setBackgroundatividade antes de ligar getBackground()? Deveria funcionar se você o tivesse feito.
Lee Yi Hong
41

Uma solução mais simples hoje em dia seria usar sua forma como plano de fundo e depois mudar programaticamente sua cor via:

view.background.setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP)

Consulte PorterDuff.Mode para obter as opções disponíveis.

ATUALIZAÇÃO (API 29):

O método acima foi descontinuado desde a API 29 e substituído pelo seguinte:

view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP)

Veja BlendMode para as opções disponíveis.

Georgios
fonte
4
A correta é: view.getBackground().setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP);Como pode haver uma borda no fundo ou em couriers arredondados.
Berkay Turancı
1
Nice @ BerkayTurancı minha forma tinha cantos arredondados. Eu poderia omitir a getBackground()ligação. Meu imageview.src continha uma forma e eu usei: imageIndicator.setColorFilter(toggleColor, PorterDuff.Mode.SRC_ATOP);where toggleColoré apenas um int que anteriormente armazenava o resultado de getColor ()
Someone Somewhere
1
Ter PorterDuff.Modepara BlendModeColorFilternão será compilado conforme necessário BlendMode. Assim, para a API 29, deveria ser view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP).
Onik 01/06
Boa captura @Onik. Atualizei a resposta de acordo. Obrigado!
Georgios
14

Tente o seguinte:

 public void setGradientColors(int bottomColor, int topColor) {
 GradientDrawable gradient = new GradientDrawable(Orientation.BOTTOM_TOP, new int[]  
 {bottomColor, topColor});
 gradient.setShape(GradientDrawable.RECTANGLE);
 gradient.setCornerRadius(10.f);
 this.setBackgroundDrawable(gradient);
 }

para mais detalhes, verifique este link neste

espero ajuda.

androidqq6
fonte
voto positivo no link. Mas não é a resposta para minha pergunta.
Cote Mounyo
13

espero que isso ajude alguém com o mesmo problema

GradientDrawable gd = (GradientDrawable) YourImageView.getBackground();
//To shange the solid color
gd.setColor(yourColor)

//To change the stroke color
int width_px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, youStrokeWidth, getResources().getDisplayMetrics());
gd.setStroke(width_px, yourColor);
medhdj
fonte
1
Inicialmente eu não poderia chegar a este trabalho, eu descobri yourColor devem ser fornecidos assim:gd.setStroke(width_px, Color.parseColor("#FF5722"));
pwnsauce
12

Expandindo a resposta de Vikram , se você estiver colorindo visualizações dinâmicas, como itens de visualização de recicladores, etc .... Então provavelmente desejará chamar mutate () antes de definir a cor. Se você não fizer isso, qualquer vista que tenha um desenho comum (ou seja, um plano de fundo) também terá seu desenho alterado / colorido.

public static void setBackgroundColorAndRetainShape(final int color, final Drawable background) {

    if (background instanceof ShapeDrawable) {
        ((ShapeDrawable) background.mutate()).getPaint().setColor(color);
    } else if (background instanceof GradientDrawable) {
        ((GradientDrawable) background.mutate()).setColor(color);
    } else if (background instanceof ColorDrawable) {
        ((ColorDrawable) background.mutate()).setColor(color);
    }else{
        Log.w(TAG,"Not a valid background type");
    }

}
Tyler Pfaff
fonte
3
necessidades e verificação e parâmetro extras: if (background instanceof LayerDrawable) { background = ((LayerDrawable) background.mutate()).getDrawable(indexIfLayerDrawable); } if (background instanceof ShapeDrawable)[...]para lidar com os layouts de plano de fundo usados <layer-list ... <item ....
Johny
11

Esta pergunta foi respondida há um tempo, mas pode ser modernizada reescrevendo como uma função de extensão kotlin.

fun Drawable.overrideColor(@ColorInt colorInt: Int) {
    when (this) {
        is GradientDrawable -> setColor(colorInt)
        is ShapeDrawable -> paint.color = colorInt
        is ColorDrawable -> color = colorInt
    }
}
Carlos Paulino
fonte
7

esta é a solução que funciona para mim ... também escrevi em outra pergunta: Como mudar a cor da forma dinamicamente?

//get the image button by id
ImageButton myImg = (ImageButton) findViewById(R.id.some_id);

//get drawable from image button
GradientDrawable drawable = (GradientDrawable) myImg.getDrawable();

//set color as integer
//can use Color.parseColor(color) if color is a string
drawable.setColor(color)
Comunidade
fonte
4

Nada funciona para mim, mas quando eu defino a cor da tonalidade, ele funciona no Shape Drawable

 Drawable background = imageView.getBackground();
 background.setTint(getRandomColor())

requer Android 5.0 API 21

Sumit
fonte
3

Minha versão da função de extensão Kotlin com base nas respostas acima com Compat :

fun Drawable.overrideColor_Ext(context: Context, colorInt: Int) {
    val muted = this.mutate()
    when (muted) {
        is GradientDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        is ShapeDrawable -> muted.paint.setColor(ContextCompat.getColor(context, colorInt))
        is ColorDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        else -> Log.d("Tag", "Not a valid background type")
    }
}
Sattar Hummatli
fonte
1

A maneira simples de preencher a forma com o Radius é:

(view.getBackground()).setColorFilter(Color.parseColor("#FFDE03"), PorterDuff.Mode.SRC_IN);
SANAT
fonte
1

Talvez seja tarde demais, mas se você estiver usando o Kotlin. Existe uma maneira como esta

var gd = layoutMain.background as GradientDrawable

 //gd.setCornerRadius(10)
  gd.setColor(ContextCompat.getColor(ctx , R.color.lightblue))
  gd.setStroke(1, ContextCompat.getColor(ctx , R.color.colorPrimary)) // (Strokewidth,colorId)

Aproveitar....

Sistema zeloso
fonte
0

Para quem usa o C # Xamarin, aqui está um método baseado no snippet do Vikram:

private void SetDrawableColor(Drawable drawable, Android.Graphics.Color color)
{
    switch (drawable)
    {
        case ShapeDrawable sd:
            sd.Paint.Color = color;
            break;
        case GradientDrawable gd:
            gd.SetColor(color);
            break;
        case ColorDrawable cd:
            cd.Color = color;
            break;
    }
}
Ryan
fonte