Night模块(二)

NightMode的具体实现这个手法值得梳理归纳一下.

<1>首先,NightMode的切换并不是简单的变暗/亮<好吧我们之间的NightMode是这么搞的>,
而是整体配色方案的变化<比如反色>,注意是整体,有一个View没有变过来就会造成强烈的违和感.
整体配色方案的变化包括了很多细节, View的background, 字体颜色,如果显示图像,也要变暗/亮.
并且NightMode的切换显然应该是点了就生效.
最常规的思路可能就是把每个View的background之类的全部change一遍,这个会非常恶心,不知道会有多少ugly的code.
这里就使用了android本身的selector机制,
xml中的selector用的最多的就是为View的selected/unselected提供两种资源,android:state_selected这个flag在管理.
那么套到NightMode中,完全也可以搞一套类似的机制,在selector中为NightMode/LightMode提供两种资源,一个flag在管理,
而这里的这个flag,就需要自己在attr.xml中定义了:
http://stackoverflow.com/questions/3441396/defining-custom-attrs/3441986#3441986 教程
只定义这个flag显然不够,虽然可以在selector 中work,但是当用户change NightMode时,怎么使这个flag变化,
selected状态的变化是因为View本身的setSelected函数<触发了refreshDrawableState>.
那么可以仿效这个操作,为一个View增加一个SetNightMode(boolean)的函数,在调用时,如果发现NightMode确实变化了,
那么就更新内部维护的NightModeFlag,然后refreshDrawableState,而refreshDrawableState一定会触发onCreateDrawableState()
这里如果确实是NightMode,那么就需要在super的onCreateDrawableState()创建的DrawableStates中将之间在attr.xml中自定义的
flag merge进去<mergeDrawableStates(orignalDrawableState, R.attr.night_mode);>,
然后返回,这样就会触发selector中的flag被打开,
具体的原理: http://blog.csdn.net/fyfcauc/article/details/41941859
onAttchToWindow也要Override,在onAttchToWindow就为这个View 根据Setting中的NightMode flag调用其setNightMode,
这样,在Night/LightMode下,新加入window View就直接保持了一致.
注意,上面这个手法都不同的View影响范围是不一样的:
(1)View类,只影响background:
(2)ImageView,除了background以外,还会有source.
(3)TextView,除了background以外,还会有text.
(4)Button同View.
(5)CheckBox同View.
(6)XXXLayout同View.
...........
总之一些常用的View都被implements了NightMode这个Interface,使其可以被setNightMode.
然后本项目使用的这些View都被替换为这类View<也是一个大工程,但是已经先进很多了>.
还有一些特殊的View需要额外的工作.
至于怎么将整个APP的View在切换时都setNightMode,就可以从App的ViewRoot开始,
进行BFS/DFS的childView遍历<推荐DFS,好写呀>,只要这个View/ViewGroup实现了NightMode接口<instanceof>,就调用一次方法.
最后就完成了所有View的变化.

<2>LisView:
drawableStateChanged()中要对其divider drawable 同步set相同的drawableState: setState(getDrawableState())

<3>TextView:
CompoundDrawable和BackgroundDrawable还会接收一些自定义的滤镜color设置:
在xml中会加入自定义xmlns:http://schemas.android.com/apk/res-auto<自定义的要使用res-auto>,
attr.xml中增加相应的declare-styleable和子attr
然后在xml View的属性中加入,
在View构造的时候,从其输入的AttributeSet中就可以得到这些自定义的值,类型为ColorStateList
<Lets you map {@link android.view.View} state sets to colors.>
然后在drawableStateChanged()中按照当前的drawableState从输入的ColorStateList中得到要做滤镜的color<getColorForState()>.
然后对要修改的Drawable setColorFilter(color, Mode.MULTIPLY<正片叠底>); 这就实现了Drawable变亮/暗的效果.

<4>ImageView同上.

<5>作为MainView的Webview有另外一套机制,通过注入js实现网页的NightMode.