Android realizes a deep understanding of day and night models _android

Source: Internet
Author: User
Tags current time getcolor

In this article, there are three ways to implement day/night mode switching, three scenarios may lead to an excessive length of the article, please read patiently.

1, setTheme the use of the method to Activity reset the theme;

2, Android Support Library in the setting UiMode to support the daytime/night mode switch;

3, through the resource ID mapping, callback custom ThemeChangeListener interface to handle daytime/night mode switching.

First, using Settheme method

Let's take a look at how we setTheme can implement a daytime/night mode switching scheme. The idea of this scheme is simple: when the user chooses the night mode, the activity is set to the theme of the night mode, and then let Activity recreate() the calling method recreate it again.

Then do it. Define two sets of colors in the Colors.xml, representing the theme colors for the day and night, respectively:

<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
 <color name= "Colorprimary" > #3F51B5 </color>
 <color name= "Colorprimarydark" > #303F9F </color>
 <color name= "Coloraccent" > #FF4081 </color>

 <color name= "nightcolorprimary" > #3b3b3b </color>
 <color name= " Nightcolorprimarydark "> #383838 </color>
 <color name=" nightcoloraccent "> #a72b55 </color>
</resources>

After that, two sets of topics are defined in Styles.xml, namely, Day and night themes:

<resources> <!--Base application theme. --> <style name= "Apptheme" parent= "Theme.AppCompat.Light.DarkActionBar" > <!--Customize your here. --> <item name= "colorprimary" > @color/colorprimary</item> <item name= "Colorprimarydark" > @color /colorprimarydark</item> <item name= "coloraccent" > @color/coloraccent</item> <item " Android:textcolor "> @android:color/black</item> <item name=" Mainbackground "> @android: Color/white </item> </style> <style name= "Nightapptheme" parent= "Theme.AppCompat.Light.DarkActionBar" > <!- -Customize your theme here. --> <item name= "colorprimary" > @color/nightcolorprimary</item> <item name= "ColorPrimaryDark" >@ color/nightcolorprimarydark</item> <item name= "coloraccent" > @color/nightcoloraccent</item> < Item Name= "Android:textcolor" > @android:color/white</item> <item name= "Mainbackground" > @color/nightcolorprimarydark</item> </style> </resources> 

The attributes in the topic mainBackground are our custom attributes that represent the background color:

<?xml version= "1.0" encoding= "Utf-8"?>
<resources> <attr name= "Mainbackground" format= "
 Color|reference "></attr>
</resources>

The next step is to look at the layout activity_main.xml:

<?xml version= "1.0" encoding= "Utf-8"?> <relativelayout xmlns:android= "http://schemas.android.com/apk/res/" Android "xmlns:tools=" Http://schemas.android.com/tools "android:layout_width=" Match_parent "android:layout_height = "Match_parent" android:background= "Attr/mainbackground" android:paddingbottom= "@dimen/activity_vertical_margin "Android:paddingleft=" @dimen/activity_horizontal_margin "android:paddingright=" @dimen/activity_horizontal_ Margin "android:paddingtop=" @dimen/activity_vertical_margin "tools:context=" com.yuqirong.themedemo.MainActivity " > <button android:id= "@+id/btn_theme" android:layout_width= match_parent "android:layout_height=" Wrap_conten T "android:text=" Switch day/night mode "/> <textview android:id=" @+id/tv "android:layout_below=" @id/btn_theme "Android: Layout_width= "Match_parent" android:layout_height= "wrap_content" android:gravity= "Center_horizontal" Android:text = "Through the Settheme () method"/> </RelativeLayout>

In the <RelativeLayout> android:background properties, we use "? Attr/mainbackground" to represent RelativeLayout the background color to refer to the value of the previously defined attribute in the topic mainBackground . This enables the day/night mode switch to change color.

The last MainActivity code is:

public class Mainactivity extends Appcompatactivity {//default is daytime mode private int theme = R.style.apptheme;
 @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
   Determine if there is a topic store if (savedinstancestate!= null) {theme = Savedinstancestate.getint ("theme");
  Settheme (theme);

  } setcontentview (R.layout.activity_main);
  Button Btn_theme = (button) Findviewbyid (R.id.btn_theme);  Btn_theme.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) {theme = (theme = = R.style.apptheme)?
    R.style.nightapptheme:r.style.apptheme;
   MainActivity.this.recreate ();
 }
  });
  } @Override protected void Onsaveinstancestate (Bundle outstate) {super.onsaveinstancestate (outstate);
 Outstate.putint ("theme", theme); } @Override protected void Onrestoreinstancestate (Bundle savedinstancestate) {super.onrestoreinstancestate (savedinst
  Ancestate);
 Theme = Savedinstancestate.getint ("theme"); }
}

MainActivity There are a few points to note in the:

1. The recreate() lifecycle of the activity after invoking the method is invoked onSaveInstanceState(Bundle outState) to back up the relevant data, which is then invoked onRestoreInstanceState(Bundle savedInstanceState) to restore the relevant data, so we keep theme the values in so that the activity is recreated and then used.

2, we restore the value in the method, the onCreate(Bundle savedInstanceState) theme setTheme() method must be setContentView() called before the method, otherwise the words will not see the effect.

3, the recreate() method is added in API 11, so the use of the Android 2.X will throw an exception.

After pasting the above code, let's take a look at the results of the implementation of the scheme:

Ii. using the Uimode method in the Android Support Library

The method of using Uimode is also very simple, we need to define colors.xml as day/night two kinds. After that, different colors.xml will be chosen according to different patterns. recreate() after the activity call, the function of switching the day/night mode is realized.

Say so much, directly on the code. Here is Values/colors.xml:

<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
 <color name= "Colorprimary" > #3F51B5 </color>
 <color name= "Colorprimarydark" > #303F9F </color>
 <color name= "Coloraccent" > #FF4081 </color>
 <color name= "TextColor" > #FF000000 </color>
 <color name= " BackgroundColor "> #FFFFFF </color>
</resources>

In addition to Values/colors.xml, we also create a values-night/colors.xml file to set the color of the night mode, where the name of <color> must be and values/colors.xml The corresponding in:

<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
 <color name= "Colorprimary" > #3b3b3b </color>
 <color name= "Colorprimarydark" > #383838 </color>
 <color name= "Coloraccent" > #a72b55 </color>
 <color name= "TextColor" > #FFFFFF </color>
 <color name= " BackgroundColor "> #3b3b3b </color>
</resources>

In Styles.xml, refer to the color we defined in Colors.xml:

<resources>

 <!--Base application theme.-->
 <style name= "Apptheme" parent= " Theme.AppCompat.Light.DarkActionBar ">
  <!--Customize your Theme here--> <item name=
  " Colorprimary "> @color/colorprimary</item>
  <item name=" Colorprimarydark "> @color colorprimarydark</item>
  <item name= "coloraccent" > @color/coloraccent</item>
  <item Name= "Android:textcolor" > @color/textcolor</item>
  <item name= "Mainbackground" > @color backgroundcolor</item>
 </style>

</resources>

The content of the Activity_main.xml layout is similar to the above setTheme() method, it is not posted here. After that it becomes very easy to choose a default Mode in MyApplication:

public class MyApplication extends application {

 @Override public
 void OnCreate () {
  super.oncreate ();
  The default setting is daytime mode
  Appcompatdelegate.setdefaultnightmode (
    appcompatdelegate.mode_night_no);
 }

Note that there are four types of Mode here to choose from:

1, Mode_night_no: Use bright colors (light) theme, do not use the night mode;

2, Mode_night_yes: Use Dark Color (dark) theme, the use of night mode;

3, Mode_night_auto: Automatically switch light (light)/Dark Color (dark) theme According to the current time;

4, Mode_night_follow_system (default option): Set to follow the system, usually mode_night_no

When the user clicks on the button to switch to the day/night, set the corresponding Mode again:

public class Mainactivity extends Appcompatactivity {

 @Override
 protected void OnCreate (Bundle Savedinstancestate) {
  super.oncreate (savedinstancestate);
  Setcontentview (r.layout.activity_main);

  Button Btn_theme = (button) Findviewbyid (r.id.btn_theme);
  Btn_theme.setonclicklistener (New View.onclicklistener () {
   @Override public
   void OnClick (View v) {
    int Currentnightmode = Getresources (). GetConfiguration (). Uimode & Configuration.ui_mode_night_mask;
    Getdelegate (). Setlocalnightmode (Currentnightmode = = Configuration.ui_mode_night_no
      ? AppCompatDelegate.MODE_NIGHT_YES:AppCompatDelegate.MODE_NIGHT_NO);
    It is also necessary to invoke the recreate method to take effect
    recreate ();
   }}
  );

Let's take UiMode a look at the results of the implementation of the scheme:

In the first two ways, the configuration is relatively simple, the final implementation of the results are basically the same. But the downside is that you need to call it recreate() into effect. and to recreate the activity must involve some state preservation. This adds some difficulty. So let's take a look at the third approach.

Callback interface through Resource ID mapping

The third approach is to dynamically get the mapping of resource IDs based on the theme set, and then use the callback interface to let the UI set the associated property values. We are here to specify: The night pattern of resources in the name of the suffix "_night", such as day mode background color named Color_background, then the corresponding night-mode background resources will be named Color_background_night. OK, here's what we need to use for our Demo: Colors.xml

<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
 
 <color name= "Colorprimary" > #3F51B5 </color>
 <color name= "Colorprimary_night" > #3b3b3b </color>
 <color name= " Colorprimarydark "> #303F9F </color>
 <color name=" Colorprimarydark_night "> #383838 </color>
 <color name= "coloraccent" > #FF4081 </color>
 <color name= "Coloraccent_night" > #a72b55 < /color>
 <color name= "TextColor" > #FF000000 </color>
 <color name= "Textcolor_night" ># ffffff</color>
 <color name= "BackgroundColor" > #FFFFFF </color>
 <color name= " Backgroundcolor_night "> #3b3b3b </color>
 
</resources>

You can see that each color will have a corresponding "_night" match.

See here, certainly someone will ask, why should set corresponding "_night"? What is the way to set the day/night mode? The following is thememanager to answer for you:

public class Thememanager {//default is daytime mode private static thememode Mthememode = Thememode.day;
 Theme Mode listener private static list<onthemechangelistener> mthemechangelistenerlist = new linkedlist<> (); Cache of nightly Resources, key: Resource type, value <key: Resource Name, Value:int value > private static hashmap<string, hashmap<string, Integer>&gt ;
 Scachednightresrouces = new hashmap<> (); The suffix for night mode resources, such as the Japanese-style resource name: R.COLOR.ACTIVITY_BG, then the night mode is: R.color.activity_bg_night private static final String Resource_

 SUFFIX = "_night"; /** * Theme mode, divided into daytime mode and night Mode/public enum Thememode {day, NIGHT}/** * Set theme mode * * @param thememode/Pub
   Lic static void Setthememode (Thememode thememode) {if (Mthememode!= thememode) {mthememode = Thememode; if (mthemechangelistenerlist.size () > 0) {for (Onthemechangelistener listener:mthemechangelistenerlist) {L
    Istener.onthemechanged (); /** * According to the resid of the incoming daytime mode to get the resid of the corresponding topic, note: Must be a daytime mode resid * * @param dayREsid Day Mode Resid * @return The corresponding theme of the Resid, if the daytime mode, then get Dayresid; instead the night mode gets NIGHTRESID/public static int Getcurrentthemeres (Co
  ntext context, int dayresid) {if (getthememode () = = Thememode.day) {return dayresid;
  }//Resource name String EntryName = Context.getresources (). Getresourceentryname (DAYRESID);
  Resource type String typeName = Context.getresources (). Getresourcetypename (DAYRESID);
  hashmap<string, integer> cachedres = Scachednightresrouces.get (typeName);
  First from the cache, if there is a direct return of the ID if (cachedres = = null) {cachedres = new hashmap<> ();
  The Integer resid = cachedres.get (EntryName + resource_suffix);
  if (resid!= null && resid!= 0) {return resid; else {//if the cache does not dynamically acquire try {///through resource ID, resource type, package name to get resource int value int nightresid = Context.getresources (). getide
    Ntifier (EntryName + resource_suffix, TypeName, Context.getpackagename ());
    Put into the cache Cachedres.put (EntryName + resource_suffix, nightresid); Scachednightresrouces.put (TypeName, CACHedres);
   return nightresid;
   catch (Resources.notfoundexception e) {e.printstacktrace ();
 } return 0; /** * Registration Themechangelistener * * @param listener/public static void Registerthemechangelistener (Onthemechan
  Gelistener listener) {if (!mthemechangelistenerlist.contains (listener)) {Mthemechangelistenerlist.add (listener); }/** * Anti-registration Themechangelistener * * @param listener/public static void Unregisterthemechangelistener (Onth Emechangelistener listener) {if (Mthemechangelistenerlist.contains (listener)) {Mthemechangelistenerlist.remove (LIS
  Tener);
 }/** * Get theme Mode * * @return/public static Thememode Getthememode () {return mthememode;
 }/** * Theme mode switch listener/public interface Onthemechangelistener {/** * A callback for the theme switch/void onthemechanged (); }
}

The above Thememanager code basically has the annotation, wants to understand does not have the difficulty. The core of which is the getCurrentThemeRes method. Explain getCurrentThemeRes the logic here. The DAYRESID in the parameter is the resource ID of the daytime mode, and if the current theme is daytime mode, it is returned directly to Dayresid. If the current theme is night mode, the resource name and resource type are obtained first according to Dayresid. For example, now there is a resource for R.color.colorPrimary , then the resource name is Colorprimary, the resource type is color. The cache is then obtained based on the resource type and resource name. If there is no caching, then the resource will be dynamically acquired. The method used here is

Context.getresources (). Getidentifier (string name, String Deftype, String defpackage)

The name parameter is the resource name, but note that the resource name is also appended with the suffix "_night", which is the name defined above in Colors.xml;
The Deftype parameter is the type of resource. such as color,drawable;

Defpackage is the package name of the resource file, which is the package name of the current APP.

With the above method, you can R.color.colorPrimary find the corresponding resources through the resources R.color.colorPrimary_night . Finally, the night-mode resources found are added to the cache. That way, you can read the cache directly, without having to dynamically look up the resource ID again.

Thememanager the rest of the code should be relatively simple, I believe that we can read.

Now let's look at the Mainactivity code:

public class Mainactivity extends Appcompatactivity implements Thememanager.onthemechangelistener {private TextView TV
 ;
 Private Button Btn_theme;
 Private Relativelayout relativelayout;

 Private Actionbar Supportactionbar;
  @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
  Setcontentview (R.layout.activity_main);
  Thememanager.registerthemechangelistener (this);
  Supportactionbar = Getsupportactionbar ();
  Btn_theme = (Button) Findviewbyid (r.id.btn_theme);
  Relativelayout = (relativelayout) Findviewbyid (r.id.relativelayout);
  TV = (TextView) Findviewbyid (r.id.tv); Btn_theme.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) {THEMEMANAGER.S Etthememode (thememanager.getthememode () = = ThemeManager.ThemeMode.DAY?
   ThemeManager.ThemeMode.NIGHT:ThemeManager.ThemeMode.DAY);
 }
  }); public void Inittheme () {Tv.settextcolor getresources (). GetColor (Thememanager.getcurreNtthemeres (Mainactivity.this, R.color.textcolor)); Btn_theme.settextcolor (Getresources (). GetColor (Thememanager.getcurrentthemeres (Mainactivity.this,
  R.color.textcolor))); Relativelayout.setbackgroundcolor (Getresources (). GetColor (Thememanager.getcurrentthemeres (MainActivity.this,
  R.color.backgroundcolor))); Sets the title bar color if (supportactionbar!= null) {supportactionbar.setbackgrounddrawable (new colordrawable getresources (). Get
  Color (Thememanager.getcurrentthemeres (Mainactivity.this, r.color.colorprimary))); //Set the status bar color if (Build.VERSION.SDK_INT >= build.version_codes).
   lollipop) {window window = GetWindow (); Window.setstatusbarcolor (Getresources (). GetColor (Thememanager.getcurrentthemeres (MainActivity.this,
  r.color.colorprimary)));
 @Override public void onthemechanged () {inittheme ();
  } @Override protected void OnDestroy () {Super.ondestroy ();
 Thememanager.unregisterthemechangelistener (this); }

}

The interface is implemented in mainactivity so that the OnThemeChangeListener callback method can be executed when the theme changes. Then, in the initTheme() , reset the UI's associated color property values. And don't forget to remove the onDestroy() themechangelistener in.

Finally, let's take a look at the effect of the third method:

Some people may say that the effect of the first two methods is not different ah, but look carefully will find that the previous two methods in the switching mode of the moment there will be a temporary black screen phenomenon exists, and the third method does not. This is because both of the first two methods are invoked recreate() . The third method does not require the activity to be recreated, and the callback method is used to implement it.

Three ways to compare

Here, according to the routine should be summed up the time. Then, according to the above three methods to a simple comparison:

setTheme Methods: You can configure more than a set of themes, more easy to start. In addition to the day/night mode, there are other colorful themes. But needs to call recreate() , the switch instantaneous will have the phenomenon which the black screen flashes;

UiMode Method: The advantage is that the Android Support Library has been supported by a simple specification. But also need recreate() to call, there is a phenomenon of black screen flashes;

Dynamic Fetch resource ID, Callback interface: This method is more complex to use than the first two methods, and each UI-related property value needs to be set in the callback method. But there is no need recreate() to call, there is no black screen flashing phenomenon.

Summarize

That's all that's in this article, and hopefully it will help Android developers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.