轉(zhuǎn)載:
ximsfei的博客地址:
http://blog.csdn.net/ximsfei
1
背景
換膚方案原理在網(wǎng)上已經(jīng)很多了, 這里不再詳細(xì)描述, 強迫癥的我總是想讓提供給別人使用的SDK盡量好用, 哪怕是給自己帶來額外的工作量, 經(jīng)過一段時間的奮斗, 實現(xiàn)了一個自我感覺良好的換膚框架.
這里主要來看看Android 源碼中”com.android.support:appcompat-v7”包的實現(xiàn), 以及源碼思想在Android-skin-support中的應(yīng)用 – 如何打造一款好用的換膚框架.
2
appcompat-v7包實現(xiàn)
首先來看一下源碼的實現(xiàn):
AppCompatActivity源碼
AppCompatActivity 將大部分生命周期委托給了AppCompatDelegate。
看下AppCompateDelegate的子類AppCompatDelegateImplV9實現(xiàn):
從這可以看出通過實現(xiàn)LayoutInflaterFactory接口來實現(xiàn)換膚至少可以支持到api 9以上。
網(wǎng)上很多換膚框架的實現(xiàn), 通過LayoutInflater.setFactory的方式, 在回調(diào)的onCreateView中解析每一個View的attrs, 判斷是否有已標(biāo)記需要換膚的屬性, 比方說background, textColor, 或者說相應(yīng)資源是否為skin_開頭等等.
然后保存到map中, 對每一個View做for循環(huán)去遍歷所有的attr, 想要對更多的屬性進行換膚, 需要Activity實現(xiàn)接口, 將需要換膚的View, 以及相應(yīng)的屬性收集到一起。
那么是不是能夠?qū)で笠环N讓使用者更方便的方式來實現(xiàn), 做一個侵入性盡量小的框架呢?
本著開發(fā)者應(yīng)有的好奇心, 深入的研究了一些v7包的實現(xiàn):
AppCompatDelegateImplV9中, 在LayoutInflaterFactory的接口方法
onCreateView 中將View的創(chuàng)建交給了AppCompatViewInflater
再來看一下AppCompatViewInflater中createView的實現(xiàn)
再看一下其中一個類AppCompatTextView的實現(xiàn)
AppCompatBackgroundHelper.Java
到這里我仿佛是發(fā)現(xiàn)了新大陸一樣興奮, 源碼中可以通過攔截View創(chuàng)建過程, 替換一些基礎(chǔ)的組件, 然后對一些特殊的屬性(eg: background, textColor) 做處理, 那我們?yōu)槭裁床荒軐⑦@種思想拿到換膚框架中來使用呢?
3
Android-skin-support換膚框架實現(xiàn)
抱著試一試不會少塊肉的心情, 開始了我的換膚框架開發(fā)之路。
先簡單講一下原理:
- 參照源碼實現(xiàn)在Activity onCreate中為LayoutInflater setFactory, 將View的創(chuàng)建過程交給自定義的SkinCompatViewInflater類來實現(xiàn)
- 重寫系統(tǒng)組件, 實現(xiàn)換膚接口, 表明該控件支持換膚, 并在View創(chuàng)建之后統(tǒng)一收集
- 在重寫的View中解析出需要換膚的屬性, 并保存ResId到成員變量
- 重寫類似setBackgroundResource方法, 解析需要換膚的屬性, 并保存變量
- applySkin 在切換皮膚的時候, 從皮膚資源中獲取資源
下面說一個簡單的例子(SkinCompatTextView):
- 實現(xiàn)SkinCompatSupportable接口 2. 在構(gòu)造方法中通過SkinCompatBackgroundHelper和SkinCompatTextHelper分別解析出background, textColor并保存 3. 重寫setBackgroundResource和setTextAppearance, 解析出對應(yīng)的資源Id, 表明該控件支持從代碼中設(shè)置資源, 且支持該資源換膚 4. 在用戶點擊切換皮膚時調(diào)用applySkin方法設(shè)置皮膚
對于其他View也一樣,復(fù)寫其換膚相關(guān)的API,統(tǒng)一對外入口為applySkin。
SkinCompatTextHelper的部分實現(xiàn):
4
開發(fā)過程中遇到的一些問題
在5.0以上, 使用color為ImageView設(shè)置src, 可以通過getColorStateList獲取資源, 而在5.0以下, 需要通過ColorDrawable setColor的方式實現(xiàn)
還有很多問題, 有興趣的同學(xué)可以來一起交流解決.
5
總結(jié)
這樣的做法與網(wǎng)上其他框架相比優(yōu)勢在哪里, 為什么重復(fù)造輪子?
在增加框架開發(fā)成本的基礎(chǔ)上降低了框架使用的成本, 我覺得更有意義, 一次開發(fā), 所有Android 開發(fā)者都受用;
換膚框架對業(yè)務(wù)代碼的侵入性比較小, 業(yè)務(wù)代碼只需要繼承自SkinCompatActivity, 不需要實現(xiàn)接口重寫方法, 不需要其他額外的代碼, 接入方便, 假如將來不想再使用本框架, 只需要把SkinCompatActivity改為原生Activity即可;
深入源碼, 和源碼實現(xiàn)方式類似, 兼容性更好.
為什么選擇繼承自AppCompatActivity, AppCompatTextView…而不是選擇直接繼承自Activity, TextView…
本身appcompat-v7包是一個support包, 兼容原生控件, 同時符合Material design, 我們只需要獲取我們想要換膚的屬性就可以在不破壞support包屬性的前提下進行換膚;
參與開發(fā)的同學(xué)更多的話, 完全可以支持一套繼承自Activity, TextView…的skin support包.
自定義View能否支持, 第三方控件是否支持換膚
答案是肯定的, 完全可以參照SkinCompatTextView的實現(xiàn), 自己去實現(xiàn)自定義控件, 對于使用者來說, 擴展性很好.