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不需要做任何改动就能支持主题切换。
以下是切换后的效果示例:
科技蓝:
田园绿:
热门推荐
旧车置换新车保留号牌流程及法律规定全解析
五线谱记忆口诀:从基础到进阶的全面指南
硝酸纤维素膜(NC膜)技术解析与应用指南
石敬瑭与刘知远:一段复杂的历史关系
当一个家庭选择要了第二个孩子,可能会对第一个孩子带来哪些影响?
这7座大桥见证新中国桥梁钢迭代升级之路
外币现钞如何辨别真假
干粉灭火器使用与维护:了解年检的重要性
三电系统电磁兼容实战:构建数字世界的“电磁护盾”
仪器仪表的革新与未来趋势探讨
传统测量仪器的操作难度如何与机器视觉智能测量仪比较
中国城市综合发展指标:北京连续8年居首,四大一线城市格局稳固
鼻炎患者必看:解锁远离鼻炎的N个生活小妙招!
医生解答:进食辣味食物后出汗的四大原因
注意!这4个部位出汗多,可能暗示身体有问题
5W分析法:一种通用的问题解决工具
营养均衡的这8道菜,适合全家人吃!一份菜,提供每天营养所需!
15个实用技巧,教你写出吸引人的文章标题
春意盎然的诗句精选赏析
“颠狂柳絮随风去,轻薄桃花逐水流。”--杜甫《漫兴》全诗翻译赏析
“颠狂柳絮随风去,轻薄桃花逐水流。”--杜甫《漫兴》全诗翻译赏析
如何查找和分析企业年报?这些信息在投资决策中有哪些重要性?
股票回盘是什么意思,它在交易中如何体现?
团队协作的关键要素与实践方法
诸暨古井——桔槔古井
利用大数据和AI技术优化用户画像,提升营销自动化效果
每日期货全景复盘3.18:油脂板块焦点分散且难以形成合力,短期走势容易反复
如何高效进行工作进度管理:实用技巧与工具推荐
小叶黄杨怎么养
小叶黄杨怎么养护?小叶黄杨盆景造型