285

Here's 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>

How to set style attribute programmatically?

Gene Bo
  • 11,284
  • 8
  • 90
  • 137
Jim
  • 8,874
  • 16
  • 68
  • 125

16 Answers16

271

Technically you can apply styles programmatically, with custom views anyway:

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

The one argument constructor is the one used when you instantiate views programmatically.

So chain this constructor to the super that takes a style parameter.

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

Or as @Dori pointed out simply:

RelativeLayout someLayout = new RelativeLayout(new ContextThemeWrapper(activity,R.style.LightStyle));

Now in Kotlin:

class MyRelativeLayout @JvmOverloads constructor(
    context: Context, 
    attributeSet: AttributeSet? = null, 
    defStyleAttr: Int = R.style.LightStyle,
) : RelativeLayout(context, attributeSet, defStyleAttr)

or

 val rl = RelativeLayout(ContextThemeWrapper(activity, R.style.LightStyle))
Blundell
  • 75,855
  • 30
  • 208
  • 233
  • 39
    You could just do this programatically without extending as the 3 arg constructor is public anyhow http://developer.android.com/reference/android/widget/RelativeLayout.html#RelativeLayout(android.content.Context, android.util.AttributeSet, int) – Dori Jan 27 '14 at 10:44
  • @Ramin where can I find info about the 3 arg constructor only working in API 11+? Thanks – Tony Chan Apr 08 '14 at 23:43
  • 1
    @Turbo, both the docs and eclipse should tell you that: `http://developer.android.com/reference/android/widget/LinearLayout.html#LinearLayout(android.content.Context, android.util.AttributeSet, int)`. With LinearLayout Level 11+ is required. – TheOne Apr 14 '14 at 13:02
  • 2
    @Dori What would you pass for the `AttributeSet`? – Blundell Jun 04 '14 at 06:59
  • 1
    @Blundell probably just null - this is usually used to pass in the attributes set via xml but as your are creating programmatically you probably wont have these in xml format anyway. You can just call the setters directly on the view... – Dori Jun 04 '14 at 10:08
  • 11
    If it's a TextView, you need to use setTextAppearance(R.style.small_text) for those attributes that affect text (size, color, etc.) – Maragues Jun 06 '14 at 14:30
  • 3
    I have no idea why 3rd-argument approach does not work. I applied to `Button`. @Benjamin Piette approach works fine. – Youngjae Sep 15 '15 at 10:41
  • 1
    It didn't work for me. I notice there is another constructor: `public View (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)` and my style is in the styles.xml, so maybe that will work. But the API level is too high for me (added in API 21). – Rock Lee Jan 17 '16 at 04:37
  • What can I do if my case to apply on Button, I need to "findViewByID()" at first, but it doesn't sounds correct to **construct** a new Button again with 3 args and style applied. I need to change style programmatically – elliotching Apr 21 '17 at 06:53
  • For TextView, i set the style in the 4th parameter too, that do the trick for me. – passatgt May 03 '18 at 09:04
  • when i use 3rd constructor my view goes invisible. i want to set ratingbar style to small progammatically – M.kazem Akhgary Nov 10 '18 at 14:05
  • Thanks, I've used your code with ContextThemeWrapper but it doesn't apply layout parameters for instance to ImageView. It could be fixed like this (Kotlin): imageView.layoutParams = LayoutParams(ContextThemeWrapper(context, R.style.MyImageView),null) – czang Jan 24 '20 at 08:04
  • `int buttonStyle = R.style.your_button_style; Button button = new Button(new ContextThemeWrapper(context, buttonStyle), null, buttonStyle).` Only it works for me. See https://stackoverflow.com/a/24438579/5093308 – Zhou Hongbo Feb 16 '22 at 14:49
150

What worked for me:

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

AND

  • Use the 3-arguments constructor (won't work without this)
Benjamin Piette
  • 3,645
  • 1
  • 27
  • 24
  • Using your method works, but I get W/ResourceType﹕ Too many attribute references, stopped at: 0x01010034. Any workaround? – HaloMediaz Sep 07 '15 at 20:54
  • I've never seen that issue. That may be a device-specific issue, or something related to a recent Android version ? – Benjamin Piette Sep 08 '15 at 09:18
  • 14
    **IMPORTANT!** This method *does* work, but will set the styling for **every** child view as well if creating a new Layout with the ContextThemeWrapper. – aProperFox Sep 25 '15 at 17:59
  • 1
    @aProperFox can you explain better? With me works by adding just to a Button. You refer to a layout? – Gilian Sep 06 '16 at 15:18
  • 2
    @Gilian exactly, if you use this on a compound view that has one or more children, they will get the same styling as the parent view you set the style for. This could lead to too much padding or margin within inner views and can drive you insane if you're not aware of it :) – aProperFox Sep 06 '16 at 15:26
  • For some reason this didn't work for me. Throws `UnsupportedOperationException: Failed to resolve attribute at index 24: TypedValue{t=0x3/d=0x1c7 "res/color/primary_text_material_light.xml" a=1 r=0x106010a}` – MadeOfAir Feb 21 '17 at 08:20
  • only thing that worked. with this i could set small style to the rating bar. – M.kazem Akhgary Nov 10 '18 at 14:17
  • Why do we have to use 3-arguments constructor though? I looked up the sources and internally 1-argument constructor call eventually calls 3-argument constructor. But the problem is that when I use 1-argument it somehow doesn't work – ulmaxy Jul 10 '19 at 06:19
93

Update: At the time of answering this question (mid 2012, API level 14-15), setting the view programmatically was not an option (even though there were some non-trivial workarounds) whereas this has been made possible after the more recent API releases. See @Blundell's answer for details.

OLD Answer:

You cannot set a view's style programmatically yet, but you may find this thread useful.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Korhan Ozturk
  • 11,148
  • 6
  • 36
  • 49
  • 16
    Not true. See @Blundell's answer. – a.bertucci Jan 13 '14 at 12:04
  • 35
    For some situations (using TextView for example) you can use `textView.setTextAppearance(context, R.style.mystyle);` . According to doc "Sets the text color, size, style, hint color, and highlight color from the specified TextAppearance resource." http://developer.android.com/reference/android/widget/TextView.html#setTextAppearance(android.content.Context, int) – Rogel Garcia Feb 24 '14 at 18:09
  • in api 23 setTextAppearance is depricated – Arpit Patel Aug 25 '16 at 10:57
  • 6
    api > 23 ; textView.setTextAppearance(R.style.mystyle); – alican akyol Nov 29 '16 at 23:15
  • 1
    I posted this answer over 4 years ago @HaydenKai. and the API has changed a lot since then enabling applying styles programmatically. – Korhan Ozturk Dec 02 '16 at 15:35
  • 3
    @KorhanOzturk agreed, but on SO with questions like this that have activity the question should be reopened and a new answer accepted – HaydenKai Dec 03 '16 at 01:59
23

For a new Button/TextView:

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

For an existing instance:

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

For Image or layouts:

Image mMyImage = new ImageView(new ContextThemeWrapper(context, R.style.article_image), null, 0);
Alon Kogan
  • 3,258
  • 1
  • 21
  • 20
14

This is quite old question but solution that worked for me now is to use 4th parameter of constructor defStyleRes - if available.. on view... to set style

Following works for my purposes (kotlin):

val textView = TextView(context, null, 0, R.style.Headline1)
Anup
  • 4,024
  • 1
  • 18
  • 27
convexHull
  • 1,761
  • 2
  • 15
  • 18
  • 3
    Note that [this constructor](https://developer.android.com/reference/android/widget/TextView?hl=en#TextView(android.content.Context,%20android.util.AttributeSet,%20int,%20int)) was added in API 21 hence if you have `minSdkVersion` < 21 you'll get the error "Call requires API level 21 (current min is 19)" – Albert Vila Calvo Jul 27 '20 at 13:00
11

If you'd like to continue using XML (which the accepted answer doesn't let you do) and set the style after the view has been created you may be able to use the Paris library which supports a subset of all available attributes.

Since you're inflating your view from XML you'd need to specify an id in the layout:

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

Then when you need to change the style programmatically, after the layout has been inflated:

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

For more: the list of supported view types and attributes (includes background, padding, margin, etc. and can easily be extended) and installation instructions with additional documentation.

Disclaimer: I'm the original author of said library.

Nathanael
  • 701
  • 5
  • 12
  • Is this still a recommended approach to use in 2019? – IgorGanapolsky Mar 22 '19 at 14:21
  • 1
    @IgorGanapolsky afaik yes! I don't know of any better one. – Nathanael Mar 23 '19 at 17:14
  • Thank you @Nathanael for providing this solution. Also for being the maintainer of it with the latest line_height attribute. Keep up the good work! Really useful. – Guillem Roca Jul 18 '19 at 13:28
  • Yes but take in mind it is currently not supported with material components attributes. (correct me if I am wrong) I am new to android development, the best solution currently would be to hide/show same views with different styles? – I.Step Nov 12 '20 at 17:27
  • Interesting... is this library of yours suitable for android SDK 31? – SebasSBM Sep 07 '22 at 05:25
6

You can apply a style to your activity by doing:

super.setTheme( R.style.MyAppTheme );

or Android default:

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

in your activity, before setContentView().

siemian
  • 665
  • 1
  • 5
  • 12
FOMDeveloper
  • 4,370
  • 3
  • 21
  • 20
6

Non of the provided answers are correct.

You CAN set style programatically.

Short answer is take a look at http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/content/Context.java#435

Long answer. Here's my snippet to set custom defined style programatically to your view:

1) Create a style in your styles.xml file

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

Do not forget to define your custom attributes in attrs.xml file

My attrsl.xml file:

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

Notice you can use any name for your styleable (my CustomWidget)

Now lets set the style to the widget Programatically Here's My simple widget:

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>

And finally StyleLoader class implementation

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

You can find fully working example at https://github.com/Defuera/SetStylableProgramatically

Cam
  • 14,930
  • 16
  • 77
  • 128
Defuera
  • 5,356
  • 3
  • 32
  • 38
  • 18
    This is not true style, it's just a fake by applying/setting the saved settings (yes it's actually some kind of settings saved in your XML file) on your Views (here you have just 2 fixed Views - *and* you surely need to know them beforehand). All the magic is in the `apply` method which does nothing interesting. The true meaning of style is it ***automatically*** applying some visual style on all the future dynamically added Views. This code here of course cannot do that and there is nothing dynamic here, we need to know ***what*** Views and ***which*** properties/fields to set. – King King Aug 03 '16 at 23:54
  • 2
    I don't mind specifying what views to apply styles to, but this solution has the style elements hard-coded (e.g. textColor and dividerColor). It will not dynamically find all elements defined in the style and apply them to the view. – nasch Sep 29 '17 at 22:40
  • The link you posted doesn't open. Can you please re-post? – IgorGanapolsky Mar 22 '19 at 14:19
6

This is my simple example, the key is the ContextThemeWrapper wrapper, without it, my style does not work, and using the three parameters constructor of the View.

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

the simple way is passing through constructor

RadioButton radioButton = new RadioButton(this,null,R.style.radiobutton_material_quiz);
saigopi.me
  • 14,011
  • 2
  • 83
  • 54
2

I don't propose to use ContextThemeWrapper as it do this:

The specified theme will be applied on top of the base context's theme.

What can make unwanted results in your application. Instead I propose new library "paris" for this from engineers at Airbnb:

https://github.com/airbnb/paris

Define and apply styles to Android views programmatically.

But after some time of using it I found out it's actually quite limited and I stopped using it because it does not support a lot of properties i need out off the box, so one have to check out and decide as always.

lk135
  • 1,359
  • 2
  • 13
  • 17
Renetik
  • 5,887
  • 1
  • 47
  • 66
  • Explain your downvote buddy... I lost one day and half because I have used ContextThemeWrapper , and it applied to my context theme white background, then suddenly Chip widget from google material library was crashing, guess why... – Renetik Nov 17 '18 at 23:53
  • Doesn't Paris library use **ContextThemeWrapper** underneath? – IgorGanapolsky Mar 22 '19 at 14:24
  • @IgorGanapolsky it does not. It reads the XML style and converts it into the corresponding method calls on the view. – Nathanael Mar 23 '19 at 17:15
  • It's actually quite limited I stopped using it because it does not support a lot of properties i needed... – Renetik Mar 24 '19 at 21:40
2
int buttonStyle = R.style.your_button_style;
Button button = new Button(new ContextThemeWrapper(context, buttonStyle), null, buttonStyle);

Only this answer works for me. See https://stackoverflow.com/a/24438579/5093308

Zhou Hongbo
  • 1,297
  • 13
  • 25
0

best simple solution i found, using alertDialog with a custom layout, is :

val mView = LayoutInflater.from(context).inflate(layoutResId, null)

val dialog = AlertDialog.Builder(context, R.style.CustomAlertDialog)
    .setView(mView)
    .setCancelable(false)
    .create()

where style is

<style name="CustomAlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
    <item name="android:background">@drawable/bg_dialog_white_rounded</item>
</style>

and bg_dialog_white_rounded.xml is

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="16dp" />

    <solid android:color="@Color/white" />
</shape>

layoutResId is a resource id of any layout that has to have the theme set to "@style/CustomAlertDialog", for example:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginStart="@dimen/wdd_margin_medium"
    android:theme="@style/CustomAlertDialog"
    android:layout_marginEnd="@dimen/wdd_margin_medium">

..... etc...
</androidx.constraintlayout.widget.ConstraintLayout>
S.Bozzoni
  • 998
  • 9
  • 18
-1

I used views defined in XML in my composite ViewGroup, inflated them added to Viewgroup. This way I cannot dynamically change style but I can make some style customizations. My composite:

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

}

and my view in xml where i can assign styles:

<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"
    />

-1

if inside own custom view : val editText = TextInputEditText(context, attrs, defStyleAttr)

fvolodimir
  • 445
  • 5
  • 3
-6

You can create the xml containing the layout with the desired style and then change the background resource of your view, like this.

rfsbraz
  • 2,101
  • 1
  • 18
  • 26
  • You are mote talking about a "style" as in Android, but about the "aspect" of a button. this is very different. – Christ May 30 '14 at 07:02