Search code examples
androidandroid-themeandroid-styles

Views created progmatically aren't inheriting theme


I am trying to create a view pragmatically and then add it to my activity. This bit is working fine, however the theme for the view group isn't inherited by my new view

My theme:

<style name="CustomButtonTheme" parent="@style/Widget.AppCompat.Button">
  <item name="android:textColor">#FF0000</item>
  <item name="android:background">#00FF00</item>
</style>

My layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/buttonArea"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:theme="@style/CustomButtonTheme">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This button inherits CustomButtonTheme" />
</LinearLayout>

Java code

AppCompatButton button = new AppCompatButton(getContext());
button.setText("This button does not inherit CustomButtonTheme");

LinearLayout buttonArea = findViewById<LinearLayout>(R.id.buttonArea);
buttonArea.addView(button);

Solution

  • An android:theme attribute in a layout will only have effect during inflation, and only on that particular subtree. It won't be applied to the Activity's overall theme.

    All that attribute does, though, is cause the LayoutInflater to wrap its current Context with the specified theme in a ContextThemeWrapper. We could do something similar ourselves, and just to illustrate the basic usage:

    ContextThemeWrapper wrapper = new ContextThemeWrapper(getContext(), R.style.CustomButtonTheme);
    AppCompatButton button = new AppCompatButton(wrapper);
    

    However, this has already been done for us, basically, when the LayoutInflater created a ContextThemeWrapper internally, for that android:theme attribute. That ContextThemeWrapper is the Context that the LinearLayout will have been created with, so we can simply use its Context to instantiate our AppCompatButton:

    AppCompatButton button = new AppCompatButton(buttonArea.getContext());
    

    As the OP points out, this has the added benefit of working in pretty much every similar setup without having to know the exact theme needed.