zoukankan      html  css  js  c++  java
  • 【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动画消失,这样切换主题动作就会变的很柔和了

  • 相关阅读:
    27 Spring Cloud Feign整合Hystrix实现容错处理
    26 Spring Cloud使用Hystrix实现容错处理
    25 Spring Cloud Hystrix缓存与合并请求
    24 Spring Cloud Hystrix资源隔离策略(线程、信号量)
    23 Spring Cloud Hystrix(熔断器)介绍及使用
    22 Spring Cloud Feign的自定义配置及使用
    21 Spring Cloud使用Feign调用服务接口
    20 Spring Cloud Ribbon配置详解
    19 Spring Cloud Ribbon自定义负载均衡策略
    18 Spring Cloud Ribbon负载均衡策略介绍
  • 原文地址:https://www.cnblogs.com/kimmy/p/4555197.html
Copyright © 2011-2022 走看看