Android 换皮肤


转载: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再添加回来。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值