Android应用开发:如何实现主题切换功能
创作时间:
作者:
@小白创作中心
Android应用开发:如何实现主题切换功能
引用
CSDN
1.
https://blog.csdn.net/usabcd2/article/details/143061841
在Android应用开发中,提供主题切换功能可以增强用户体验,让用户根据个人喜好选择不同的界面风格。本文将详细介绍如何实现这一功能,包括定义主题、创建设置界面和处理用户选择等关键步骤。
前言
在Android应用开发中,如果业务功能已经基本完成,想要让应用更加开放和个性化,可以考虑为用户提供主题切换的功能。虽然网上已有不少相关资料,但本文将介绍一种更加优雅和流畅的实现方式。
要实现主题切换功能,需要完成以下三个主要步骤:
- 定义主题内容
- 创建设置界面
- 处理用户选择并切换主题
定义主题内容
首先需要定义应用中可用的主题。例如,我们可以定义5个主题:
- 霞光紫
- 科技蓝
- 烈焰红
- 田园绿
- 宝石灰
在常数类C中定义这些主题名称:
public static final String[] themeNames = {"霞光紫", "科技蓝", "烈焰红", "田园绿", "宝石灰"};
然后在values目录下创建theme.xml文件,定义每个主题的具体样式:
<resources>
<!-- Base application theme. -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item><!--original #3333FF-->
<item name="colorPrimaryVariant">@color/purple_700</item><!--#00008B-->
<item name="colorDim">#a569bd</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
</style>
<style name="Theme.MyApp.Blue" parent="Theme.MyApp">
<item name="colorPrimary">#1565c0</item>
<item name="colorPrimaryVariant">#1a237e</item>
<item name="colorDim">#5dade2</item>
</style>
<style name="Theme.MyApp.Red" parent="Theme.MyApp">
<item name="colorPrimary">#bb4444</item>
<item name="colorPrimaryVariant">#660000</item>
<item name="colorDim">#ec7063</item>
</style>
<style name="Theme.MyApp.Green" parent="Theme.MyApp">
<item name="colorPrimary">#1e8449</item>
<item name="colorPrimaryVariant">#145a32</item>
<item name="colorDim">#52be80</item>
</style>
<style name="Theme.MyApp.Gray" parent="Theme.MyApp">
<item name="colorPrimary">#888888</item>
<item name="colorPrimaryVariant">#424242</item>
<item name="colorDim">#a6acaf</item>
</style>
</resources>
同时在常数类C中定义主题值数组:
public static final Integer[] themeValues = {R.style.Theme_MyApp,
R.style.Theme_MyApp_Blue,
R.style.Theme_MyApp_Red,
R.style.Theme_MyApp_Green,
R.style.Theme_MyApp_Gray
};
定义设置界面
接下来需要创建一个设置界面,允许用户选择不同的主题。在setting.xml中定义界面布局:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingTop="30dp"
android:paddingEnd="10dp"
android:paddingBottom="0dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="80dp"
android:text="@string/system_setting"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/top_line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:background="@color/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="30dp"
android:orientation="horizontal">
<TextView
android:id="@+id/input_label"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:labelFor="@+id/input_content"
android:text="@string/server_ip" />
<EditText
android:id="@+id/input_content"
android:layout_width="match_parent"
android:layout_height="40dp"
android:autofillHints=""
android:background="@drawable/bg_edittext"
android:gravity="center_vertical"
android:inputType="text"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textSize="16sp"
tools:ignore="RtlHardcoded,RtlSymmetry,TextFields" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="30dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:labelFor="@+id/input_content"
android:text="@string/style" />
<LinearLayout
android:id="@+id/dropTheme"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:background="@drawable/bg_edittext"
android:clickable="true"
android:focusable="true"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="5dp"
android:paddingEnd="5dp">
<TextView
android:id="@+id/themeName"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="5dp"
android:layout_marginTop="0dp"
android:layout_weight="1"
android:autofillHints=""
android:background="@null"
android:textSize="16sp"
android:gravity="center_vertical"
android:textColor="?attr/editTextColor"
android:textColorHint="#DCDCDC"
tools:ignore="NestedWeights" />
<ImageView
android:layout_width="13dp"
android:layout_height="10dp"
android:layout_marginStart="5dp"
android:contentDescription="@string/image"
android:gravity="center"
android:scaleType="fitXY"
android:src="@drawable/icon_expanded"
app:tint="@color/expanded" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingEnd="0dp"
tools:ignore="RtlHardcoded,RtlSymmetry">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnOk"
style="?android:attr/button"
android:layout_width="100dp"
android:layout_height="45dp"
android:gravity="center"
android:insetTop="0dp"
android:insetBottom="0dp"
android:text="@string/ok"
android:textColor="@color/white" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnBack"
style="?android:attr/button"
android:layout_width="100dp"
android:layout_height="45dp"
android:layout_marginStart="30dp"
android:gravity="center"
android:insetTop="0dp"
android:insetBottom="0dp"
android:text="@string/back"
android:textColor="@color/white" />
</LinearLayout>
</LinearLayout>
</layout>
处理用户选择
在SettingActivity中处理用户选择主题的逻辑:
import android.content.Intent;
import android.os.Bundle;
import com.bob.app.common.BaseActivity;
import com.bob.app.databinding.SettingBinding;
import com.bob.app.func.FuncRunInt;
import java.util.Arrays;
public class SettingActivity extends BaseActivity {
SettingBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = SettingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
int oldThemeValue = sp.getInt("Theme", C.themeValues[0]);
binding.inputLabel.setText(R.string.server_ip);
binding.inputContent.setText(C.serverIp);
int i = Arrays.asList(C.themeValues).indexOf(oldThemeValue);
i = Math.max(i, 0);
binding.themeName.setText(C.themeNames[i]);
int finalI = i;
binding.dropTheme.setOnClickListener(v -> {
FuncRunInt fri = (pos) -> {
if (pos != finalI) {
int newTheme = C.themeValues[pos];
editor.putInt("Theme", newTheme);
editor.commit();//保存用户选择的主题
Intent intent = new Intent();
setResult(RESULT_OK, intent);
recreate();
}
};
showPopupMenu(v, C.themeNames, fri, 220);
});
binding.btnBack.setOnClickListener(v -> goBack());
binding.btnOk.setOnClickListener(v -> goBack());
}
private void goBack() {
String serverIp = binding.inputContent.getText().toString();
editor.putString("serverIp", serverIp);
editor.commit();
C.serverIp = serverIp;
C.serverUrl = String.format(C.SERVER_URL, serverIp);
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
}
当用户选择某个主题后,需要保存这个主题标识到硬盘,并切换当前主题。这里使用recreate()方法来实现主题切换,可以给用户提供流畅的体验。
切换主题的具体实现
为了确保每个界面都能正确切换主题,需要在基类BaseActivity中处理主题切换的逻辑:
@SuppressWarnings("unused")
public abstract class BaseActivity extends AppCompatActivity {
protected SharedPreferences sp;
protected SharedPreferences.Editor editor;
protected final Set<String> callingActions = new HashSet<>();
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
sp = getSharedPreferences(getString(R.string.app_name), MODE_PRIVATE);
editor = sp.edit();
processTheme();
super.onCreate(savedInstanceState);
}
private void processTheme() {
int theme = sp.getInt("Theme", C.themeValues[0]);
int pos = Arrays.asList(C.themeValues).indexOf(theme);
if (pos < 0) {
editor.remove("Theme");
editor.commit();
theme = C.themeValues[0];
}
setTheme(theme);
}
public void showPopupMenu(View anchorView, String[] menuItems, FuncRunInt fri, int widthInDp) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@SuppressLint("InflateParams") View popupView = inflater.inflate(R.layout.popup_menu_list, null);
int widthInPx = (int) (widthInDp * getResources().getDisplayMetrics().density);
final PopupWindow popupWindow = new PopupWindow(popupView,
widthInPx,
ViewGroup.LayoutParams.WRAP_CONTENT,
true);
popupWindow.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.popup_background))); // Set background color or drawable
ListView listView = popupView.findViewById(R.id.listView);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.list_item_left, menuItems);
listView.setAdapter(adapter);
listView.setOnItemClickListener((parent, view, position, id) -> {
fri.apply(position);
popupWindow.dismiss();
});
popupWindow.setOutsideTouchable(true);
popupWindow.showAsDropDown(anchorView);
}
}
需要注意的是,processTheme()方法必须在调用super.onCreate(savedInstanceState)之前执行,否则主题切换将不会生效。
通过以上步骤,我们就可以实现一个完整的主题切换功能。由于具体的切换逻辑放在了基类中,因此其他Activity不需要做任何改动就能支持主题切换。
以下是切换后的效果示例:
科技蓝:
田园绿:
热门推荐
Auto.js截图范围怎么写
海姆达尔:阿斯加德守护者揭秘
怎么知道自己的喜用神和忌神,如何具体分析喜用神及忌用神
想拥有弹性转动核心的能力?这4招动作不可错过!
越忙越胖?跟这个“激素”有关!还会影响情绪!这3类食物能解压……
文学的未来:人工智能在小说创作中的角色
科学选用染发剂和焗油膏:守护秀发之美
《泰坦尼克号》:一场浪漫与灾难的史诗传奇
3C类目包括哪些具体产品?3c类目详细介绍
《暗黑破坏神2:重制版》开荒角色Top排行
如何提高足球观赛技巧(以足球技巧为主的观赛指南)
仅需15分钟,三个拉伸动作强化上半身力量
《启蒙辩证法》中的技术理性与自我持存批判
线路被砍!第四期地铁规划有变,长沙这些地铁,可能不修了!
豆瓣开分8.9,《日光之城》刷新2024国产都市剧纪录
运维必考的8个信创证书:从入门到精通的详细指南
Excel中快速插入多行的五种方法
荆门旅游景点排名前十:从自然景观到人文古迹
学士学位的含义与重要性
申请执行,需要提供哪些财产线索?一文详解!
黄财神吊坠佩戴禁忌全解析
全国爱耳日:全生命周期听力健康管理——耳朵的一生
数学公式优化:泰勒级数在arcsin函数中的应用与优化
如何准确把握股价站稳的含义?这种股价站稳的判断依据有哪些局限性?
人均收入和人均可支配收入区别详解
八字命理的理性思考:婚姻爱情与事业的多面分析
连江海边旅游景点大全:必去景点推荐与排名
杜甫《春夜喜雨》诗意赏析及朗读
柔然的崛起:从奴隶到北方霸主的历史征程
想在暑假做近视手术?这些事项要注意→