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不需要做任何改动就能支持主题切换。
以下是切换后的效果示例:
科技蓝:
田园绿:
热门推荐
新加坡留学生活成本解析
八字命理学中的阴阳属性:如何影响命局分析?
友情与关爱:在人生中培养深厚的人际关系
维生素D滴剂的用量是多少
别只盯着高考分!这篇综评指南带你另辟蹊径上名校
我国药监局严查假药,保障人民群众用药安全
国家医保局:“码上查”有力打击假药、劣药、回流药
C语言中long数据类型的含义、使用场景及注意事项
如何在银行办理个人养老金账户?
入门学易经要看什么书
那牙医这个职业到底多赚钱呢?
购房合同与备案号:概念解析与法律辨析
域名备案号从哪里查
OpenWrt UPnP功能如何优化网络连接
【運動科學】喜歡跑長跑的你都該注意的骨骼健康:預防疲勞性骨折的關鍵

怎样在银行办理银行存单的支取条件?
超高频RFID技术助力电动自行车加强安全监管
乌鲁木齐眼科医院哪家好?排名前十的眼科医院详细介绍
SPSS26统计分析笔记——2 描述统计
7年,从军人到辅警再到民警,石嘴山警营的奋斗故事一直在上演
军人退伍立功:退伍军人的杰出成就
劳动报酬包括哪几部分?(附:如何界定不足额、不及时和拖欠工资的?)
神经网络算法——损失函数(Loss Function)
10M宽带如何合理分配?
五问分析法在问题解决中的应用
收好这份痘痘时期的 “运动秘籍”
跑步是否能促进皮肤新陈代谢
文化接近:如何促进跨文化交流与理解
每天吃一个鸡蛋和一个玉米
城市更新改造项目:如何提升城市面貌和居民生活质量?