Android: definir estilo de exibição programaticamente

226

Aqui está o XML:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/LightStyle"
    android:layout_width="fill_parent"
    android:layout_height="55dip"
    android:clickable="true"
    android:orientation="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />

</RelativeLayout>

Como definir o styleatributo programaticamente?

Jim
fonte
Veja aqui ['response'] [1], isso funcionou para mim. [1]: stackoverflow.com/a/5488652/2938493
Artificioo

Respostas:

211

Tecnicamente, você pode aplicar estilos programaticamente, com visualizações personalizadas de qualquer maneira:

private MyRelativeLayout extends RelativeLayout {
  public MyRelativeLayout(Context context) {
     super(context, null, R.style.LightStyle);
  }
}

O construtor de um argumento é o usado quando você instancia exibições programaticamente.

Então, encadeie esse construtor ao super que leva um parâmetro de estilo.

RelativeLayout someLayout = new MyRelativeLayout(new ContextThemeWrapper(this,R.style.RadioButton));

Ou como @Dori apontou simplesmente:

RelativeLayout someLayout = new RelativeLayout(new ContextThemeWrapper(activity,R.style.LightStyle));
Blundell
fonte
34
Você poderia apenas fazer isso programaticamente sem estender como o construtor 3 arg é público de qualquer forma developer.android.com/reference/android/widget/... , android.util.AttributeSet, int)
Dori
1
@Turbo, ambos os documentos e eclipse deve dizer-lhe que: http://developer.android.com/reference/android/widget/LinearLayout.html#LinearLayout(android.content.Context, android.util.AttributeSet, int). Com o LinearLayout, o nível 11+ é necessário.
TheOne
2
@Ori O que você passaria pelo AttributeSet?
Blundell
9
Se é um TextView, você precisa usar setTextAppearance (R.style.small_text) para esses atributos que afetam texto (tamanho, cor, etc.)
Maragues
2
Não faço ideia por que a abordagem do terceiro argumento não funciona. Eu me inscrevi para Button. A abordagem de @Benjamin Piette funciona bem.
Youngjae
124

O que funcionou para mim:

Button b = new Button(new ContextThemeWrapper(this, R.style.ButtonText), null, 0);
  • Use um ContextThemeWrapper

E

  • Use o construtor de 3 argumentos (não funcionará sem isso)
Benjamin Piette
fonte
Usando seu método funciona, mas recebo W / ResourceType ﹕ Muitas referências de atributos, paradas em: 0x01010034. Alguma solução alternativa?
HaloMediaz 07/09
Eu nunca vi esse problema. Esse pode ser um problema específico do dispositivo ou algo relacionado a uma versão recente do Android?
Benjamin Piette
11
IMPORTANTE! Este método faz o trabalho, mas irá definir o estilo para cada vista da criança, bem como se a criação de um novo layout com o ContextThemeWrapper.
AProperFox
@aProperFox, você pode explicar melhor? Comigo trabalha adicionando apenas a um botão. Você se refere a um layout?
Gilian
1
@ Gilian exatamente, se você usar isso em uma exibição composta que tenha um ou mais filhos, eles terão o mesmo estilo da exibição principal para a qual você definiu o estilo. Isto poderia levar a demasiada preenchimento ou margem dentro vistas internas e pode conduzi-lo louco se você não está ciente disso :)
aProperFox
88

Atualização : no momento de responder a essa pergunta (meados de 2012, nível 14-15 da API), definir a exibição programaticamente não era uma opção (mesmo que houvesse algumas soluções não-triviais), enquanto isso foi possível após a API mais recente lançamentos. Consulte a resposta da @ Blundell para obter detalhes.

Resposta ANTIGA:

Ainda não é possível definir o estilo de uma exibição por meio de programação, mas você pode achar esse thread útil.

Korhan Ozturk
fonte
16
Não é verdade. Veja a resposta de @ Blundell.
precisa saber é o seguinte
31
Para algumas situações (usando o TextView, por exemplo), você pode usar textView.setTextAppearance(context, R.style.mystyle);. De acordo com o documento "Define a cor, tamanho, estilo, cor da dica e cor do texto do recurso TextAppearance especificado". Por exemplo , você pode
usar o seguinte comando
4
api> 23; textView.setTextAppearance (R.style.mystyle);
Alican akyol
1
Eu publiquei esta resposta mais de 4 anos atrás @HaydenKai. e a API mudou muito desde então, permitindo a aplicação de estilos programaticamente.
Korhan Ozturk
3
@KorhanOzturk concordou, mas no SO com perguntas como esta que têm actividade a questão deve ser reaberto e uma nova resposta aceita
HaydenKai
16

Para um novo Button / TextView:

Button mMyButton = new Button(new ContextThemeWrapper(this, R.style.button_disabled), null, 0);

Para uma instância existente:

mMyButton.setTextAppearance(this, R.style.button_enabled);

Para imagem ou layouts:

Image mMyImage = new ImageView(new ContextThemeWrapper(context, R.style.article_image), null, 0);
Alon Kogan
fonte
8

Se você quiser continuar usando XML (que a resposta aceita não permite) e definir o estilo após a criação da exibição, poderá usar a biblioteca de Paris que suporta um subconjunto de todos os atributos disponíveis.

Como você está inflando sua visualização do XML, você precisará especificar um ID no layout:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_styleable_relative_layout"
    style="@style/LightStyle"
    ...

Então, quando você precisar alterar o estilo programaticamente, após o layout ter sido inflado:

// Any way to get the view instance will do
RelativeLayout myView = findViewById(R.id.my_styleable_relative_layout);

// This will apply all the supported attribute values of the style
Paris.style(myView).apply(R.style.LightStyle);

Para mais: a lista de tipos e atributos de exibição suportados (inclui plano de fundo, preenchimento, margem etc. e pode ser facilmente estendida) e instruções de instalação com documentação adicional .

Disclaimer: Eu sou o autor original da referida biblioteca.

Natanael
fonte
Ainda é uma abordagem recomendada para uso em 2019?
IgorGanapolsky 22/03/19
1
@IgorGanapolsky afaik yes! Não conheço ninguém melhor.
Nathanael
Obrigado @Nathanael por fornecer esta solução. Também por ser o mantenedor dele com o último atributo line_height. Mantenha o bom trabalho! Muito útil.
Guillem Roca
7

Você pode aplicar um estilo à sua atividade fazendo:

super.setTheme( R.style.MyAppTheme );

ou padrão do Android:

super.setTheme( android.R.style.Theme );

em sua atividade, antes setContentView().

FOMDeveloper
fonte
4
@siemian, você pode deixar a FOM se expressar livremente? Ele não precisa de edição!
Mika
6

Nenhuma das respostas fornecidas está correta.

Você pode definir o estilo programaticamente.

A resposta curta é ver http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/content/Context.java#435

Resposta longa. Aqui está meu snippet para definir estilo definido personalizado programaticamente para sua visualização:

1) Crie um estilo no seu arquivo styles.xml

 <style name="MyStyle">
    <item name="customTextColor">#39445B</item>
    <item name="customDividerColor">#8D5AA8</item>
</style>

Não se esqueça de definir seus atributos customizados no arquivo attrs.xml

Meu arquivo attrsl.xml:

<declare-styleable name="CustomWidget">
    <attr name="customTextColor" format="color" />
    <attr name="customDividerColor" format="color" />
</declare-styleable>

Observe que você pode usar qualquer nome para seu estilo (meu CustomWidget)

Agora vamos definir o estilo para o widget Programaticamente Aqui está o meu widget simples:

public class StyleableWidget extends LinearLayout {

private final StyleLoader styleLoader = new StyleLoader();

private TextView textView;
private View divider;

public StyleableWidget(Context context) {
    super(context);
    init();
}

private void init() {
    inflate(getContext(), R.layout.widget_styleable, this);
    textView = (TextView) findViewById(R.id.text_view);
    divider = findViewById(R.id.divider);
    setOrientation(VERTICAL);
}

protected void apply(StyleLoader.StyleAttrs styleAttrs) {
    textView.setTextColor(styleAttrs.textColor);
    divider.setBackgroundColor(styleAttrs.dividerColor);
}

public void setStyle(@StyleRes int style) {
    apply(styleLoader.load(getContext(), style));
}
}

layout:

<TextView
    android:id="@+id/text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="22sp"
    android:layout_gravity="center"
    android:text="@string/styleble_title" />

<View
    android:id="@+id/divider"
    android:layout_width="match_parent"
    android:layout_height="1dp"/>

</merge>

E, finalmente, a implementação da classe StyleLoader

public class StyleLoader {

public StyleLoader() {

}

public static class StyleAttrs {
    public int textColor;
    public int dividerColor;
}

public StyleAttrs load(Context context, @StyleRes int styleResId) {
    final TypedArray styledAttributes = context.obtainStyledAttributes(styleResId, R.styleable.CustomWidget);
    return load(styledAttributes);
}

@NonNull
private StyleAttrs load(TypedArray styledAttributes) {
    StyleAttrs styleAttrs = new StyleAttrs();
    try {
        styleAttrs.textColor = styledAttributes.getColor(R.styleable.CustomWidget_customTextColor, 0);
        styleAttrs.dividerColor = styledAttributes.getColor(R.styleable.CustomWidget_customDividerColor, 0);
    } finally {
        styledAttributes.recycle();
    }
    return styleAttrs;
}
}

Você pode encontrar um exemplo completo de trabalho em https://github.com/Defuera/SetStylableProgramatically

Defuera
fonte
13
Este não é um estilo verdadeiro, é apenas falso aplicando / definindo as configurações salvas (sim, na verdade, são alguns tipos de configurações salvas no seu arquivo XML) nas suas Views (aqui você tem apenas 2 Views fixas - e certamente precisa conhecê-las) antecipadamente). Toda a mágica está no applymétodo que não faz nada de interessante. O verdadeiro significado do estilo é aplicar automaticamente algum estilo visual em todas as futuras visualizações adicionadas dinamicamente. É claro que esse código aqui não pode fazer isso e não há nada dinâmico aqui, precisamos saber quais Views e quais propriedades / campos definir.
King King
1
Não me importo de especificar em quais modos de exibição aplicar estilos, mas esta solução possui os elementos de estilo codificados (por exemplo, textColor e dividerColor). Ele não localizará dinamicamente todos os elementos definidos no estilo e os aplicará à visualização.
Nasch 29/09/17
O link que você postou não abre. Você pode re-postar?
IgorGanapolsky 22/03/19
4

Esta é uma pergunta bastante antiga, mas a solução que funcionou para mim agora é usar o 4º parâmetro do construtor defStyleRes - se disponível .. em exibição ... para definir o estilo

Trabalhos a seguir para meus propósitos (kotlin):

val textView = TextView(context, null, 0, R.style.Headline1)
casco convexo
fonte
3

Este é meu exemplo simples, a chave é o ContextThemeWrapperwrapper, sem ele, meu estilo não funciona e usando o construtor de três parâmetros da View.

ContextThemeWrapper themeContext = new ContextThemeWrapper(this, R.style.DefaultLabelStyle);
TextView tv = new TextView(themeContext, null, 0);
tv.setText("blah blah ...");
layout.addView(tv);
guogangj
fonte
2

a maneira simples é passar pelo construtor

RadioButton radioButton = new RadioButton(this,null,R.style.radiobutton_material_quiz);
Sai Gopi N
fonte
Um pouco mais de ilustração sobre como usar esta solução seria útil.
IgorGanapolsky 22/03/19
1

Não me proponho a usar ContextThemeWrapper da seguinte maneira:

O tema especificado será aplicado sobre o tema do contexto base.

O que pode gerar resultados indesejados no seu aplicativo. Em vez disso, proponho uma nova biblioteca "paris" para isso dos engenheiros do Airbnb:

https://github.com/airbnb/paris

Defina e aplique estilos às visualizações do Android programaticamente.

Mas depois de algum tempo de uso, descobri que na verdade é bastante limitado e parei de usá-lo porque ele não suporta muitas propriedades que eu preciso fora da caixa, então é preciso verificar e decidir como sempre.

Renetik
fonte
Explique ao seu amigo que já fez o voto negativo ... Perdi um dia e meio porque usei o ContextThemeWrapper, e ele se aplicava ao fundo branco do meu tema de contexto, e de repente o widget Chip da biblioteca de materiais do Google estava falhando, adivinhe ...
Renetik
A biblioteca de Paris não usa o ContextThemeWrapper por baixo?
IgorGanapolsky 22/03/19
@IgorGanapolsky isso não acontece. Ele lê o estilo XML e o converte nas chamadas de método correspondentes na exibição.
Nathanael
É realmente muito limitado parei de usá-lo porque ele não suporta uma grande quantidade de propriedades que eu precisava ...
Renetik
-1

Eu usei visualizações definidas em XML no meu ViewGroup composto, e as adicionei ao Viewgroup. Dessa forma, não posso alterar dinamicamente o estilo, mas posso fazer algumas personalizações de estilo. Meu composto:

public class CalendarView extends LinearLayout {

private GridView mCalendarGrid;
private LinearLayout mActiveCalendars;

private CalendarAdapter calendarAdapter;

public CalendarView(Context context) {
    super(context);

}

public CalendarView(Context context, AttributeSet attrs) {
    super(context, attrs);

}

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    init();
}

private void init() {
    mCalendarGrid = (GridView) findViewById(R.id.calendarContents);
    mCalendarGrid.setNumColumns(CalendarAdapter.NUM_COLS);

    calendarAdapter = new CalendarAdapter(getContext());
    mCalendarGrid.setAdapter(calendarAdapter);
    mActiveCalendars = (LinearLayout) findViewById(R.id.calendarFooter);
}

}

e minha visão em xml, onde posso atribuir estilos:

<com.mfitbs.android.calendar.CalendarView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/calendar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
>

<GridView
    android:id="@+id/calendarContents"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<LinearLayout
    android:id="@+id/calendarFooter"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    />

user3918502
fonte
1
Onde você está definindo dinamicamente o estilo?
IgorGanapolsky 22/03/19
-3

Você pode criar o XML contendo o layout com o estilo desejado e altere o recurso de sua opinião do fundo, como este .

rfsbraz
fonte
Você está mais falando sobre um "estilo" como no Android, mas sobre o "aspecto" de um botão. isso é muito diferente.
Cristo