轻松实现夜间模式


今天带来的是一个夜间模式的实现,这种夜间模式呢,不会重启我们的Activity的生命周期,对于初学者也是非常的好用,简单的介绍他呢就是根据设置的主题去动态地获取资源 id 的映射,然后使用回调接口的方式让 UI 去设置相关的属性值。我们在这里先规定一下:夜间模式的资源在命名上都要加上后缀 “_night” ,比如日间模式的背景色命名为 color_background ,那么相对应的夜间模式的背景资源就要命名为 color_background_night 。


好了,下面就是我们的 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>

可以看到每一项 color 都会有对应的 “_night” 与之匹配。

然后我们就用到了 ThemeManager 来实现夜间模式的ID映射逻辑


public class ThemeManager {
 
 // 默认是日间模式
 private static ThemeMode mThemeMode = ThemeMode.DAY;
 // 主题模式监听器
 private static List<OnThemeChangeListener> mThemeChangeListenerList = new LinkedList<>();
 // 夜间资源的缓存,key : 资源类型, 值<key:资源名称, value:int值>
 private static HashMap<String, HashMap<String, Integer>> sCachedNightResrouces = new HashMap<>();
 // 夜间模式资源的后缀,比如日件模式资源名为:R.color.activity_bg, 那么夜间模式就为 :R.color.activity_bg_night
 private static final String RESOURCE_SUFFIX = "_night";
 
 /**
  * 主题模式,分为日间模式和夜间模式
  */
 public enum ThemeMode {
  DAY, NIGHT
 }
 
 /**
  * 设置主题模式
  *
  * @param themeMode
  */
 public static void setThemeMode(ThemeMode themeMode) {
  if (mThemeMode != themeMode) {
   mThemeMode = themeMode;
   if (mThemeChangeListenerList.size() > 0) {
    for (OnThemeChangeListener listener : mThemeChangeListenerList) {
     listener.onThemeChanged();
    }
   }
  }
 }
 
 /**
  * 根据传入的日间模式的resId得到相应主题的resId,注意:必须是日间模式的resId
  *
  * @param dayResId 日间模式的resId
  * @return 相应主题的resId,若为日间模式,则得到dayResId;反之夜间模式得到nightResId
  */
 public static int getCurrentThemeRes(Context context, int dayResId) {
  if (getThemeMode() == ThemeMode.DAY) {
   return dayResId;
  }
  // 资源名
  String entryName = context.getResources().getResourceEntryName(dayResId);
  // 资源类型
  String typeName = context.getResources().getResourceTypeName(dayResId);
  HashMap<String, Integer> cachedRes = sCachedNightResrouces.get(typeName);
  // 先从缓存中去取,如果有直接返回该id
  if (cachedRes == null) {
   cachedRes = new HashMap<>();
  }
  Integer resId = cachedRes.get(entryName + RESOURCE_SUFFIX);
  if (resId != null && resId != 0) {
   return resId;
  } else {
   //如果缓存中没有再根据资源id去动态获取
   try {
    // 通过资源名,资源类型,包名得到资源int值
    int nightResId = context.getResources().getIdentifier(entryName + RESOURCE_SUFFIX, typeName, context.getPackageName());
    // 放入缓存中
    cachedRes.put(entryName + RESOURCE_SUFFIX, nightResId);
    sCachedNightResrouces.put(typeName, cachedRes);
    return nightResId;
   } catch (Resources.NotFoundException e) {
    e.printStackTrace();
   }
  }
  return 0;
 }
 
 /**
  * 注册ThemeChangeListener
  *
  * @param listener
  */
 public static void registerThemeChangeListener(OnThemeChangeListener listener) {
  if (!mThemeChangeListenerList.contains(listener)) {
   mThemeChangeListenerList.add(listener);
  }
 }
 
 /**
  * 反注册ThemeChangeListener
  *
  * @param listener
  */
 public static void unregisterThemeChangeListener(OnThemeChangeListener listener) {
  if (mThemeChangeListenerList.contains(listener)) {
   mThemeChangeListenerList.remove(listener);
  }
 }
 
 /**
  * 得到主题模式
  *
  * @return
  */
 public static ThemeMode getThemeMode() {
  return mThemeMode;
 }
 
 /**
  * 主题模式切换监听器
  */
 public interface OnThemeChangeListener {
  /**
   * 主题切换时回调
   */
  void onThemeChanged();
 }
}


上面 ThemeManager 的代码基本上都有注释,想要看懂并不困难。其中最核心的就是 getCurrentThemeRes 方法了。在这里解释一下 getCurrentThemeRes 的逻辑。参数中的 dayResId 是日间模式的资源id,如果当前主题是日间模式的话,就直接返回 dayResId 。反之当前主题为夜间模式的话,先根据 dayResId 得到资源名称和资源类型。比如现在有一个资源为 R.color.colorPrimary ,那么资源名称就是 colorPrimary ,资源类型就是 color 。然后根据资源类型和资源名称去获取缓存。如果没有缓存,那么就要动态获取资源了。这里使用方法的是

context.getResources().getIdentifier(String name, String defType, String defPackage)

name 参数就是资源名称,不过要注意的是这里的资源名称还要加上后缀 “_night” ,也就是上面在 colors.xml 中定义的名称;
defType 参数就是资源的类型了。比如 color,drawable等;

defPackage 就是资源文件的包名,也就是当前 APP 的包名。

有了上面的这个方法,就可以通过 R.color.colorPrimary 资源找到对应的 R.color.colorPrimary_night 资源了。最后还要把找到的夜间模式资源加入到缓存中。这样的话以后就直接去缓存中读取,而不用再次去动态查找资源 id 了。

ThemeManager 中剩下的代码应该都是比较简单的,相信大家都可以看得懂了。

现在我们来看看 MainActivity 的代码:

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)));
  // 设置标题栏颜色
  if(supportActionBar != null){
   supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.colorPrimary))));
  }
  // 设置状态栏颜色
  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);
 }
 
}

在 MainActivity 中实现了 OnThemeChangeListener 接口,这样就可以在主题改变的时候执行回调方法。然后在 initTheme() 中去重新设置 UI 的相关颜色属性值。还有别忘了要在 onDestroy() 中移除 ThemeChangeListener 。


上一张效果图:


好了,今天分享到这里,想要查看更多消息呢,请关注以下地址

http://www.jb51.net/article/93852.htm




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值