• 【android】夜间模式简单实现


    完整代码,请参考我的博客园客户端,git地址:http://git.oschina.net/yso/CNBlogs

    关于阅读类的app,有个夜间模式真是太重要了。

    那么有两种方式可以实现夜间模式

    1:修改theme,重启activity

    优点:正儿八经的夜间模式,配色看着舒服

    缺点:图片刺眼、闪屏

    核心思路:自定义一个颜色属性名 A,A在日间和夜间模式下都有具体的颜色代码,页面布局文件只管引用A,至于是日间还是夜间,由后台主题决定。

    2:使用一个带黑色带透明度的View,盖在现有的activity上,效果类似你带上墨镜,看着太阳不刺眼。

    优点:不用重启activity,不闪屏;加上透明度过渡动画,模式之间切换非常舒服,解决了1中,白底图片依旧刺眼的问题。;

    缺点:配色没变化,就算带上墨镜,白天依旧是白天。

    核心思路:使用WindowManager,在当前activity上,通过addView,添加一个黑色带透明度的View。

    本方案整合了两种解决方案。在夜间配色的基础上,再加上一层墨镜,让图片也变得柔和起来,效果图如下:

    可以看待chrome图标的白色底,在夜间模式下也变得柔和了

    好,下面来讲讲具体的实现步骤,本环节使用的开发环境是android Studio

    1 首先,在values下要准备好三个文件,没有就自己创建

    attrs.xml(声明属性的类型,布局xml中用) reference可以使用系统的资源ID,比如R.color.gray; color可以直接使用#ffffff颜色代码

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <attr name="containerBackground" format="reference|color"></attr>
        <attr name="cardBackground" format="reference|color"></attr>
        <attr name="titleColor" format="reference|color"></attr>
        <attr name="textColor" format="reference|color"></attr>
        <attr name="selectorBtn" format="reference"></attr>
        <attr name="selectorListItem" format="reference"></attr>
    </resources>

    colors.xml(调色板,集中管理颜色hex)遵循优秀格式规范,即调色板模式,避免使用btn1,btn2,fontTitle,fontText之类的颜色名。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <color name="white">#fafafa</color>
        <color name="white_dark">#f3f3f3</color>
    
        <color name="gray_light">#cccccc</color>
        <color name="gray">#777</color>
        <color name="gray_dark">#383838</color>
    
        <color name="green_light">#8e9ea4</color>
        <color name="green">#34515c</color>
        <color name="green_dark">#1e3e4a</color>
    
        <color name="night_mask">#90000000</color>
    </resources>

    styles.xml(日间、夜间主题)

    <resources>
        <!-- Application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    
        </style>
        <!-- 日间模式 -->
        <style name="AppTheme.day" parent="AppTheme">
            <item name="containerBackground">@color/white_dark</item>
            <item name="titleColor">@color/gray_dark</item>
            <item name="textColor">@color/gray</item>
            <item name="selectorBtn">@drawable/navigator_list_item_day</item>
            <item name="selectorListItem">@drawable/list_item_day</item>
        </style>
        <!-- 夜间模式 -->
        <style name="AppTheme.night" parent="AppTheme">
            <item name="containerBackground">@color/green_dark</item>
            <item name="titleColor">@color/white_dark</item>
            <item name="textColor">@color/green_light</item>
            <item name="selectorBtn">@drawable/navigator_list_item_night</item>
            <item name="selectorListItem">@drawable/list_item_night</item>
        </style>
    </resources>

    2定义activity父类,自动托管日间、夜间模式

    BaseApplication就是自己包装的Application,通过它,保存日间、夜间模式

     Application和Activity,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)。

      android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局 的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享 等,数据缓存等操作。

    public class BaseActionBarActivity extends ActionBarActivity {
    
        private BaseApplication mBaseApp = null;
        private WindowManager mWindowManager = null;
        private View mNightView = null;
        private LayoutParams mNightViewParam; 
        private boolean mIsAddedView;   
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            mBaseApp = (BaseApplication) getApplication();
    
            if (mBaseApp.isNightMode())
                setTheme(R.style.AppTheme_night);
            else
                setTheme(R.style.AppTheme_day);
    
            super.onCreate(savedInstanceState);
     
            mIsAddedView = false; 
    
            if (mBaseApp.isNightMode()) {
                initNightView();
                mNightView.setBackgroundResource(R.color.night_mask);
            }
     
        }
    
        @Override
        protected void onDestroy() {
            if (mIsAddedView) {
                mBaseApp = null;
                mWindowManager.removeViewImmediate(mNightView);
                mWindowManager = null;
                mNightView = null;
            } 
            super.onDestroy();
        }
      
        public void ChangeToDay() {
            mBaseApp.setIsNightMode(false);
            mNightView.setBackgroundResource(android.R.color.transparent);
        }
    
        public void ChangeToNight() {
            mBaseApp.setIsNightMode(true);
            initNightView();
            mNightView.setBackgroundResource(R.color.night_mask);
        }
    
        /**
         * wait a time until the onresume finish
         */
        public void recreateOnResume() {
            new Handler().postDelayed(new Runnable() {
                public void run() {
                    recreate();
                }
            }, 100);
        }
    
        private void initNightView() {
            if (mIsAddedView == true)
                return;
            mNightViewParam = new LayoutParams(
                    LayoutParams.TYPE_APPLICATION,
                    LayoutParams.FLAG_NOT_TOUCHABLE | LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSPARENT);
    
            mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
            mNightView = new View(this);
            mWindowManager.addView(mNightView, mNightViewParam);
            mIsAddedView = true;
        }
    }

    值得一提的是recreateOnResume()函数,因为是从Resume里面重建activity的(避免闪屏)此时,直接调用系统的recreate函数时,会报错,原因是resume还没执行完,就被recreate了,因此,我们函数需要延时一会,等待系统完成resume就好了。一般延时1毫秒就可以了,但是我的app里面用到抽屉式导航栏,保存的时间要长点。

    准备工作到这里已经结束了。只要activity集成自该父类,就会自动托管日间、夜间模式了

    3调用方式

    在布局文件里,引用颜色的地方,我们使用问号来访问定义在styles.xml里面主题的属性。换句话说,这种方式的神奇之处在于,我在夜间主题、白天主题都定义了titleColor,布局只管引用titleColor,至于是白天的还是晚上的,则由activity的setTheme指定。

    ?表示引用属性
    “?”引用主题属性,当您使用这个标记,你所提供的资源名必须能够在主题属性中找到,因为资源工具认为这个资源属性是被期望得到的,您不需要明确的指出它的类型

            <TextView
                android:id="@+id/base_swipe_item_title"
                android:layout_width="0dp"
                android:layout_height="100dp"
                android:layout_weight="1"
                android:gravity="left|center_vertical"
                android:textColor="?titleColor"
                android:textSize="21sp" />

    子类只要调用ChangeToDay、ChangeToNight就可以完成模式的切换了。别忘了recreate activity来生效哦,实例代码如下:

      void changeViewMode() {
            boolean isNight = getMyApplication().isNightMode();
            if (isNight)
                ChangeToDay();
            else
                ChangeToNight();
    
            recreate();
        }

    OK,如过你能坚持看到这里,说明阁下是有耐心的人,奉上一个彩蛋。

    关于WebView网页如何实现日间、夜间模式。

    这个问题比我们现象的要简单的多。因为网页么,在生成html内容时,只要根据日间、还是夜间模式,替换css路径为日间.css、夜间.css就好了。

    栗子来了,瞧,就是这个"{style}",我们要替换的对象。

    <html>
    <head>
        <title>Cnblogs</title>
        <link rel="stylesheet" type="text/css" href="{style}"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
        minimum-scale=1.0, user-scalable=no"/>
    </head>

    在对webview加载内容文本时,替换该字符串的css

    replace("{style}",baseApplication.isNightMode() ? "style_night.css" : "style_day.css")

    结构如下:

    切换主题如何避免闪屏

    我们知道,通过reCreate(),来重启activity的时候,会闪屏;

    那么 在切换之前,先截屏作为activity盖在当前的activity上面,然后重启activity,完毕之后,把盖着的activity通过alpha动画消失,这样切换主题动作就会变的很柔和了

  • 相关阅读:
    使用mysqltools配置读写分离环境
    mysql group replication 主节点宕机恢复
    django ---- models继承
    django -- 对模式进行调式(pay with the api)
    django -- 多对多关系的实现
    django -- verbose_name的对数据库层面的影响
    django -- model中只有Field类型的数据才能成为数据库中的列
    django -- 为model 指定数据库名
    django -- 联合索引
    flask基础之jijia2模板使用基础(二)
  • 原文地址:https://www.cnblogs.com/kimmy/p/4555197.html
Copyright © 2020-2023  润新知