xvlaze
xvlaze

Reputation: 869

Change app theme globally programatically

I am writing an app which has both light and dark modes as declared here:

styles.xml

<style name="Noon" parent="Theme.AppCompat.NoActionBar">
    <item name="upper_bg">@drawable/day_sky_top</item>
    <item name="lower_bg">@drawable/day_sky</item>
    <item name="android:statusBarColor">@color/colorPrimaryDark</item>
</style>

<style name="Night" parent="Theme.AppCompat.NoActionBar">
    <item name="upper_bg">@drawable/night_sky_top</item>
    <item name="lower_bg">@drawable/night_sky</item>
    <item name="android:statusBarColor">@color/colorNightDark</item>
</style>

By following this answer, I created the following file:

/res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="customAttrs">
        <attr name="upper_bg" format="reference" />
        <attr name="lower_bg" format="reference" />
    </declare-styleable>
</resources>

And customized my ImageViews like this:

activity_main.xml

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/top_bg"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.65"
            android:src="?attr/upper_bg"/>

        <ImageView
            android:id="@+id/bottom_bg"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.35"
            android:src="?attr/lower_bg"/>
    </LinearLayout>

(Note that this is part of the code, all tags are properly closed.)

Everything works well provided I have:

boolean night = true;
setTheme(night ? R.style.Night : R.style.Noon);
setContentView(R.layout.activity_main); // or whatever activity I'm in.

on every single Activity of my app. Is there a way to run this code ONCE so that my theme changes globally?

Upvotes: 0

Views: 1227

Answers (2)

Julio Lemus
Julio Lemus

Reputation: 661

You can still add a BaseActivity to then override override fun onCreate(), delegating it the responsibility of setTheme() for any other Activity that inherits from it.

override fun onCreate(savedInstanceState: Bundle?) {
    val night = true
    setTheme(if (night) R.style.Night else R.style.Noon) 
}

if you don't prefer BaseActivity, you can create an extension function somewhere in charged of set theme according user preferences:

fun Activity.setTheme() {
    val night = true
    // Or even have more than two theme styles
    this.setTheme(if (night) R.style.Night else R.style.Noon)
}

To then be called like this:

override fun onCreate(savedInstanceState: Bundle?) {
    setTheme()
}

UPDATE: Java code

This class would be your base class for each required Activity

public abstract class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        boolean night = true;
        setTheme(night ? R.style.Night : R.style.Noon);
    }
}

So now, imagine you have to implement "Feature 1" and "Feature 2", so that, you just inherits them from BaseActivity.

"Feature 1":

public class Feature1Activity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); // <- BaseActivity's onCreate() will set theme for you
        setContentView(R.layout.activity_feature_1);
    }
}

"Feature 2":

public class Feature2Activity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); // <- BaseActivity's onCreate() will set theme for you
        setContentView(R.layout.activity_feature_2);
    }
}

Upvotes: 1

Mustafa Ibrahim
Mustafa Ibrahim

Reputation: 606

Yes you can ! You have to use method setDefaultNightMode() of class AppCompatDelegate that take the value of the theme you want to apply , the values available is MODE_NIGHT_NO & MODE_NIGHT_YES & MODE_NIGHT_FOLLOW_SYSTEM for light , night and phone default theme respectively , you can do something like this :

AppCompatDelegate.setDefaultNightMode(THE_MODE_YOU_WANT);

Its important to note that starting with AppCompat v1.0.0 , when this method get called it recreates all running activity to apply the theme .

If you are running Api 29 or higher , you should consider using Force Dark , you can check it all in the official document to get better understanding of it .

Enjoy !

Upvotes: 0

Related Questions