- Android: Changing app theme at runtime
- Changing the theme at runtime
- Possible issues
- Recursively change attributes on all your Views
- Possible issues
- There is a third option?
- What I recommend
- Do you know a better way of doing this?
- Темы и стили в Android без магии. И как их готовить с SwitchCompat
- Содержание
- Введение
- Новый стиль для SwitchCompat
- Стиль в верстке
- Стиль в теме. Тема назначается через Manifest
- Стиль в теме. Тема назначается программно
- Другие View
- Ресурсы
- Android Set Theme Dynamically
- android change theme programmatically
- Define Attributes
- Define Themes
- Main Screen
- Theme Settings
- Add Themes to Settings
- Main Activity
- Reset Theme Dynamically
Android: Changing app theme at runtime
Jun 28, 2015 · 5 min read
Every so often, I see a question posted on StackOverflow which is effectively asks how to change the themes of an app at runtime. The use case is often that there is a setting, button or check box which can switch between different colour themes or between something like day and night mode.
Every time such a requirement comes up, the fir s t thing a quick Google search shows is that it’s not possible to change themes at runtime. That being said, it is possible to change the theme of an Activity, however only in the`onCreate` method and only before `super` is called.
This is problematic because it’s hard to provide a seamless experience to the user if you have to restart the app or Activity in order to change the theme. So our second option is to recursively loop through all of our views and set their attributes each time an Activity or Fragment is created. This way, when the theme is changed, you can loop through all the Views again and change the attributes to reflect the new theme. Neither of these options is ideal, you may even want to consider a hybrid of these two approaches. I’ve provided an example implementation of each of these methods below.
Changing the theme at runtime
As mentioned before, it’s only possible to change the theme of an Activity in the `onCreate` method and that to only before `super` has been called. Changing the theme is fairly straight forward, something like below should do it:
This is pretty straight forward, however this works when an activity is first created and has no effect on the current open Activity or any backgrounded Activities. In order to affect change on the current Activity, we’ll have save the state of the current Activity and relaunch the activity, in order to make this experience seamless for the user, you have 2 options, either remove all transition animations for Activities or change them to provide a nice fade in effect. The result of this approach is shown in the video below.
As you can see, the approach produces a pretty nice result. If you don’t want a fade in effect, remove all animations for Activity transition and you should have a sudden change.
The code to achieve this is in my gist “Transition themes”.
Possible issues
- In order to achieve theme change in this manner, you have to make sure that all your View inherit attributes that matter from the theme and do not in-line any attributes that matter like background colour or text colour.
- Saving your Activity state and relaunching it may not be as smooth as in my example above. This depends a lot of how heavy your Activity and it’s layouts are. Some elements may need to be reloaded.
- Any Activities that are already open in the background will not have the theme change applied to it when you go back to them. The easiest solution to this is to close all the backgrounded Activities, or else, you’ll have to save their state, close them and relaunch them in `onStart` or `onResume`.
Recursively change attributes on all your Views
As much as we hope that the theme can contain all our formatting, we invariably need to override a text colour or background colour in-line in our layout or an in a style attribute and this needs to be changed programmatically. In this scenario, you would likely have to check the appropriate Views or all Views to see if they are consistent with your set theme. If you know which Views are likely to be affected and can check them directly, nothing could be better. If not, you will have to loop through all the View in your layout and check them. The code to do this depends heavily on your project and it’s requirements, however, the skeleton code for checking all your Views be something like this:
Possible issues
- Depending on how complex your screens are, your code for checking each View can become quite complex. An alternate solution can be to set the Views theme related attributes when we build our Activity, Fragment or Layout. This will still add to the complexity of your code.
- There is a time and performance cost to doing this for each layout.
There is a third option?
You could bundle duplicate layouts for each of your themes where the only difference between each layout is that the style or in-line style related attributes are different. Then in your code, depending on the selected theme you inflate or set the appropriate layout. This approach while very simple, however it has the same issues as the first option.
What I recommend
If this is a requirement for your app, I recommend you research what is possible before you try any of these approaches. If all you want to do is change some text colour and the colour of the Toolbar and tabs, this is possible without having to change the theme. I would take a look at the Design Support Library.
If you are going to do down one of the routes I have talked about above, I would recommend not getting too attached to any one approach and to combine all three approaches above. Find the best fit for your particular situation.
Also, if you’re going to need to change the colour of your drawable assets, my article on how to change the colour of drawable assets may help.
Do you know a better way of doing this?
I’m honestly asking the readers, if there are any out there, to chime in and tell me if there is a better way to handle runtime theme changes. It’s a topic I have researched and Google’d, however, I’m just not happy with what I’ve found so far. If you have a better approach or some advice on the matter, I’d love to hear it.
For more Android development article or follow me on LinkedIn, Twitter or Google+.
Источник
Темы и стили в Android без магии. И как их готовить с SwitchCompat
В предыдущей статье мы рассмотрели как использовать темы и стили на уровне кода, на примере кастомной view. В этой статье давайте разберем несколько способов стилизации стандартного ui элемента, а в частности SwitchCompat.
Содержание
Введение
Не всегда оформление по умолчанию стандартного UI элемента устраивает дизайнера. Давайте разберем, как поменять внешний вид элемента на примере SwitchCompat.
Для решения задачи нам нужно:
- Создать свой стиль для SwitchCompat.
- Каким-то образом задать этот стиль SwitchCompat.
Назначить стиль SwitchCompat можно несколькими способами, например:
- Указывать для каждой view в верстке экранов через атрибут style.
- Создать тему с переопределенным атрибутом switchStyle и назначить эту тему в манифесте для всего приложения или конкретной активити. Это изменит внешний вид view для всего приложения/активити.
- Тему также можно установить программно, в коде активити. При необходимости ее можно менять «на лету».
Новый стиль для SwitchCompat
В ресурсах создадим новый стиль MySwitchStyle, наследуем оформление от Widget.AppCompat.CompoundButton.Switch, задав parent. Можно и не наследовать, но тогда придется указать все значения, даже которые мы не планируем менять.
Чтобы что-то изменить, надо переопределить требуемые атрибуты. Атрибуты можно посмотреть в документации.
В документации видим несколько атрибутов. Они указаны в виде, как если бы мы обращались к ним в коде (например, вот так R.styleable.SwitchCompat_android_thumb). Я расшифрую только часть из них, чтобы не было сомнений. Назначение остальных несложно понять из документации.
В коде | В xml |
SwitchCompat_android_thumb | android:thumb |
SwitchCompat_thumbTint | thumbTint |
SwitchCompat_track | track |
SwitchCompat_trackTint | trackTint |
- android:thumb — ресурс для подвижной части SwitchCompat
- track — ресурс для неподвижной части SwitchCompat
- thumbTint — позволяет окрашивать подвижную часть в нужные цвета в зависимости от состояния SwitchCompat
- trackTint — позволяет окрашивать неподвижную часть в нужные цвета в зависимости от состояния SwitchCompat
В качестве примера изменим цвет thumb (кружочка) — пусть во включенном состоянии он будет оранжевым, в выключенном — зеленым. Некрасиво, но наглядно.
Нам понадобится селектор в папке color наших ресурсов. Файл selector_switch_thumb.xml
Теперь зададим атрибут thumbTint в нашем стиле.
Теперь все SwitchCompat, получившие каким-то образом стиль MySwitchStyle, будут выглядеть по-новому.
Стиль в верстке
Самый тривиальный и негибкий способ.
- Стиль применяется при inflate ресурса layout.
- Повлиять программно мы никак не можем.
- Указывать каждый раз в верстке неудобно. И можем забыть.
Стиль в теме. Тема назначается через Manifest
Создаем тему AppTheme и задаем значение атрибуту switchStyle. Значением является наш стиль MySwitchStyle.
Тема может быть указана в манифесте для всего приложения
Или для конкретной активити
Теперь все SwitchCompat будут иметь новый внешний вид. Без изменения в верстке.
- Плюсы — Можем менять внешний вид для всего приложения сразу.
- Минусы — налету менять не получится.
Стиль в теме. Тема назначается программно
Для того, чтобы установить тему для активити программно, нужно вызвать метод активити setTheme(themeResId).
Давайте менять тему активити в зависимости от состояния Switch.
- Устанавливаем тему программно, вызвав setTheme. Метод надо вызывать до super.onCreate(savedInstanceState). В onCreate у нас происходит инициализация фрагментов (когда они есть).
- Задаем начальное состояние Switch в зависимости от темы.
- Устанавливаем листенер, который при изменении Switch меняет тему в настройках и перезапускает активити через метод активити recreate().
Результат
Другие View
Чтобы переопределить стиль для SwitсhView для всего приложения, мы переопределили значение атрибута switchStyle, можно догадаться, что такие атрибуты есть и для других View.
- editTextStyle
- checkboxStyle
- radioButtonStyle
Как их искать? Я просто смотрю исходники, через Android Studio.
Заходим в тему, зажимаем ctrl, кликаем на родителе нашей темы. Смотрим, как описывают тему ребята из Google. Смотрим, какой атрибут определяется и от какого стиля можно отнаследоваться. Пользуемся.
Кусок из темы Base.V7.Theme.AppCompat.Light.
Ресурсы
Статья не претендует на полный справочник. Код умышленно сокращен. Я ставил задачу дать общее понимание — как это работает и зачем это нужно. Дальше все легко ищется в документации и в стандартных ресурсах.
Источник
Android Set Theme Dynamically
In this article “android change theme programmatically” we will see how to change the theme dynamically. We want to provide a new setting which will allow one to choose from a list of themes. After the theme is selected, when we go back to our activity, we want the theme to be applied. Also, we will take special care for android change theme programmatically.
Steps include the following:
- If you want to use any custom attributes, define those attributes first.
- If you are relying on colors or dimensions, define the values and associate them with proper names.
- Create few themes. When you group a set of style attributes and give a name to it, it becomes your theme.
- Create your main screen. You should be able to set the layout attributes to the custom attributes defined.
- Apply theme programatically before you call the super.onCreate() and setContentView()
- You will set the theme calling Activity.setTheme(resource)
Let’s try the above steps on our example.
android change theme programmatically
Define Attributes
We will define few custom attributes using attr element. It has a name attribute and format . This is just like declaring your program variables.
Define Themes
Now we will create our themes. Before that you can define some style attributes and assign a value. For example, below we define few color resources.
Now we will group our custom attributes together, give a name to the style and assign some values to them. We have created two themes Theme1 and Theme2 .
Each theme consists of textColoe , background and textSize .
Main Screen
In our main screen, we will refer to our custom attributes using ?attr , for example ?attr/textColor . Once a theme is applied, the actual value will replace the placeholder.
Theme Settings
In order to know which theme to apply, we will use android’s perferences. We will define a ListPreference which will display a list of theme names. User will select one of them.
The theme values are specified in string-array .
Our theme values will be defined in themes string-array .
Add Themes to Settings
ThemePreferenceActivity is our PreferenceActivity . We will set its content view to preferences.xml using addPreferencesFromResource
The user will chose the theme from the displayed themes. After the selection when we navigate back to the previous activity, we want the theme to be applied which means the activity which called the prefernces activity should get notified of the change when it returns back to the activity.
We will set an OnPreferenceChangeListener to set a result code. This is done so that the Activity which called the perference activity gets notified and from the result code we know whether the theme preference was changed. The result code will be accessed by the Activity class in onActivityResult .
Main Activity
Before super.onCreate and setContentView is called, we read our theme from the preferences and apply using setTheme method. When we change our theme from settings, the main activity should get notified of the change so that it can restart itself and which in turn will re-apply the new theme. This is important so that the new theme gets applied dynamically.
The trick here is to call the perferences activity using startActivityForResult .
We pass a request code along with the activity class name. We will get this request code back in the callback onActivityResult() . This way we will know from which activity we are coming back.
In the main activity’s onActivityResult() , we will look into the request code and result code. The request code will help us to know something about previous activity. Likewise, we can use the result code to hint about some action that we performed in our previous activity.
Reset Theme Dynamically
Main screen opens with the default Theme.
Let’s change the theme use the settings.
Change Theme in Settings
Reset the theme to Theme2.
Set the theme to Theme2
Theme2 is applied, Main screen look is changed.
Источник