靠谱 的软件外包伙伴

您的位置:首页 > 新闻动态 > 知乎和简书软件APP开发的夜间模式实现套路

知乎和简书软件APP开发的夜间模式实现套路

2016-08-30 17:12:34

前言

好了,回归正题,说回夜间模式。在网上看到很多童鞋都说用什么什么框架来实现这个功能,然后仔细去看一下各个推荐的框架,发现其实都是动态换肤的,动态换肤可比夜间模式要复杂多了,未免大材小用了。说实话,我一直没用什么好思路,虽然网上有童鞋提供了一种思路是通过 setTheme 然后再 recreate Activity 的方式,但是这样带来的问题是非常多的,看起来就相当不科学(为什么不科学,后文会说)。于是,直接想到了去逆向分析那些夜间模式做得好的应用的源代码,学习他们的实现套路。所以,本文的实现思路来自于编写这些应用的夜间模式功能的童鞋,先在这里向他们表示感谢。我的手机里面使用高频的应用不少,其中简书和知乎是属于夜间模式做得相当 nice 的。先给两个效果图大家对比感受下

简书 知乎

如果大家仔细观察,肯定会发现,知乎的切换效果更漂亮些,因为它有一个渐变的效果。那么它们的夜间模式到底是如何实现的呢?别急接着往下看,你也可以。

实现套路

这里先展示一下我的实现效果吧

简书实现效果 知乎实现效果

此处分为两个部分,一部分是 xml 文件中要干的活,一部分是 Java 代码要实现的活,先说 xml 吧。

XML 配置

首先,先写一套UI界面出来,上方左边是两个 TextView,右边是两个 CheckBox,下方是一个 RecyclerView ,实现很简单,这里我不贴代码了。

接着,在 styles 文件中添加两个 Theme,一个是日间主题,一个是夜间主题。它们的属性都是一样的,唯一区别在于颜色效果不同。

<!--白天主题-->
<style name="DayTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <item name="colorPrimary">@color/colorPrimary</item>
  <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  <item name="colorAccent">@color/colorAccent</item>
  <item name="clockBackground">@android:color/white</item>
  <item name="clockTextColor">@android:color/black</item>
</style>

<!--夜间主题-->
<style name="NightTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <item name="colorPrimary">@color/color3F3F3F</item>
  <item name="colorPrimaryDark">@color/color3A3A3A</item>
  <item name="colorAccent">@color/color868686</item>
  <item name="clockBackground">@color/color3F3F3F</item>
  <item name="clockTextColor">@color/color8A9599</item>  
</style>

需要注意的是,上面的 clockTextColor 和 clockBackground 是我自定义的 color 类型属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="clockBackground" format="color" />
    <attr name="clockTextColor" format="color" />
</resources>

然后再到所有需要实现夜间模式功能的 xml 布局文件中,加入类似下面设置,比如我在 RecyclerView 的 Item 布局文件中做了如下设置

稍稍解释下其作用,如 TextView 里的 android:textColor=”?attr/clockTextColor” 是让其字体颜色跟随所设置的 Theme。到这里,xml 需要做的配置全部完成,接下来是 Java 代码实现了。

Java 代码实现

大家可以先看下面的实现代码,看不懂的童鞋可以边结合我代码下方实现思路解说。

package com.clock.study.activity;
import ...

/**
 * 夜间模式实现方案
 *
 * @author Clock
 * @since 2016-08-11
 */
public class DayNightActivity
  extends AppCompatActivity
  implements CompoundButton.OnCheckedChangeListener {
  
  private final static String TAG = DayNightActivity.class.getSimpleName();
    
  /**用于将主题设置保存到SharePreferences的工具类**/
  private DayNightHelper mDayNightHelper;
  private RecyclerView mRecyclerView;
  private LinearLayout mHeaderLayout;
  private List<RelativeLayout> mLayoutList;
  private List<TextView> mTextViewList;
  private List<CheckBox> mCheckBoxList;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    initData();
    initTheme();
    setContentView(R.layout.activity_day_night);
    initView();
  }
  
  private void initView() {
    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
    mRecyclerView.setLayoutManager(layoutManager);
    mRecyclerView.setAdapter(new SimpleAuthorAdapter());

    mHeaderLayout = (LinearLayout) findViewById(R.id.header_layout);

    mLayoutList = new ArrayList<>();
    mLayoutList.add((RelativeLayout) findViewById(R.id.jianshu_layout));
    mLayoutList.add((RelativeLayout) findViewById(R.id.zhihu_layout));

    mTextViewList = new ArrayList<>();
    mTextViewList.add((TextView) findViewById(R.id.tv_jianshu));
    mTextViewList.add((TextView) findViewById(R.id.tv_zhihu));

    mCheckBoxList = new ArrayList<>();
    CheckBox ckbJianshu = (CheckBox) findViewById(R.id.ckb_jianshu);
    ckbJianshu.setOnCheckedChangeListener(this);
    mCheckBoxList.add(ckbJianshu);
    CheckBox ckbZhihu = (CheckBox) findViewById(R.id.ckb_zhihu);
    ckbZhihu.setOnCheckedChangeListener(this);
    mCheckBoxList.add(ckbZhihu);
  }
  
  @Override
  public void onCheckedChanged(
    CompoundButton buttonView,
    boolean isChecked) {
  
    int viewId = buttonView.getId();
    if (viewId == R.id.ckb_jianshu) {
      changeThemeByJianShu();
    } else if (viewId == R.id.ckb_zhihu) {
      changeThemeByZhiHu();
    }
  }
  
  private void initData() {
    mDayNightHelper = new DayNightHelper(this);
  }
  
  private void initTheme() {
    if (mDayNightHelper.isDay()) {
      setTheme(R.style.DayTheme);
    } else {
      setTheme(R.style.NightTheme);
    }
  }
  
  /**
   * 切换主题设置
   */
  private void toggleThemeSetting() {
    if (mDayNightHelper.isDay()) {
      mDayNightHelper.setMode(DayNight.NIGHT);
      setTheme(R.style.NightTheme);
    } else {
      mDayNightHelper.setMode(DayNight.DAY);
      setTheme(R.style.DayTheme);
    }
  }

  /**
   * 使用简书的实现套路来切换夜间主题
   */
  private void changeThemeByJianShu() {
    toggleThemeSetting();
    refreshUI();
  }

  /**
   * 使用知乎的实现套路来切换夜间主题
   */
  private void changeThemeByZhiHu() {
    showAnimation();
    toggleThemeSetting();
    refreshUI();
  }  

  /**
   * 刷新UI界面
   */
  private void refreshUI() {
    TypedValue background = new TypedValue();//背景色
    TypedValue textColor = new TypedValue();//字体颜色
    Resources.Theme theme = getTheme();
    theme.resolveAttribute(R.attr.clockBackground, background, true);
    theme.resolveAttribute(R.attr.clockTextColor, textColor, true);

    mHeaderLayout.setBackgroundResource(background.resourceId);

    for (RelativeLayout layout : mLayoutList) {
      layout.setBackgroundResource(background.resourceId);
    }
 
    for (CheckBox checkBox : mCheckBoxList) {
      checkBox.setBackgroundResource(background.resourceId);
    }

    for (TextView textView : mTextViewList) {
      textView.setBackgroundResource(background.resourceId);
    }
    Resources resources = getResources();

    for (TextView textView : mTextViewList) {
      textView.setTextColor(resources.getColor(textColor.resourceId));
    }

    int childCount = mRecyclerView.getChildCount();
 
    for (int childIndex = 0; childIndex < childCount; childIndex++) {
      ViewGroup childView = (ViewGroup) mRecyclerView.getChildAt(childIndex);
      childView.setBackgroundResource(background.resourceId);
      View infoLayout = childView.findViewById(R.id.info_layout);
      infoLayout.setBackgroundResource(background.resourceId);
      TextView nickName = (TextView) childView.findViewById(R.id.tv_nickname);
      n										
  上一篇   [返回首页] [打印] [返回上页]   下一篇