转载:http://blog.csdn.net/bboyfeiyu/article/details/48322023

最近本人需要用到夜间模式,但是经过一番搜索似乎并没有看到好的开源实现,看到有一个类似的库MultipleTheme,但是需要自定义所有要实现换肤功能的View,感觉比较麻烦。而Prism又不支持Theme,且目前已经不再更新,因此这个方案也不能用。当发现现有的解决方案不能很好的解决问题时,往往只能自己实现,因此本人花了点时间简单弄了一个实现该功能的开源库,命名为Colorful。

Colorful是基于Theme,无需重启Activity、无需自定义View,方便的实现日间、夜间模式,github地址为https://github.com/bboyfeiyu/Colorful。

Colorful 动态换肤开源库

基于Theme的Android动态换肤开源库,以简单的方式实现夜间模式。

效果如下:

一、使用方式

1.1 自定义属性

<code class="language-xml hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-pi" style="color: rgb(0, 102, 102); box-sizing: border-box;"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">resources</span>></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><!-- 自定义属性 --></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">attr</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"root_view_bg"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">format</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"reference|color"</span> /></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">attr</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"btn_bg"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">format</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"reference|color"</span> /></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">attr</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"text_color"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">format</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"reference|color"</span> /></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">resources</span>></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

1.2 在布局中使用自定义属性设置View的背景、文本颜色等属性

activity_main.xml中的布局:

<code class="language-xml hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">RelativeLayout</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">xmlns:android</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"http://schemas.android.com/apk/res/android"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">xmlns:tools</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"http://schemas.android.com/tools"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/root_view"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:background</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"?attr/root_view_bg"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">tools:context</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"com.example.androidthemedemo.MainActivity"</span> ></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">TextView
</span>        <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/textview"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"wrap_content"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:gravity</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"center"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:text</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@string/change_theme"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:textColor</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"?attr/text_color"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:textSize</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"20sp"</span> /></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">Button
</span>        <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/change_btn"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"wrap_content"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_below</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@id/textview"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_marginTop</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"20dp"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:text</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@string/change_theme"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:textColor</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"?attr/text_color"</span> /></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">Button
</span>        <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/second_btn"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"wrap_content"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_below</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@id/change_btn"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_marginTop</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"20dp"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:text</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@string/sec_act"</span>/></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">ListView
</span>        <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/listview"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_below</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@id/second_btn"</span><span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_marginTop</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"20dp"</span> /></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">RelativeLayout</span>></span>
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li></ul>

例如上述布局中我们将root_view的背景设置为"?attr/root_view_bg",代表它的背景是自定义属性root_view_bg的值,还有Textview和Button的textColor属性设置为"?attr/text_color"

1.3 定义多个Theme

然后在不同的Theme中为这些属性设置不同的值,例如,通常我们有日间和夜间模式两种颜色模式。styles.xml中的完整代码如下:

<code class="language-xml hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">resources</span>></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"AppBaseTheme"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">parent</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"Theme.AppCompat.Light"</span>></span><span class="css" style="box-sizing: border-box;"></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span>></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><!-- Application theme. --></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"AppTheme"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">parent</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"AppBaseTheme"</span>></span><span class="css" style="box-sizing: border-box;"></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span>></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><!-- 日间主题 --></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"DayTheme"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">parent</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"AppTheme"</span>></span><span class="css" style="box-sizing: border-box;"><<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">item</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">name</span>="<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">root_view_bg</span>"><span class="hljs-at_rule" style="box-sizing: border-box;">@<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">drawable/bg_day</item></span><item name=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"btn_bg"</span>>@color/white_btn_color</item><item name=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"text_color"</span>>@color/black_tx_color</item></span></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span>></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><!-- 夜间主题 --></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"NightTheme"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">parent</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"AppTheme"</span>></span><span class="css" style="box-sizing: border-box;"><<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">item</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">name</span>="<span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">root_view_bg</span>"><span class="hljs-at_rule" style="box-sizing: border-box;">@<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">drawable/bg_night</item></span><item name=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"btn_bg"</span>>@color/black_btn_color</item><item name=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"text_color"</span>>@color/white_tx_color</item></span></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">style</span>></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">resources</span>></span>
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>

两个主题下为同一个属性设置了不同的值,达到切换主题时修改View的相关属性的目的。例如定义在colors.xml中的颜色值。

<code class="language-xml hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-pi" style="color: rgb(0, 102, 102); box-sizing: border-box;"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">resources</span>></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><!-- 日间模式 --></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"white_btn_color"</span>></span>#3BB32E<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span>></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"black_tx_color"</span>></span>#333333<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span>></span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><!-- 夜间模式 --></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"black_btn_color"</span>></span>#aa7788<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span>></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">name</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"white_tx_color"</span>></span>#f0f0f0<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">color</span>></span><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">resources</span>></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

1.4 设置要修改的View的属性

下面我们为activity_main.xml中的视图进行换肤设置:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">ListView  mNewsListView = (ListView) findViewById(R.id.listview);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 为ListView设置要修改的属性,在这里没有对ListView本身的属性做修改</span>
ViewGroupSetter listViewSetter = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ViewGroupSetter(mNewsListView, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 绑定ListView的Item View中的news_title视图,在换肤时修改它的text_color属性</span>
listViewSetter.childViewTextColor(R.id.news_title, R.attr.text_color);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构建Colorful对象</span>
Colorful mColorful = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Colorful.Builder(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>).backgroundDrawable(R.id.root_view, R.attr.root_view_bg) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 设置view的背景图片</span>.backgroundColor(R.id.change_btn, R.attr.btn_bg) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 设置按钮的背景色</span>.textColor(R.id.textview, R.attr.text_color) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 设置文本颜色</span>.setter(listViewSetter)           <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 手动设置setter</span>.create();
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

首先我们定义了一个listViewSetter,该Setter用于为ListView的每个Item View中的news_title控件设置文本颜色,文本颜色的值是自定义属性text_color的颜色值。然后构建Colorful对象,并且id分别为change_btn、root_view、textview的控件绑定特定属性值,例如backgroundDrawable(R.id.root_view, R.attr.root_view_bg)代表root_view的背景Drawable为自定义属性root_view_bg的值,textColor(R.id.textview, R.attr.text_color)表示id为textview的TextView控件的文本颜色为R.attr.text_color的值。这些属性都在不同的Theme中有不同的值,因此切换Theme时就会发生变化。然后我们将listViewSetter添加到Colorful对象中,在修改主题时被遍历ListView中的所有Item View,然后修改news_title控件的文本颜色。

1.5 切换主题

最后通过Colorful对象设置主题即可实现切换,代码如下:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isNight = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span> ;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 切换主题</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">changeThemeWithColorful</span>() {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!isNight) {mColorful.setTheme(R.style.DayTheme);} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {mColorful.setTheme(R.style.NightTheme);}isNight = !isNight;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

时间仓促,功能简单,希望有时间的同学积极加入,将功能慢慢完善起来!

------------------------------------------------

第二篇

http://blog.csdn.net/marktheone/article/details/48354369

首发于我的个人博客

Android换肤技术总结

背景

纵观现在各种Android app,其换肤需求可以归为 
- 白天/黑夜主题切换(或者别的名字,通常2套),如同花顺/自选股/天天动听等,UI表现为一个switcher。 
- 多种主题切换,通常为会员特权,如QQ/QQ空间。

对于第一种来说,目测应该是直接通过本地theme来做的,即所有图片/颜色的资源都在apk里面打包了。

而对于第二种,则相对复杂一些,由于作为一种线上服务,可能上架新皮肤,且那么多皮肤包放在apk里面实在太占体积了,所以皮肤资源会在选择后再进行下载,也就不能直接使用android的那套theme。

技术方案

内部资源加载方案和动态下载资源下载两种。

动态下载可以称为一种黑科技了,因为往往需要hack系统的一些方法,所以在部分机型和新的API上有时候可能有坑,但相对好处则很多 
- 图片/色值等资源由于是后台下发的,可以随时更新 
- APK体积减小 
- 对应用开发者来说,换肤几乎是透明的,不需要关心有几套皮肤 
- 可以作为增值服务卖钱!!

内部资源加载方案

内部资源加载都是通过android本身那套theme来做的,相对业务开发来说工作量更大(需要定义attr和theme),不同方案类似地都是在BaseActivity里面做setTheme,差别主要在解决以下2个问题的策略: 
- setTheme后如何实时刷新,而不用重新创建页面(尤其是listview里面的item)。 
- 哪些view需要刷新,刷新什么(背景?字体颜色?ImageView的src?)。

自定义view

MultipleTheme 
做自定义view是为了在setTheme后会去立即刷新,更新页面UI对应资源(如TextView替换背景图和文字颜色),在上述项目中,则是通过对rootView进行遍历,对所有实现了ColorUiInterface的view/viewgroup进行setTheme操作来实现即使刷新的。

显然这样太重了,需要把应用内的各种view/viewgroup进行替换。

手动绑定view和要改变的资源类型

Colorful

这个…我们看看用法吧….

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">ViewGroupSetter listViewSetter = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ViewGroupSetter(mNewsListView);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 绑定ListView的Item View中的news_title视图,在换肤时修改它的text_color属性</span>
listViewSetter.childViewTextColor(R.id.news_title, R.attr.text_color);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构建Colorful对象来绑定View与属性的对象关系</span>
mColorful = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Colorful.Builder(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>).backgroundDrawable(R.id.root_view, R.attr.root_view_bg)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 设置view的背景图片</span>.backgroundColor(R.id.change_btn, R.attr.btn_bg)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 设置背景色</span>.textColor(R.id.textview, R.attr.text_color).setter(listViewSetter) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 手动设置setter</span>.create(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 设置文本颜色</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

我就是想换个皮肤,还得在activity里自己去设置要改变哪个view的什么属性,对应哪个attribute?是不是成本太高了?而且activity的逻辑也很容易被弄得乱七八糟。

动态资源加载方案

resource替换

开源项目可参照Android-Skin-Loader。

即覆盖application的getResource方法,优先加载本地皮肤包文件夹下的资源包,对于性能问题,可以通过attribute或者资源名称规范(如需要换肤则用skin_开头)来优化,从而不对不换肤的资源进行额外开销。

可以重点关注该项目中的SkinInflaterFactorySkinManager(实现了自己的getColor、getDrawable方法)。

不过由于Android 5.1源码里,getDrawable方法的实现被修改了,所以会导致无法跟肤的问题(其实是loadDrawable被修改了,连参数都改了,类似的内部API大改在5.1上还很多)。

4.4的源码中Resources.java:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Drawable <span class="hljs-title" style="box-sizing: border-box;">getDrawable</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> id) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> NotFoundException {TypedValue value;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (mAccessLock) {value = mTmpValue;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (value == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {value = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TypedValue();} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {mTmpValue = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;}getValue(id, value, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>);}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 实际资源通过loadDrawable方法加载</span>Drawable res = loadDrawable(value, id);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (mAccessLock) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mTmpValue == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {mTmpValue = value;}}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> res;
}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// loadDrawable会去preload的LongSparseArray里面查找</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*package*/</span> Drawable loadDrawable(TypedValue value, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> id)<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> NotFoundException {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (TRACE_FOR_PRELOAD) {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Log only framework resources</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((id >>> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">24</span>) == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0x1</span>) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String name = getResourceName(id);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (name != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) android.util.Log.d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"PreloadDrawable"</span>, name);}}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isColorDrawable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&value.type <= TypedValue.TYPE_LAST_COLOR_INT) {isColorDrawable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> key = isColorDrawable ? value.data :(((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span>) value.assetCookie) << <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">32</span>) | value.data;Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (dr != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> dr;}......<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> dr;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li></ul>

而5.1代码里Resources.java:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 可以看到,方法参数里面加上了Theme</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Drawable <span class="hljs-title" style="box-sizing: border-box;">getDrawable</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> id, @Nullable Theme theme) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> NotFoundException {TypedValue value;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (mAccessLock) {value = mTmpValue;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (value == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {value = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TypedValue();} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {mTmpValue = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;}getValue(id, value, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>);}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Drawable res = loadDrawable(value, id, theme);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (mAccessLock) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mTmpValue == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {mTmpValue = value;}}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> res;
}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*package*/</span> Drawable loadDrawable(TypedValue value, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> id, Theme theme) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> NotFoundException {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (TRACE_FOR_PRELOAD) {<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Log only framework resources</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((id >>> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">24</span>) == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0x1</span>) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String name = getResourceName(id);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (name != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {Log.d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"PreloadDrawable"</span>, name);}}}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isColorDrawable;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> key;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (value.type >= TypedValue.TYPE_FIRST_COLOR_INT&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {isColorDrawable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;caches = mColorDrawableCache;key = value.data;} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {isColorDrawable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;caches = mDrawableCache;key = (((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span>) value.assetCookie) << <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">32</span>) | value.data;}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// First, check whether we have a cached version of this drawable</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// that was inflated against the specified theme.</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!mPreloading) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Drawable cachedDrawable = getCachedDrawable(caches, key, theme);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (cachedDrawable != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> cachedDrawable;}}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li></ul>

方法名字都改了

Hack Resources internally

黑科技方法,直接对Resources进行hack,Resources.java:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Information about preloaded resources.  Note that they are not</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// protected by a lock, because while preloading in zygote we are all</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// single-threaded, and after that these are immutable.</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables= <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LongSparseArray<Drawable.ConstantState>();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> LongSparseArray<ColorStateList> sPreloadedColorStateLists= <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LongSparseArray<ColorStateList>();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>

直接对Resources里面的这三个LongSparseArray进行替换,由于apk运行时的资源都是从这三个数组里面加载的,所以只要采用interceptor模式:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">DrawablePreloadInterceptor</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">LongSparseArray</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Drawable</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ConstantState</span>></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

自己实现一个LongSparseArray,并通过反射set回去,就能实现换肤,具体getDrawable等方法里是怎么取preload数组的,可以自己看Resources的源码。

等等,就这么简单?,NONO,少年你太天真了,怎么去加载xml,9patch的padding怎么更新,怎么打包/加载自定义的皮肤包,drawable的状态怎么刷新,等等。这些都是你需要考虑的,在存在插件的app中,还需要考虑是否会互相覆盖resource id的问题,进而需要修改apt,把resource id按位放在2个range。

手Q和独立版QQ空间使用的是这种方案,效果挺好。

总结

尽管动态加载方案比较黑科技,可能因为系统API的更改而出问题,但相对来所 
好处有 
- 灵活性高,后台可以随时更新皮肤包 
- 相对透明,开发者几乎不用关心有几套皮肤,不用去定义各种theme和attr,甚至连皮肤包的打包都可以交给设计或者专门的同学 
- apk体积节省 
存在的问题 
没有完善的开源项目,如果我们采用动态加载的第二种方案,需要的项目功能包括: 
- 自定义皮肤包结构 
- 换肤引擎,加载皮肤包资源并load,实时刷新。 
- 皮肤包打包工具 
- 对各种rom的兼容

如果有这么一个项目的话,就一劳永逸了,有兴趣的同学可以联系一下,大家一起搞一搞。

内部加载方案大同小异,主要解决的都是即时刷新的问题,然而从目前的一些开源项目来看,仍然没有特别简便的方案。让我选的话,我宁愿让界面重新创建,比如重启activity,或者remove所有view再添加回来。

Android 换皮肤相关推荐

  1. android换皮肤思路总结

       前段时间公司有做换皮肤的项目,经过网上搜罗,查看资料,我个人总结三种换皮肤的方法.     网上说的最多的就是使用android:sharedUserId标签来共享资源,但是经我测试无论用不用这 ...

  2. Android UI换皮肤或 白天黑夜模式

    Android UI换皮肤或 白天黑夜模式 UI换皮肤或白天黑夜模式,从产品上来看,是两种不同产品设计模式:白天黑夜模式只有两种模式:而换皮肤可以有多套,可以进行商业化,并盈利. > 白天夜间模 ...

  3. android 类似QQ 换皮肤 实现思路 apk资源共享

    android 类似QQ 换皮肤 实现思路 apk资源共享 发布:t80t90s | 发布时间: 2013年3月30日 原文:http://t80t90s.com/view.asp?id=23 1.首 ...

  4. android+自定义皮肤,android studio自定义更换皮肤详细图文教程

    android studio这款app程序开发软件内也内置了多种皮肤主题,程序开发人员如果感觉一种皮肤太过单调乏味,可以选择使用软件内的其他皮肤风格,软件默认的皮肤是IntelliJ,还有黑色的Dra ...

  5. Android 换肤demo,轻量快捷接入集成,判断是否夜间模式

    true为黑夜模式 //检查当前系统是否已开启暗黑模式 public static boolean getDarkModeStatus(Context context) {int mode = con ...

  6. android 换肤(1)——插件式无缝换肤(解析鸿洋大神的换肤流程)

    对于app换肤,这是一个常见而又常用的功能.虽然我做的项目中还没涉及到换肤,但是还是想研究下. 于是,下载了鸿洋大神的换肤demo来研究. 先看效果图:(尊重鸿洋大神的代码,效果图上原创) 鸿洋大神的 ...

  7. android换肤哪个简单,Android换肤

    这是一个Android换肤的库,代码量极少,支持换肤的情况还比较多,提供了以下功能: 无需重启,一键换肤效率高 支持App内多套皮肤换肤 支持插件式动态换肤 支持Activity,Fragment,以 ...

  8. android换肤动画,Android换肤(二) — 插件式换肤

    ###前言 上节我们讲到了`Android-skin-support`库的应用内换肤,大家感兴趣的可以参看文章: [Android换肤(一) - 应用内换肤](http://www.demodashi ...

  9. Android-skin-support 一款用心去做的Android 换肤框架

    介绍 Github地址: https://github.com/ximsfei/Android-skin-support Android-skin-support: 一款用心去做的Android 换肤 ...

最新文章

  1. Nebula3的Input系统
  2. 2021 三月1日雅思口语考试反思
  3. 【thymeleaf】 Link (URL) expressions: 使用 Link 表达式创建 URL
  4. MySql基础教程(三)——查询训练
  5. Sacrilege –自定义SWT滚动条
  6. ubuntu复制文件到另一个文件夹_Excel VBA之FSO-2.2文件夹的复制
  7. jsoup Java HTML解析器
  8. 2015年数学建模-A影子定位
  9. matlab峰值提取,如何从MATLAB中的自相关数据中提取峰值?
  10. 最简单易懂的SpringCloudSleuth教程(spring cloud体系的链路追踪系统)
  11. html游戏官网制作 英雄联盟LOL游戏网站设计与实现 (web课程与设计)
  12. 不知道玩什么游戏的你看过来
  13. 搭建oa服务器后怎么接入到微信,微信oa是什么?微信如何与OA进行结合?
  14. 重庆大学计算机学院张敏,张敏(安徽大学生命科学学院院长)_百度百科
  15. Mysql相关操作与命令
  16. 什么是金手指,金手指的设计要求有哪些?
  17. numpy.random.normal函数
  18. 涵数计算机怎么玩游戏,科学计算器
  19. matlab中辗转相除法,基于Matlab的辗转相除法
  20. 当火车进入隧道,耳朵感到不舒服以后

热门文章

  1. 中国汽车制造行业市场供需与战略研究报告
  2. 如何在ipad上玩转思维导图软件
  3. 达摩院2023年十大科技趋势预测,第一个太火了!
  4. 如何使用DEMON生成镜像文件
  5. Markdown基本语法学习,工具Typora
  6. 软件测试_BadBoy自动化测试工具2_录制脚本
  7. IT6616: 高密度传感器 1.4 至 MIPI 中级/数轴型转换器
  8. C#控制台简单魔塔小游戏
  9. 报错 ESET-NOD32 或 a.gray.Bulimia.b (腾讯) 病毒
  10. elasticsearch搜索引擎下载安装