Realize night mode, no need to recreate, no flash screen

Keywords: xml encoding Java

Through resource id mapping, callback to customize ThemeChangeListener interface to handle day / night mode switching.


Under values, in 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>  
Need a theme management class ThemeManager

public class ThemeManager {  
  
 // Default is day mode  
 private static ThemeMode mThemeMode = ThemeMode.DAY;  
 // Theme mode listener  
 private static List<OnThemeChangeListener> mThemeChangeListenerList = new LinkedList<>();  
 // Nighttime resource cache, key: resource type, value < key: resource name, value:int value >  
 private static HashMap<String, HashMap<String, Integer>> sCachedNightResrouces = new HashMap<>();  
 // Suffix of nighttime mode resource. For example, if the name of the Japanese mode resource is r.color.activity, then the nighttime mode is r.color.activity  
 private static final String RESOURCE_SUFFIX = "_night";  
  
 /**  
  * Theme mode, divided into day mode and night mode  
  */  
 public enum ThemeMode {  
  DAY, NIGHT  
 }  
  
 /**  
  * Set theme mode  
  *  
  * @param themeMode  
  */  
 public static void setThemeMode(ThemeMode themeMode) {  
  if (mThemeMode != themeMode) {  
   mThemeMode = themeMode;  
   if (mThemeChangeListenerList.size() > 0) {  
    for (OnThemeChangeListener listener : mThemeChangeListenerList) {  
     listener.onThemeChanged();  
    }  
   }  
  }  
 }  
  
 /**  
  * According to the resId of the incoming daytime mode, get the resId of the corresponding topic. Note: it must be the resId of the daytime mode  
  *  
  * @param dayResId resId of day mode  
  * @return The resId of the corresponding topic. If it is in daytime mode, it will get dayResId; otherwise, nightResId will get nightResId  
  */  
 public static int getCurrentThemeRes(Context 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);  
  // Fetch from the cache first, and return the id directly if any  
  if (cachedRes == null) {  
   cachedRes = new HashMap<>();  
  }  
  Integer resId = cachedRes.get(entryName + RESOURCE_SUFFIX);  
  if (resId != null && resId != 0) {  
   return resId;  
  } else {  
   //If there is no dynamic acquisition based on resource id in the cache  
   try {  
    // Get resource int value through resource name, resource type and package name  
    int nightResId = context.getResources().getIdentifier(entryName + RESOURCE_SUFFIX, typeName, context.getPackageName());  
    // Put in cache  
    cachedRes.put(entryName + RESOURCE_SUFFIX, nightResId);  
    sCachedNightResrouces.put(typeName, cachedRes);  
    return nightResId;  
   } catch (Resources.NotFoundException e) {  
    e.printStackTrace();  
   }  
  }  
  return 0;  
 }  
  
 /**  
  * Register ThemeChangeListener  
  *  
  * @param listener  
  */  
 public static void registerThemeChangeListener(OnThemeChangeListener listener) {  
  if (!mThemeChangeListenerList.contains(listener)) {  
   mThemeChangeListenerList.add(listener);  
  }  
 }  
  
 /**  
  * Unregister ThemeChangeListener  
  *  
  * @param listener  
  */  
 public static void unregisterThemeChangeListener(OnThemeChangeListener listener) {  
  if (mThemeChangeListenerList.contains(listener)) {  
   mThemeChangeListenerList.remove(listener);  
  }  
 }  
  
 /**  
  * Get theme pattern  
  *  
  * @return  
  */  
 public static ThemeMode getThemeMode() {  
  return mThemeMode;  
 }  
  
 /**  
  * Topic mode switching listener  
  */  
 public interface OnThemeChangeListener {  
  /**  
   * Callback on topic switching  
   */  
  void onThemeChanged();  
 }  
} 
In activity main.xml, you only need to write a button, and then give the root layout an ID (because you need to change the background color of the root layout)

Find the id of the button and the id of the root layout in MainActivity.java

The OnThemeChangeListener interface is implemented in MainActivity, so that the callback method can be executed when the theme changes. Then reset it in initTheme() The value of the relevant color property of the UI. And don't forget to remove it in onDestroy() ThemeChangeListener .

    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.setThemeMode(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)));  
      // Set title block color  
      if(supportActionBar != null){  
       supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.colorPrimary))));  
      }  
      // Set 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);  
     }  
      
    }  






Posted by MyWebAlias on Thu, 30 Apr 2020 13:29:09 -0700