Android skin changing function is nothing new. There are many third-party skin changing libraries and implementation schemes on the market. The main reasons for choosing Tencent's QMUI library to demonstrate the skin changing function of APP are as follows: 1. The implementation process of skin changing function is simple and easy to understand; 2. It can easily adapt to the Dark Mode provided by Android 10; 3. It can also whitewash various components and effects of QMUI (this is important, 😁 Ha ha ~);
1. Realization of skin changing process:
1.1 new project
Create an empty project through Android studio (the process of creating a project is omitted), and add QMUI dependency:
implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
1.2. Define attr and its implementation style (emphasis)
This step requires us to work with designers to sort out a set of color and background resources for App use. Then we name it in the form of attr in xml. This project case:
src/main/res/values/styles.xml:
<resources> <attr name="colorPrimary" format="color" /> <attr name="colorBg1" format="color" /> <attr name="colorBg2" format="color" /> <attr name="colorBg3" format="color" /> <attr name="colorTextWhite" format="color" /> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimaryDefault</item> <item name="colorBg1">@color/colorBgDefault1</item> <item name="colorBg2">@color/colorBgDefault2</item> <item name="colorBg3">@color/colorBgDefault3</item> <item name="colorTextWhite">@color/colorTextWhite</item> </style> <style name="app_skin_1" parent="AppTheme"> <item name="colorPrimary">@color/colorPrimarySkin1</item> <item name="colorBg1">@color/colorBgDefault1Skin1</item> <item name="colorBg2">@color/colorBgDefault1Skin2</item> <item name="colorBg3">@color/colorBgDefault1Skin3</item> </style> <style name="app_skin_2" parent="AppTheme"> <item name="colorPrimary">@color/colorPrimarySkin2</item> <item name="colorBg1">@color/colorBgDefault2Skin1</item> <item name="colorBg2">@color/colorBgDefault2Skin2</item> <item name="colorBg3">@color/colorBgDefault2Skin3</item> </style> </resources>
src/main/res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimaryDefault">#FCE4EC</color> <color name="colorBgDefault1">#F06292</color> <color name="colorBgDefault2">#EC407A</color> <color name="colorBgDefault3">#880E4F</color> <color name="colorTextWhite">#FFFFFF</color> <color name="colorPrimarySkin1">#E3F2FD</color> <color name="colorBgDefault1Skin1">#90CAF9</color> <color name="colorBgDefault1Skin2">#42A5F5</color> <color name="colorBgDefault1Skin3">#0D47A1</color> <color name="colorPrimarySkin2">#FAFAFA</color> <color name="colorBgDefault2Skin1">#757575</color> <color name="colorBgDefault2Skin2">#424242</color> <color name="colorBgDefault2Skin3">#212121</color> </resources>
style supports inheritance. Take the above example, app\_skin\_1 inherits from AppTheme. When looking for its value through attr, if it is in app\_skin\_1 if it doesn't find it, it will go to AppTheme to look for it. Therefore, we can take the theme of the App as our skin, and other skins inherit from this skin.
1.3 custom skin change management
Different skins and colors of APP have been defined. We need to define a class to interface with QMUI to manage these skins. The code functions include skin loading, switching and other operations.
src/main/java/com/qxc/testandroid/QDSkinManager.java:
package com.qxc.testandroid; import android.content.Context; import android.content.res.Configuration; import com.qmuiteam.qmui.skin.QMUISkinManager; public class QDSkinManager { public static final int SKIN_DEFAULT = 1; public static final int SKIN_1 = 2; public static final int SKIN_2 = 3; public static void install(Context context) { QMUISkinManager skinManager = QMUISkinManager.defaultInstance(context); skinManager.addSkin(SKIN_DEFAULT, R.style.AppTheme); skinManager.addSkin(SKIN_1, R.style.app_skin_1); skinManager.addSkin(SKIN_2, R.style.app_skin_2); boolean isDarkMode = (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; int storeSkinIndex = QDPreferenceManager.getInstance(context).getSkinIndex(); if (isDarkMode && storeSkinIndex != SKIN_2) { skinManager.changeSkin(SKIN_2); } else if (!isDarkMode && storeSkinIndex == SKIN_1) { skinManager.changeSkin(SKIN_1); }else{ skinManager.changeSkin(storeSkinIndex); } } public static void changeSkin(int index) { QMUISkinManager.defaultInstance(QDApplication.getContext()).changeSkin(index); QDPreferenceManager.getInstance(QDApplication.getContext()).setSkinIndex(index); } public static int getCurrentSkin() { return QMUISkinManager.defaultInstance(QDApplication.getContext()).getCurrentSkin(); } }
1.4. Custom skin saving class
After we switch the skin, we need to save the switched skin information. When we start the APP next time, we will load the switched skin directly.
src/main/java/com/qxc/testandroid/QDPreferenceManager.java:
package com.qxc.testandroid; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; public class QDPreferenceManager { private static SharedPreferences sPreferences; private static QDPreferenceManager sQDPreferenceManager = null; private static final String APP_VERSION_CODE = "app_version_code"; private static final String APP_SKIN_INDEX = "app_skin_index"; private QDPreferenceManager(Context context) { sPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); } public static final QDPreferenceManager getInstance(Context context) { if (sQDPreferenceManager == null) { sQDPreferenceManager = new QDPreferenceManager(context); } return sQDPreferenceManager; } public void setAppVersionCode(int code) { final SharedPreferences.Editor editor = sPreferences.edit(); editor.putInt(APP_VERSION_CODE, code); editor.apply(); } public void setSkinIndex(int index) { SharedPreferences.Editor editor = sPreferences.edit(); editor.putInt(APP_SKIN_INDEX, index); editor.apply(); } public int getSkinIndex() { return sPreferences.getInt(APP_SKIN_INDEX, QDSkinManager.SKIN_DEFAULT); } }
1.5 APP loads QDSkinManager and adapts to dark mode
This work only needs to be done once. It is recommended to customize the Application to realize this function.
src/main/java/com/qxc/testandroid/QDApplication.java:
package com.qxc.testandroid; import android.annotation.SuppressLint; import android.app.Application; import android.content.Context; import android.content.res.Configuration; import androidx.annotation.NonNull; public class QDApplication extends Application { @SuppressLint("StaticFieldLeak") private static Context context; public static Context getContext() { return context; } @Override public void onCreate() { super.onCreate(); context = getApplicationContext(); QDSkinManager.install(this); } @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); //Adapt to Dark Mode if ((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES) { QDSkinManager.changeSkin(QDSkinManager.SKIN_2); } else if (QDSkinManager.getCurrentSkin() == QDSkinManager.SKIN_2) { QDSkinManager.changeSkin(QDSkinManager.SKIN_DEFAULT); } } }
Don't forget the Android manifest Specify our custom Application class in XML:
<application android:name=".QDApplication" ......
1.6. Start writing Activity
The basic work is ready. Next, we implement the defined skin change effect. Modify the layout file of MainActivity and write our UI layout:
src/main/res/layout/activity\_main.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" android:layout_width="match_parent" android:layout_height="match_parent" app:qmui_skin_background="?attr/colorPrimary" tools:context=".MainActivity"> <RelativeLayout android:id="@+id/v1" android:layout_width="match_parent" android:layout_height="50dp" app:qmui_skin_background="?attr/colorBg2" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="16sp" android:text="Title Bar" app:qmui_skin_text_color="?attr/colorTextWhite"/> </RelativeLayout> <RelativeLayout android:id="@+id/v2" android:layout_width="match_parent" android:layout_height="200dp" android:layout_below="@id/v1" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" app:qmui_skin_background="?attr/colorBg1" /> <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton android:id="@+id/btn" android:layout_marginTop="10dp" android:layout_width="200dp" android:layout_height="50dp" android:layout_below="@id/v2" android:layout_centerHorizontal="true" android:gravity="center" app:qmui_radius="10dp" app:qmui_skin_background="?attr/colorBg3" app:qmui_skin_text_color="?attr/colorTextWhite" app:qmui_skin_border="?attr/colorBg2" android:text="change skin" /> </RelativeLayout>
Note: to realize skin change, we should use the skin change attribute provided by QMUI when setting the control color:
app:qmui_skin_xxx
The QMUI official website has provided the following skin changing attributes for our use, which can meet the general development needs, as shown in the figure below:
Next, let's write the Activity code. In the Activity, we need to register QMUISkinManager before the Activity can enjoy the skin change function (Note: in actual development, if all APP pages need to support skin change, we try to write the registration of QMUISkinManager in BaseActivity).
There are two schemes to realize registration:
Option 1:
We can inherit QMUIFragmentActivity or QMUIActivity from the Activity class, so QMUISkinManager is injected by default
Scheme 2 (in order to let you know how to register, we choose this scheme. Don't worry, it's actually very simple):
We implement the registration and deregistration of QMUISkinManager ourselves
package com.qxc.testandroid; import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import androidx.core.view.LayoutInflaterCompat; import com.qmuiteam.qmui.skin.QMUISkinLayoutInflaterFactory; import com.qmuiteam.qmui.skin.QMUISkinManager; public class MainActivity extends Activity { private QMUISkinManager skinManager; private Button btn; private int skinIndex; @Override protected void onCreate(Bundle savedInstanceState) { // Using QMUISkinLayoutInflaterFactory LayoutInflater layoutInflater = LayoutInflater.from(this); LayoutInflaterCompat.setFactory2(layoutInflater, new QMUISkinLayoutInflaterFactory(this, layoutInflater)); super.onCreate(savedInstanceState); // Inject QMUISkinManager skinManager = QMUISkinManager.defaultInstance(this); setContentView(R.layout.activity_main); initView(); initEvent(); } private void initView(){ btn = findViewById(R.id.btn); } private void initEvent(){ //Skin changing operation skinIndex = QDSkinManager.SKIN_DEFAULT; btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(skinIndex + 1 > 3){ skinIndex = 0; } skinIndex += 1; QDSkinManager.changeSkin(skinIndex); } }); } @Override protected void onPause() { super.onPause(); } @Override public void onStart() { super.onStart(); //Register QDSkinManager if(skinManager != null){ skinManager.register(this); } } @Override protected void onStop() { super.onStop(); //Unregister QDSkinManager if(skinManager != null){ skinManager.unRegister(this); } } @Override protected void onResume() { super.onResume(); } @Override protected void onDestroy() { super.onDestroy(); } }
At this point, the coding is over.
2. Knowledge expansion
API provided by QMUI skinning:
- QMUISkinManager: stores the skin color configuration and distributes the current skin color to the activities, fragments, Dialog and PopupWindow it manages. It passes through QMUISkinManager Of (name, context), which can be multi instance. Therefore, an App can perform different skin change management in different scenarios, such as skin change of reading product readers and differentiated management of uiMode switching of other business modules.
- QMUISkinValueBuilder: used to build the skin changing configuration (textColor, background, border, separator, etc.) of a View instance
- QMUISkinHelper: some auxiliary tools and methods. The most commonly used is QMUISkinHelper Setskinvalue (View, QMUISkinValueBuilder) applies the configuration of QMUISkinValueBuilder to a View instance. If you use the kotlin language, you can use View skin { ... } To configure the View instance.
- QMUISkinLayoutInflaterFactory: used to support xml skinning configuration item parsing.
- Iqmuiskinndispatchinterceptor: view can implement it to intercept the distribution of skin changes.
- IQMUISkinHandlerView: View can completely customize the processing of different skins by implementing it.
- IQMUISkinDefaultAttrProvider: View can implement it to provide the default skin changing configuration of View and provide skin changing support from the component level.
end of document
Your favorite collection is my greatest encouragement! Welcome to follow my brief book, share Android dry goods and exchange Android technology. If you have any opinions on the article or any technical problems, please leave a message in the comment area for discussion!
This article is transferred from https://juejin.cn/post/7038482977914880008 , in case of infringement, please contact to delete.