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不需要做任何改动就能支持主题切换。
以下是切换后的效果示例:
科技蓝:
田园绿:
热门推荐
如何重新连接被忽略的蓝牙设备?这些连接技巧对设备使用有何帮助?
电子科学排名20强:西电险胜成电,西交北邮互换,南大守住前10
鄂州“全周期”护航企业发展
咽炎不能吃哪些食物?一文详解咽炎患者的饮食禁忌与日常护理
60种水果热量表排行!减肥控糖人群建议收藏
每天应该吃多少水果?
有机硅树脂密度是多少,看完你就知道了
如何轻松创建高效的工作计划格式模板?
2025年高考报名考生号怎么查?身份证号一键查询考生号吗?
泡一壶生命的茶:在脆弱中寻找坚强
如何理解高中化学中金属材料的特性及应用
淡水浮游动物的采集及鉴定
柳下惠:百世之师与和圣的尊称由来
元旦聚餐,九道美味佳肴让家人感受浓浓亲情
中外合办大学有哪些?一文带你迅速了解招生方式、学费等信息!
青少年游戏成瘾怎么办?心理咨询师给出专业建议
如何选择高质量的大型机器设备减震垫以提高设备运行稳定性?
口臭吃什么可以改善
头痛、头晕不在意,可能会埋下重大隐患——慢性硬膜下血肿
树脂是什么?它是一种什么材料?盘点树脂材料在各个领域中的应用
三角洲行动K416改装秘籍:精准无后坐,配件优化全攻略!
网上咨询律师需要付费吗多少钱
如何为孕妻提供精神和情感支持?
相对湿度是什么意思 相对湿度和湿度的区别
国家统计局:三大PMI指数均在扩张区间继续上行,我国经济总体保持扩张
如何开始3DMax分布式渲染?
大豆的发源地——中国(探寻大豆文明的起源和演变)
宣武医院开出多奈单抗注射液全国首方
简谱常用符号介绍(值得收藏)
在职博士简章中对于学术成果的评估有哪些方法?