欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android主题切换之探究白天和夜间模式

程序员文章站 2024-03-04 18:51:06
智能手机的迅速普及,大大的丰富了我们的娱乐生活。现在大家都喜欢晚上睡觉前玩会儿手机,但是应用的日间模式往往亮度太大,对眼睛有较为严重的伤害。因此,如今的应用往往开发了 日间...

智能手机的迅速普及,大大的丰富了我们的娱乐生活。现在大家都喜欢晚上睡觉前玩会儿手机,但是应用的日间模式往往亮度太大,对眼睛有较为严重的伤害。因此,如今的应用往往开发了 日间和夜间 两种模式供用户切换使用,那日间和夜间模式切换究竟是怎样实现的呢?

在文字类的app上面基本上都会涉及到夜间模式、就是能够根据不同的设定、呈现不同风格的界面给用户、而且晚上看着不伤眼睛、实现方式也就是所谓的换肤(主题切换)、对于夜间模式的实现网上流传了很多种方式、这里先分享一个方法给大家、通过设置背景为透明的方法、降低屏幕的亮度与色度。


Android主题切换之探究白天和夜间模式

夜间模式代码

public void night() {
  windowmanager.layoutparams params = new windowmanager.layoutparams(
      layoutparams.match_parent, layoutparams.match_parent,
      layoutparams.type_application,
      windowmanager.layoutparams.flag_not_touchable
          | windowmanager.layoutparams.flag_not_focusable,
      pixelformat.translucent);
  params.gravity=gravity.bottom;
  params.y=10;
  if(myview==null){
    myview=new textview(this);
    myview.setbackgroundcolor(0x80000000);
  }
  mwindowmanager.addview(myview, params);
  editor edit = skinsp.edit();
  edit.putstring("skin", night);
  edit.commit();
}

白天模式

public void day(){
  if(myview!=null){
    mwindowmanager.removeview(myview);
    editor edit = skinsp.edit();
    edit.putstring("skin", day);
    edit.commit();
  }
}

下面通过实例操作来详细展示如何进行android主题切换中的白天/夜间模式。

Android主题切换之探究白天和夜间模式

Android主题切换之探究白天和夜间模式

上述两幅图片,正是两款app的夜间模式效果,所以,依据这个功能,来看看切换主题到底是怎么实现的(当然现在github有好多plugintheme开源插件,很多时候可以使用这些插件,不过我并不想讲怎么用那些插件,正所谓会用*还不如会造*)。

关于更换主题和换肤

这里提到是做换主题功能,当然与之类似的就是换肤,换肤现在比较流行的是采用插件化动态加载技术来实现的,这样可以起到热插拔作用,需要皮肤时候用户自主的在网上下载便是了,不用皮肤时便删了皮肤插件包而不会影响宿主app的功能,这样就不必把一大堆皮肤图片放在本地而增加apk的大小,关于用插件化实现换肤功能这仅仅是插件化技术的冰山一角,关于插件化技术更多的作用,可以看看360前两天开源的 droidplugin插件框架、openaltas框架、还有主席的dl框架。

好了,言归正传,现在我们需要实现的是主题切换功能,关于主题切换其实是切换整个app的颜色风格、排版风格、字体风格等,其中并不会有过多的图片资源的切换,如有过多的图片的更换那就是换肤的功能了。
现在我们要实现夜间/白天模式的切换功能,如下效果图:


Android主题切换之探究白天和夜间模式

可以看到上面的效果正是夜间和白天两种模式的切换功能,切换至夜间模式时整个app的背景色、字体颜色、按钮颜色、标题栏颜色等全部需要切为夜间模式的颜色,当切回白天模式又切回原来的颜色,来看看怎么做的?

实现主题切换

首先就是需要在app中准备两套主题:

白天主题

<resources>

  <style name="daytheme" parent="theme.appcompat.light.darkactionbar">
    <!-- customize your theme here. -->
    <item name="colorprimary">#03a9f4</item>
    <item name="android:textcolorprimary">#ffffff</item>
    <item name="android:windowbackground">@color/background_material_light</item>
    <item name="coloraccent">#00bcd4</item>
    <item name="colorcontrolnormal">#00bcd4</item>

    <item name="titlestyle">@style/daytitlestyle</item>
    <item name="contentstyle">@style/daycontentstyle</item>
    <item name="buttonbg">#2196f3</item>
    <item name="buttontextcolor">#ffffff</item>
    <item name="checktextcolor">#2196f3</item>
    <item name="switchtextcolor">#2196f3</item>
  </style>

  <style name="daytitlestyle">
    <item name="android:textcolor">#212121</item>
    <item name="android:textsize">20sp</item>
    <item name="android:layout_margin">8dp</item>
  </style>

  <style name="daycontentstyle">
    <item name="android:textcolor">#9c27b0</item>
    <item name="android:textsize">16sp</item>
    <item name="android:layout_margin">16dp</item>
    <item name="android:maxlines">10</item>
  </style>

</resources>

夜间主题

<resources>

  <style name="nighttheme" parent="theme.appcompat.light.darkactionbar">
    <!-- customize your theme here. -->
    <item name="colorprimary">#00796b</item>
    <item name="android:textcolorprimary">#212121</item>
    <item name="android:windowbackground">@color/background_material_dark</item>
    <item name="coloraccent">#00796b</item>
    <item name="colorcontrolnormal">#212121</item>

    <item name="titlestyle">@style/nighttitlestyle</item>
    <item name="contentstyle">@style/nightcontentstyle</item>
    <item name="buttonbg">#00796b</item>
    <item name="buttontextcolor">#9e9e9e</item>
    <item name="checktextcolor">#212121</item>
    <item name="switchtextcolor">#212121</item>
  </style>

  <style name="nighttitlestyle">
    <item name="android:textcolor">#212121</item>
    <item name="android:textsize">20sp</item>
    <item name="android:layout_margin">8dp</item>
  </style>

  <style name="nightcontentstyle">
    <item name="android:textcolor">#212121</item>
    <item name="android:textsize">16sp</item>
    <item name="android:layout_margin">16dp</item>
    <item name="android:maxlines">10</item>
  </style>

</resources>

上面这两套主题中,各个属性定义完全一模一样,不一样的只是属性的值,其中在daytheme和nighttheme的style中有这么一段代码:

<item name="titlestyle">@style/daytitlestyle</item>
<item name="contentstyle">@style/daycontentstyle</item>
<item name="buttonbg">#2196f3</item>
<item name="buttontextcolor">#ffffff</item>
<item name="checktextcolor">#2196f3</item>
<item name="switchtextcolor">#2196f3</item>

正常情况下style中是不存在这些属性的,它们这些是自定义属性,主要是用来控制某些控件或者布局的属性,它们的定义在attr文件中:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <attr name="contentstyle" format="reference"/>
  <attr name="titlestyle" format="reference"/>
  <attr name="buttonbg" format="reference|color"/>
  <attr name="buttontextcolor" format="reference|color"/>
  <attr name="checktextcolor" format="reference|color"/>
  <attr name="switchtextcolor" format="reference|color"/>
</resources>

然后在布局中引用即可:

<linearlayout 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:orientation="vertical"
  tools:context=".mainactivity">

  <textview
    style="?attr/titlestyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/title" />

  <textview
    style="?attr/contentstyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />

  <checkbox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="checkbox"
    android:textcolor="?attr/checktextcolor" />

  <checkbox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="checkbox"
    android:textcolor="?attr/checktextcolor" />

  <switch
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="switch"
    android:textcolor="?attr/switchtextcolor" />

  <button
    android:id="@+id/btn_setting"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="设置"
    android:background="?attr/buttonbg"
    android:textcolor="?attr/buttontextcolor" />

</linearlayout>

最后在整个app主题的style中使用它们就ok了。这样做有什么好处呢?我们都知道app设置主题时候都是设置一个style,而app中某些控件或者布局的背景或者style样式需要和整个主题样式不同时,这时候可以通过设置个自定义属性,通过在app的style中给与自定义属性不同的值来达到目的。

切换主题

好了,有了两套主题了,接下来是通过代码来进行控制主题间的切换了,控制主题的切换其实就是通过settheme(r.style.*);来设置不同的style从而达到界面风格的变换,不过这个方法settheme()只在setcontentview()方法前设置才有效,所以如果你想在其它地方调用这个方法来切换主题那是肯定不行的,所以这里有两个难点?

1、怎么处理当前的设置界面在切换主题后同时切换主题风格

2、怎么处理之前已经打开的界面让他们切换主题风格

这里我给出的答案是:

1、在当前切换主题的设置界面使用activity.recreate()方法,该方法的作用就是当当前activity的配置发生变化时,调用这个方法可以把当前activity实例销毁并重新创建出一个activity实例。如此可见通过这个方法可以很容易的解决问题一,因为它会重新创建一个新的activity实例。

2、这里我使用的方法是通过设置intent的flag来达到更新之前activity的效果,通过设置mintent.setflags(intent.flag_activity_new_task | intent.flag_activity_clear_task);让它清除之前的activity再创建一个新的activity,这样当返回之前的界面就可以更新主题了。【注】如果有多个界面可以通过设置主界面mainactivity的launchmode为singletask,在返回主界面时候清除其它界面来更新主题

对于上面的方法(如有更好的方法欢迎告知,万分感谢!)

代码实现

最后再贴下代码:
通过一个主题设置工具类设置主题,在每个activity的setcontentview()方法之前设置主题:
设置主题工具类:

public class themechangeutil {
  public static boolean ischange = false;
  public static void changetheme(activity activity){
    if(ischange){
      activity.settheme(r.style.nighttheme);
    }
  }
}

设置界面:

public class changetheme extends appcompatactivity {
  @override
  protected void oncreate(bundle savedinstancestate) {
    themechangeutil.changetheme(this);
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_change);

    button mchangebtn = (button) findviewbyid(r.id.btn_change);
    mchangebtn.setonclicklistener(new view.onclicklistener() {
      @override
      public void onclick(view v) {
        if (themechangeutil.ischange) {
          themechangeutil.ischange = false;
        } else {
          themechangeutil.ischange = true;
        }
        changetheme.this.recreate();//重新创建当前activity实例
      }
    });
  }

  @override
  public void onbackpressed() {
    super.onbackpressed();
    intent mintent = new intent(this, mainactivity.class);
    mintent.setflags(intent.flag_activity_new_task | intent.flag_activity_clear_task);
    startactivity(mintent);
    finish();
  }
}

主界面:

public class mainactivity extends appcompatactivity {
  @override
  protected void oncreate(bundle savedinstancestate) {
    themechangeutil.changetheme(this);
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);

    button msettingbtn = (button) findviewbyid(r.id.btn_setting);
    msettingbtn.setonclicklistener(new view.onclicklistener() {
      @override
      public void onclick(view v) {
        mainactivity.this.startactivity(new intent(mainactivity.this, changetheme.class));
      }
    });
  }
}

以上就是android主题切换中的白天/夜间模式的详细过程及代码,一开始先给大家简单的展示了代码,而后详细的介绍过程及代码,需要的朋友参考。