DarkMode’s popularity has skyrocketed recently with the introduction of deep support for DarkMode in ios13 and Android 10. DarkMode is supported by many apps. So what is DarkMode? On the first official document college IOS:developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/ college Android: https://developer….

My understanding is that Dark mode is definitely not just a color flip version, but a complete rewrite of the colors used in each interface, starting with dark and matching. This model allows the content we actually interact with and manipulate to be highlighted, giving users an immersive experience. In addition, it is also cool, saving eyes and power 🙂 since it is a complete set of design concepts, corresponding to the existing App design elements, it is a huge and complicated project from the development side. I’ve given some thought to this, and I’ve experimented with a few solutions, which I’ve turned into seven weapons for DarkMode. These weapons were not previously independent and can be used in conjunction with the existing ecosystem of the App. 7 Weapons use the setTheme method to reset the Activity’s theme. 
 defines two themes in which the properties of the control defined correspond to different color values. When writing the layout file, reference the defined properties.

<resources>
    <attr name="mainBackground" format="color|reference"></attr> 
</resources>
Copy the code

copy

<style name=" white theme "> <item name=" Android :textColor">@color/ white </item> // System control property <item Name ="mainBackground">@color/ black </item> // Custom attributes </style> <style name=" dark theme "> <item </item> <item name="mainBackground">@color/ white </item> </style>Copy the code

copy

” data-snippet-id=”ext.2a753c4bf27038b72afc29f534da9a31″ data-snippet-saved=”false” data-codota-status=”done”> The < RelativeLayout XMLNS: android = “schemas.android.com/apk/res/and…

copy

Another variation of this scenario is that instead of following the setTheme with an activity, a broadcast is sent. After receiving a broadcast, action view updates the UI.

Resources.Theme theme = getTheme();
theme.resolveAttribute(R.attr.mainBackground, background, true);
mLayout.setBackgroundResource(background.resourceId);
Copy the code

Duplicate UiMode Settings in Android Support Library to Support day/night mode switching; 
 using UiMode is also easy, we need to define colors.xml as day/night. After that, different colors.xml will be selected based on the schema. After the Activity calls set (), the capability to switch the day/night mode is implemented. 
 Below is values/colors.xml:

” data-snippet-id=”ext.2e3ea9df23c36fa9b3484d2e2ac898c9″ data-snippet-saved=”false” data-codota-status=”done”> <style name=”AppTheme” parent=”Theme.AppCompat.Light.DarkActionBar”> <item name=”colorPrimary”>@color/colorPrimary <item name=”colorPrimaryDark”>@color/colorPrimaryDark <item name=”colorAccent”>@color/colorAccent <item Name =” Android :textColor”>@color/textColor <item name=”mainBackground”>@color/backgroundColor Copy

Select a default Mode when creating the page and switch it when necessary.

GetDelegate (.) setLocalNightMode (AppCompatDelegate MODE_NIGHT_YES)” data-snippet-id=”ext.ac7b61b1439d3df32224ebe93bbeb49e” data-snippet-saved=”false” data-codota-status=”done”> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); GetDelegate ().setLocalnightmode (appcompatdelegate.mode_night_yes) copies the compatcompatdelegate (compatdelegate.mode_night_yes) formula for Android to switch between languages through code. It is a variant based on UiMode scheme. Since there is value-night, there can also be other value-xxx, but it is android native and can only recognize predefined suffixes. Here, we can borrow the locale type and add value-AA, value-AB and other resource folders, and each set corresponds to a mode. And then in your code, you switch this way

super.attachBaseContext(newContext);
Copy the code

} " data-snippet-id="ext.c583f5756066acf0db6bb642f2e3e685" data-snippet-saved="false" data-codota-status="done"> @Override protected void attachBaseContext(Context newBase) { Locale locale = new Locale("aa"); // aa: normal, ab: dark,.... final Resources res = newBase.getResources(); final Configuration config = res.getConfiguration(); config.setLocale(locale); // getLocale() should return a Locale final Context newContext = newBase.createConfigurationContext(config); College super. AttachBaseContext (newContext); } copy

The custom ThemeChangeListener interface is called back to handle the day/night mode switch through the resource ID mapping. The idea is to dynamically get the mapping of resource ids based on the set topic, and then use a callback interface to let the UI set the associated attribute values. Let's make a rule here: Resources in night mode should be named with suffix "_night". For example, the background color of daytime mode should be named color_background, then the background resource of night mode should be named color_background_night
 for example, We can provide a utility class, ThemeManager, that maintains a combination of resource ids and topics and ultimately returns a resource value.

/ / access to resources of specification textview. SetTextColor (getResources (). The getColor (ThemeManager. GetCurrentThemeRes (MainActivity. This, R.color.textColor)));" Data - the snippet - id = "ext b0c483c02df13dff90a92c382168e2f0" data - the snippet - saved = "false" data - codota - status = "done" > / / set the theme ThemeManager.setThemeMode(ThemeManager.getThemeMode() == ThemeManager.ThemeMode.DAY ? ThemeManager.ThemeMode.NIGHT : ThemeManager.ThemeMode.DAY); College / / access to resources of specification textview. SetTextColor (getResources (). The getColor (ThemeManager. GetCurrentThemeRes (MainActivity. This, R.color.textColor))); copy

As we introduce AppCompat-v7 and have AppCompatActivity available, We find that the TextView/Button components we render become AppCompatTextView and AppCompatButton respectively. Similarly, we can set up our own LayoutInflaterFactory using LayoutInflaterCompat. Then, following Google's lead, we turn native controls in XML into our own custom controls.

// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy // by using the parent's context if (inheritContext &amp; &amp; parent ! = null) { context = parent.getContext(); } if (readAndroidTheme || readAppTheme) { // We then apply the theme on the context, if specified context = themifyContext(context, attrs, readAndroidTheme, readAppTheme); } if (wrapContext) { context = TintContextWrapper.wrap(context); } View view = null; // We need to 'inject' our tint aware Views in place of the standard framework versions switch (name) { case &quot; TextView&quot; : //view = new AppCompatTextView(context, attrs); view = new SkinnableTextView(context, attrs); // view break; case &quot; ImageView&quot; : view = new AppCompatImageView(context, attrs); break; . } if (view == null &amp; &amp; originalContext ! = context) { // If the original context does not equal our themed context, then we need to manually // inflate it using the name so that android:theme takes effect. view = createViewFromTag(context, name, attrs); } if (view ! = null) { // If we have created a view, check it's android:onClick checkOnClickListener(view, attrs); } return view;Copy the code

} " data-snippet-id="ext.31d55951ca97c8a347768df7cc13bc38" data-snippet-saved="false" data-codota-status="done"> public final View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) { final Context originalContext = context; // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy // by using the parent's context if (inheritContext && parent ! = null) { context = parent.getContext(); } if (readAndroidTheme || readAppTheme) { // We then apply the theme on the context, if specified context = themifyContext(context, attrs, readAndroidTheme, readAppTheme); } if (wrapContext) { context = TintContextWrapper.wrap(context); } View view = null; // We need to 'inject' our tint aware Views in place of the standard framework versions switch (name) { case "TextView": //view = new AppCompatTextView(context, attrs); view = new SkinnableTextView(context, attrs); // view break; case "ImageView": view = new AppCompatImageView(context, attrs); break; . } if (view == null && originalContext ! = context) { // If the original context does not equal our themed context, then we need to manually // inflate it using the name so that android:theme takes effect. view = createViewFromTag(context, name, attrs); } if (view ! = null) { // If we have created a view, check it's android:onClick checkOnClickListener(view, attrs); } return view; }

The setDayNightMode function is called every time Skinnable is needed, which notifies all objects in the View hierarchy that implement the Skinnable interface. Call their applyDayNight method to switch their styles. When the View is generated, we record some resource ids referenced by it. After the UiMode is switched, the real resources obtained by the same resource ID have different characteristics to complete the night mode switching scheme. Css-like view control scheme The server delivers a CSS-like style sheet. Key is defined by the client, corresponds to a property of a view of a type on the client, and value is a style value. At render time, the client reads the style values according to predefined rules. The key to this approach is whether the read-set process is centralized or whether each view needs to be controlled by itself. It depends on the level of protocol design, and it also determines the cost of development. We will introduce this in detail in the later part of Youku practice. The server delivers the desired mode by setting different parameters through the switch. The component developer treats this mode as a normal parameter and renders different parameters. This puts all the work into component development. The design is the simplest.

At the end, there are a number of schemes to implement mode switching, which are not superior or inferior, depending on business requirements and technology ecology. For example, there are pain points that need to make concrete Activtiy in the scheme of switching theme or UIMode, and the way resource IDS are mapped needs to modify the usual way of obtaining resources, and imitate CSS Settings as custom views of code or ways of implementing specific interfaces. Although there are great flexibility and generality, Each component needs to be modified, which is terrible for the workload of super App.

Specifically, DarkMode is an adaptation of a visual mode defined by the system. The system does some preliminary work for it, which makes the DesignToken solution simple and efficient. At Youku, combining the DesignToken system with the existing skin change solution, you can cover most scenarios, and only need to check some details. In addition, DarkMode switching is a system behavior in the latest system version, and should be actively triggered by the user in the lower system version, so there seems to be no need for server control.