2

I am failing to implement a Theme-Selection in my Android App.

This question was asked before, but the solution - call application.setTheme(theme_id) before setContentView - does not work for me.

There are 2 themes defined in styles.xml:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

<!-- second theme, hard coded colors for testing -->
<style name="DarkTheme" parent="Theme.AppCompat.DayNight">
    <item name="colorPrimary">#ff0000</item>
    <item name="colorPrimaryDark">#00ff00</item>
    <item name="colorAccent">#0000ff</item>
</style>

Both themes can be used in AndroidManifest.xml:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"
    >
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

or

<application
    ...
    android:theme="@style/DarkTheme"
    >
    ...
</application>

The result is as expected, the styles are different.

But the theme selection should be done by the user, so I tested the basic functionality by using android:theme="@style/AppTheme" in the manifest and hard coding this:

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // yes, the line appears in the log:
    Log.d("MYAPP", "calling application.setTheme(R.style.DarkTheme)")
    application.setTheme(R.style.DarkTheme)

    setContentView(R.layout.activity_main)
  }
}

The result: the theme defined in AndroidManifest.xml is still used, the setTheme call has no effect.

No compile warnings, no runtime messages, no suspicious log entries.

Kotlin is used, but this shouldn't be the cause of the problem.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
Gisela
  • 1,194
  • 2
  • 16
  • 30
  • 1
    No. This is not a duplicate. As I elaborately described in the question there are no problems with the themes. Both inherit from AppCompat, both are tested, both are working as expected when used in the manifest. But the call to application.setTheme(R.style.DarkTheme) within onCreate does not work. And that is the problem. Sorry if this was not clear enough. – Gisela Apr 18 '18 at 13:22
  • Where is application defined in the Activity? Why aren't you using an Application class to set the application theme? – OneCricketeer Apr 18 '18 at 13:54
  • 1
    Also, you need to restart the Activity for any theme changes to be noticed, anyway – OneCricketeer Apr 18 '18 at 13:55
  • Possible duplicate of [Change Activity's theme programmatically](https://stackoverflow.com/questions/11562051/change-activitys-theme-programmatically) – Code-Apprentice Apr 18 '18 at 14:02
  • "call setTheme **before** super.onCreate" as stated in the linked question – Code-Apprentice Apr 18 '18 at 14:05
  • @cricket_007: I do use the application: application.setTheme(R.style.DarkTheme) is the same as Java getApplication.setTheme(R.style.DarkTheme) – Gisela Apr 18 '18 at 14:22
  • @Code-Apprentice: I've tested that. No improvement. – Gisela Apr 18 '18 at 14:28

1 Answers1

1

Finally I managed to get it work.

To be clear: the code I posted first:

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    application.setTheme(R.style.DarkTheme)
    setContentView(R.layout.activity_main)
  }
}

should work, according to many answers to similar questions on SO.

But it doesn't.

Also this:

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    application.setTheme(R.style.DarkTheme)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
  }
}

or other permutations in the sequence of the function calls doesn't solve the problem.

Not a real solution, but a workaround for the problem is the answer from Björn Kechel, found here: Change Activity's theme programmatically

Overwriting the getTheme method in Activity, instead of calling the none functional setTheme method:

class MainActivity : AppCompatActivity() {

  protected var selectedTheme : String = ""
  lateinit protected var sharedPreferences : SharedPreferences
  var initialized = false

  fun init() {
    if (!initialized) {
      initialized = true
      sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
    }
    if ("" == selectedTheme) {
      selectedTheme = sharedPreferences.getString("selected_theme", "AppTheme")
    }
  }

  override fun getTheme(): Resources.Theme {
    init()
    val theme = super.getTheme()
    when(selectedTheme) {
      "DarkTheme" -> {
        theme.applyStyle(R.style.DarkTheme, true)
      }
      "AppTheme" -> {
        theme.applyStyle(R.style.AppTheme, true)
      }
    }
    return theme
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    settings.setOnClickListener {
      openSettings()
    }
  }

  fun openSettings() {
    val intent = Intent(baseContext, SettingsActivity::class.java)
    startActivity(intent)
  }
}
Gisela
  • 1,194
  • 2
  • 16
  • 30