Как сделать смену темы?
как сделать смену темы во всё android приложении? Вот код, который у меня получился: Settings.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
public class Settings extends AppCompatActivity {
Spinner choose_theme;
DatabaseReference reference;
FirebaseUser firebaseUser;
FirebaseAuth auth;
FirebaseUser user = auth.getInstance().getCurrentUser();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
String[] themes = {
"Сера буро малиновая тема",
"Фиолетовая тема",
"Зелёная тема",
"Оранжевая тема",
"Жёлтая тема",
"Красная тема",
"Синия тема",
"Чёрная тема",
"Светлая тема",
};
choose_theme = findViewById(R.id.theme);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, themes);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
choose_theme.setAdapter(adapter);
AdapterView.OnItemSelectedListener itemSelectedListener = new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String theme = (String) parent.getItemAtPosition(position);
//Set new Theme
if (theme.equals("Красная тема")){
setTheme(R.style.AppTheme_Red);
}else if(theme.equals("Жёлтая тема")){
// yellow theme
setTheme(R.style.AppTheme_Yellow);
}else if(theme.equals("Синия тема")){
// blue theme
setTheme(R.style.AppTheme_Blue);
}else if (theme.equals("Чёрная тема")){
// black theme
setTheme(R.style.AppTheme_Black);
}else if(theme.equals("Светлая тема")){
// white theme
setTheme(R.style.AppTheme);
}else if (theme.equals("Зелёная тема")){
// green theme
setTheme(R.style.AppTheme_Green);
}else if (theme.equals("Фиолетовая тема")){
// purple theme
setTheme(R.style.AppTheme_Purple);
}else if (theme.equals("Сера буро малиновая тема")){
// gray brown crimson theme
setTheme(R.style.AppTheme_GR);
}else if (theme.equals("Оранжевая тема")){
// orange theme
setTheme(R.style.AppTheme_Orange);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
choose_theme.setOnItemSelectedListener(itemSelectedListener);
}
@Override
protected void onStart() {
super.onStart();
}
}
Ответы (2 шт):
Если неинтересно эту всю простыню читать, то решение в самом низу отмечено :)
Я могу предложить сохранить данные про тему в SharedPreferences, и дальше тащить данные где вам нужно. Для начала я бы советовал немного изменить ваш код по выбору темы в слушателе виджета:
AdapterView.OnItemSelectedListener itemSelectedListener = new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
выше представлен пустой слушатель. Можно немного более лаконично все сделать:
switch (parent.getItemAtPosition(position)) {
case "Красная тема":
setTheme(R.style.AppTheme_Red)
break;
...
default:
// здесь можно установить дефолтную тему приложения например
break;
}
теперь по поводу того как сетить тему во всех активностях/фрагментах. Вам нужно сохранить в память устройства переменную которая позволит идентифицировать вашу тему и выбрать соответствующий стиль. Для этого мы при выборе пункта списка будет записывать данные о выборе. Вот есть туториал на тему хранения данных. В методе onCreate() вашей активности настроек мы инициализируем переменную:
SharedPreferences mSettings = getSharedPreferences("app_data", Context.MODE_PRIVATE);
и при выборе темы записываем данные:
mSettings.edit().putString("theme_col", parent.getItemAtPosition(position)).apply();
дальше в любом месте активности вы можете получить выбранную и сохраненную ранее переменную и засетить тему:
switch (mSettings.getString("theme_col", "")) {
case "Красная тема":
setTheme(R.style.AppTheme_Red)
break;
...
default:
// здесь можно установить дефолтную тему приложения например
break;
}
с учетом того что кусок кода для выбора темы будет повторяться как минимум в двух местах я бы советовал его вынести в отдельный метод отдельного класса и просто передавать в него стринг темы, но вот вопрос в том что он будет возвращать. Если в отдельном классе сетить тему, то нужно передавать так же контекст, а в противном случае вынос в отдельный класс может быть не совсем целесообразным :)
UPDATE
Я что-то протупил немного, когда сказал что класс ничего не сможет вернуть того что нам нужно. Стиль это Integer, поэтому и метод должен его возвращать. Делаем класс-синглтон:
public class Singleton {
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
добавляем метод:
public class Singleton {
...
Integer processTheme(String name){
...
}
}
сам метод будет делать то же что и вы делали только немного иначе. Но перед этим объявим два массива: массив с русскими названиями тем и их идентификаторами:
String[] themes = {
"Сера буро малиновая тема",
"Фиолетовая тема",
"Зелёная тема",
"Оранжевая тема",
"Жёлтая тема",
"Красная тема",
"Синия тема",
"Чёрная тема",
"Светлая тема",
};
Integer[] themesIDs = {
R.style.MaterialAlertDialog_MaterialComponents_Title_Panel_CenterStacked,
R.style.Platform_MaterialComponents,
R.style.Platform_MaterialComponents_Light
};
я стили брал вообще левые, но суть я думаю будет ясна. Дальше вот сам метод:
Integer processTheme(String name){
return themesIDs[Arrays.asList(TYPES).indexOf(name)];
}
целый класс выглядит так:
public class Singleton {
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
String[] themes = {
"Сера буро малиновая тема",
"Фиолетовая тема",
"Зелёная тема",
"Оранжевая тема",
"Жёлтая тема",
"Красная тема",
"Синия тема",
"Чёрная тема",
"Светлая тема",
};
Integer[] themesIDs = {
R.style.MaterialAlertDialog_MaterialComponents_Title_Panel_CenterStacked,
R.style.Platform_MaterialComponents,
R.style.Platform_MaterialComponents_Light
};
Integer processTheme(String name){
return themesIDs[Arrays.asList(TYPES).indexOf(name)];
}
}
дальше при выборе темы из списка на экране настроек вы вызываете данный метод:
String theme = (String) parent.getItemAtPosition(position);
mSettings.edit().putString("theme_col", theme).apply();
setTheme(Singleton.getInstance().processTheme(theme));
весь слушатель будет выглядеть так:
AdapterView.OnItemSelectedListener itemSelectedListener = new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String theme = (String) parent.getItemAtPosition(position);
mSettings.edit().putString("theme_col", theme).apply();
setTheme(Singleton.getInstance().processTheme(theme));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
дальше как я уже говорил выше, вы можете получить выбранную переменную в любом месте и засетить тему:
setTheme(Singleton.getInstance().processTheme(mSettings.getString("theme_col", "")));
Итоговое решение
Но и этот метод не совсем идеальный. По факту вам нужно просто сохранять в память позицию элемента в массиве чтобы ее потом передать. В таком случае вы сохраняете в память Integer позиции:
mSettings.edit().putInt("pos",position).apply().apply();
строчка выше должна находится в теле слушателя выбора пункта списка. Метод в синглтоне и сам синглтон немного изменится и будет таким:
public class Singleton {
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
Integer[] themesIDs = {
R.style.MaterialAlertDialog_MaterialComponents_Title_Panel_CenterStacked,
R.style.Platform_MaterialComponents,
R.style.Platform_MaterialComponents_Light
};
Integer processTheme(Integer pos){
return themesIDs[pos];
}
}
и дальше соответственно для возврата идентификатора темы делаем в слушателе выбора темы так:
setTheme(Singleton.getInstance().processTheme(position));
а в других классах делаем так:
setTheme(Singleton.getInstance().processTheme(mSettings.getInt("pos", 0)));
Можно конечно долго и нудно разрабатывать свои темы и проч., но по мне (IMHO безусловно) - это какой то антипаттерн.
Можно сделать все гораздо проще. Грубо говоря есть 3 темы:
- Системная (дефолтная тема установленная в системе)
- Ночная (она же night)
- Дневная (она же light)
Выбор той или иной темы делается просто:
//системная тема
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
//light тема
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
//ночная тема
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
Если вы сильно не увлекались созданием ресурсов типа res/values/themes.xml и т.д., то это все работает обычно из коробки. Если увлекались, то придется возможно попотеть прописывая themes.xml