針對(duì)自定義及動(dòng)態(tài)創(chuàng)建View的換膚實(shí)踐

一拉馋、背景

公司的業(yè)務(wù)需要使用換膚功能實(shí)現(xiàn)白天/黑夜模式镐牺,調(diào)研了市場(chǎng)主流換膚框架吵护,主要采用了LayoutInflater.Factory接口干涉Xml中View解析的過(guò)程远剩,將創(chuàng)建View的過(guò)程由自己來(lái)接手列赎。但本項(xiàng)目大量使用自定義View及動(dòng)態(tài)創(chuàng)建View宏悦,Xml中描述界面的情況不多,針對(duì)這種情況粥谬,我設(shè)計(jì)了一套輕量級(jí)的實(shí)時(shí)換膚框架肛根。

二、使用

項(xiàng)目UI框架是單Activity+多Fragment的結(jié)構(gòu)漏策,為滿足實(shí)時(shí)刷新派哲,不出現(xiàn)頁(yè)面閃爍的需求,所以從三個(gè)方面來(lái)實(shí)現(xiàn)換膚功能掺喻。

1.Activity

當(dāng)前項(xiàng)目中需要實(shí)時(shí)刷新的Activity只有首頁(yè)和設(shè)置頁(yè)芭届,所以單獨(dú)對(duì)這兩個(gè)Activity進(jìn)行處理。首先需要實(shí)現(xiàn)Skinable接口感耙,在頁(yè)面創(chuàng)建和銷毀時(shí)添加監(jiān)聽褂乍,這樣,主題發(fā)生改變時(shí)即硼,就會(huì)通知到applySkin()方法逃片。

class MainActivity : RootActivity(), MainContract.View, Skinable {
    ...
    private fun initView() {
        SkinManager.instance.register(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        SkinManager.instance.unregister(this)
    }

    override fun applySkin() {
        container.setBackgroundColor(resources.getColor(R.color.bg_color_primary))
        navigation.setBackgroundColor(resources.getColor(R.color.bg_color_primary))
        for (fragment in supportFragmentManager.fragments) {
            if (fragment is RootFragment && fragment.isAdded && !fragment.isDetached) {
                fragment.refreshStatusBar()
                if (fragment is Skinable) {
                    fragment.applySkin()
                }
            }
        }
        changeStatusBarTheme()
    }
    ...
}

2.Fragment

Fragment是UI界面的承載體,所以在RootFragment中實(shí)現(xiàn)了Skinable接口只酥,所有的Fragment需要覆寫applySkin()方法褥实,在里面處理自己的換膚邏輯呀狼,即設(shè)置頁(yè)面控件的顏色屬性(如背景色、字體顏色损离、圖標(biāo)等)哥艇。

3.View

View是每個(gè)頁(yè)面最基礎(chǔ)的元素,出于方便使用和易維護(hù)的角度僻澎,對(duì)這層當(dāng)中的自定義控件和系統(tǒng)控件做了區(qū)別處理貌踏。

系統(tǒng)控件

直接在Fragment的applySkin()里調(diào)用設(shè)置相關(guān)屬性的方法。

showQrCode.background = resources.getDrawable(R.drawable.selector_with_ripple)
addEnigma.setTextColor(resources.getColor(R.color.text_color_tips))

自定義控件

對(duì)于自定義控件窟勃,直接讓控件實(shí)現(xiàn)Skinable接口祖乳,在自定義控件內(nèi)部處理控件自己的換膚邏輯,這樣外部不用考慮內(nèi)部控件的邏輯拳恋,只需在 Fragment的applySkin()方法中直接調(diào)用該自定義控件的applySkin()方法凡资。

class PasswordEditText: AppCompatEditText, Skinable {
    ...
    override fun applySkin() {
        mTextPaint.color = resources.getColor(R.color.text_color_input_hint)
        background = resources.getDrawable(R.drawable.selector_login_edit_bkg)
        setTextColor(resources.getColor(R.color.text_color_minor))
        setHintTextColor(resources.getColor(R.color.text_color_input_hint))
    }
    ...
}

4.資源定義

這里拿黑夜模式進(jìn)行舉例

添加資源目錄

首先需要在build.gralde文件的android節(jié)點(diǎn)中添加res-nigit

android {
    ...
    sourceSets {
        main {
            res.srcDirs = ['src/main/res', 'src/main/res-night']
        }
        ...
    }
}

定義資源名

黑夜模式的資源需要在res_night中加入同名的_night后綴谬运,如果未添加隙赁,默認(rèn)會(huì)取白天模式的。

drawable

colors.xml也需要這樣定義梆暖。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary_night">#333333</color>
    <color name="colorPrimaryDark_night">#00574B</color>
    <color name="colorAccent_night">#26B36D</color>

    <!-- text colors -->
    <color name="text_color_primary_night">#ffffff</color>
    <color name="text_color_minor_night">#cccccc</color>
    <color name="text_color_tips_night">#909399</color>
    ...

    <!-- background colors -->
    <color name="bg_color_primary_night">#333333</color>>
    <color name="bg_color_pressed_night">#2d2d2d</color>
    ...

    <!-- default avatar colors -->
    <color name="default_avatar_red_night">#ED4E4E</color>
    <color name="default_avatar_blue_night">#6C9DD9</color>
    ...

    <!-- bottom sheet colors -->
    <color name="colorSheetText_night">#DE000000</color>
    <color name="colorSheetTitle_night">#8A000000</color>
    <color name="colorSheetDivider_night">#3f717171</color>
    <color name="bg_color_status_bar_night">#333333</color>
</resources>

在這里伞访,推薦大家如果使用圖標(biāo),最好用SVG圖轰驳,除了占用空間小厚掷、縮放無(wú)質(zhì)量損失以外,添加對(duì)于黑夜模式的時(shí)候级解,也只需要修改SVG文件色值即可達(dá)到冒黑。

三、原理

其實(shí)核心思想上面也提到了勤哗,就是繼承Resource抡爹,覆寫了getColor()getDrawable()方法。

class CustomResources(val resources: Resources) :
    Resources(resources.assets, resources.displayMetrics, resources.configuration) {

    override fun getColor(id: Int): Int {
        return SkinManager.instance.getColor(id)
    }

    override fun getDrawable(id: Int): Drawable {
        return SkinManager.instance.getDrawable(id)
    }

    fun updateConfig(config: Configuration?, metrics: DisplayMetrics?) {
        resources.updateConfiguration(config, metrics)
    }
}

然后在SkinManager中通過(guò)SkinResources獲取相應(yīng)主題的資源芒划。

class SkinResources {
    ...
    fun getSkinColor(context: Context, id:Int): Int {
        val resources = context.resources
        val type = resources.getResourceTypeName(id)
        val color = resources.getResourceEntryName(id)
        val identifier = getIdentifier(context, nameConvert(color), type)
        return when {
            identifier != 0 -> resources.getColor(identifier)
            else -> resources.getColor(id)
        }
    }

    fun getSkinDrawable(context:Context,id:Int): Drawable {
        val resources = context.resources
        val type = resources.getResourceTypeName(id)
        val drawable = resources.getResourceEntryName(id)
        val identifier = getIdentifier(context, nameConvert(drawable), type)
        return when {
            identifier != 0 -> resources.getDrawable(identifier)
            else ->resources.getDrawable(id)
        }
    }
    ...
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冬竟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子民逼,更是在濱河造成了極大的恐慌泵殴,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拼苍,死亡現(xiàn)場(chǎng)離奇詭異笑诅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門吆你,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)同蜻,“玉大人,你說(shuō)我怎么就攤上這事早处。” “怎么了瘫析?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵砌梆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我贬循,道長(zhǎng)咸包,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任杖虾,我火速辦了婚禮烂瘫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奇适。我一直安慰自己坟比,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布嚷往。 她就那樣靜靜地躺著葛账,像睡著了一般。 火紅的嫁衣襯著肌膚如雪皮仁。 梳的紋絲不亂的頭發(fā)上籍琳,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音贷祈,去河邊找鬼趋急。 笑死,一個(gè)胖子當(dāng)著我的面吹牛势誊,可吹牛的內(nèi)容都是我干的呜达。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼键科,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼闻丑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起勋颖,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嗦嗡,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后饭玲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侥祭,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矮冬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谈宛。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胎署,靈堂內(nèi)的尸體忽然破棺而出吆录,到底是詐尸還是另有隱情,我是刑警寧澤琼牧,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布恢筝,位于F島的核電站,受9級(jí)特大地震影響巨坊,放射性物質(zhì)發(fā)生泄漏撬槽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一趾撵、第九天 我趴在偏房一處隱蔽的房頂上張望侄柔。 院中可真熱鬧,春花似錦占调、人聲如沸暂题。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)敢靡。三九已至,卻和暖如春苦银,著一層夾襖步出監(jiān)牢的瞬間啸胧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工幔虏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纺念,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓想括,卻偏偏與公主長(zhǎng)得像陷谱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瑟蜈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容