亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在Android 應用中實現一個換膚功能

發布時間:2020-11-27 16:35:12 來源:億速云 閱讀:309 作者:Leah 欄目:移動開發

今天就跟大家聊聊有關怎么在Android 應用中實現一個換膚功能,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

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

這個…我們看看用法吧….

ViewGroupSetter listViewSetter = new ViewGroupSetter(mNewsListView);
// 綁定ListView的Item View中的news_title視圖,在換膚時修改它的text_color屬性
listViewSetter.childViewTextColor(R.id.news_title, R.attr.text_color);

// 構建Colorful對象來綁定View與屬性的對象關系
mColorful = new Colorful.Builder(this)
  .backgroundDrawable(R.id.root_view, R.attr.root_view_bg)
  // 設置view的背景圖片
  .backgroundColor(R.id.change_btn, R.attr.btn_bg)
  // 設置背景色
  .textColor(R.id.textview, R.attr.text_color)
  .setter(listViewSetter) // 手動設置setter
  .create(); // 設置文本顏色

動態資源加載方案

resource替換

覆蓋application的getResource方法,實現自己的resource,優先加載本地皮膚包文件夾下的資源包,對于性能問題,可以通過attribute或者資源名稱規范(如需要換膚則用skin_開頭)來優化,從而不對不換膚的資源進行額外檢查開銷。

不過由于Android5.1源碼里,drawable初始化的時候調用的是loadDrawable,而不是resource.getDrawable,而loadDrawable是私有的方法,無法覆蓋,所以雖然很方便,卻無法繼續使用(不用關心任何皮膚相關的事情,android:color指定顏色就行了,神奇滴會自動換膚)。

自定義LayoutInflator.Factory

開源項目可參照Android-Skin-Loader。

即setFactory使用自定義的LayoutInflator.Factory,可以重點關注該項目中的SkinInflaterFactory和SkinManager(實現了自己的getColor、getDrawable、getBitmap、getColorStateList等等方法)。

需要自定義一個tag比如app:customStyle,重寫所有的style,轉成set方法,這樣帶來的犧牲就是增加了換膚的成本,要寫很多style,自己去set,并不完全透明了。

Hack Resources internally

黑科技方法,直接對Resources進行hack,Resources.Java:

// Information about preloaded resources. Note that they are not
// protected by a lock, because while preloading in zygote we are all
// single-threaded, and after that these are immutable.
private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
  = new LongSparseArray<Drawable.ConstantState>();
private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists
  = new LongSparseArray<ColorStateList>();

直接對Resources里面的這三個LongSparseArray進行替換,由于apk運行時的資源都是從這三個數組里面加載的,所以只要采用interceptor模式:

public class DrawablePreloadInterceptor extends LongSparseArray<Drawable.ConstantState>

自己實現一個LongSparseArray,并通過反射set回去,就能實現換膚,具體getDrawable等方法里是怎么取preload數組的,可以自己看Resources的源碼。

等等,就這么簡單?,NONO,少年你太天真了,怎么去加載xml,9patch的padding怎么更新,怎么打包/加載自定義的皮膚包,drawable的狀態怎么刷新,等等。這些都是你需要考慮的,在存在插件的app中,還需要考慮是否會互相覆蓋resource id的問題,進而需要修改apt,把resource id按位放在2個range。

總結

盡管動態加載方案比較黑科技,可能因為系統API的更改而出問題,但相對來說

好處有

- 靈活性高,后臺可以隨時更新皮膚包
- 相對透明,開發者幾乎不用關心有幾套皮膚,不用去定義各種theme和attr,甚至連皮膚包的打包都可以交給設計或者專門的同學
- apk體積節省

存在的問題

沒有完善的開源項目,如果我們采用動態加載的第二種方案,需要的項目功能包括:

- 自定義皮膚包結構
- 換膚引擎,加載皮膚包資源并load,實時刷新。
- 皮膚包打包工具
- 對各種rom的兼容

如果有這么一個項目的話,就一勞永逸了,有興趣的同學可以聯系一下,大家一起搞一搞。

內部加載方案大同小異,主要解決的都是即時刷新的問題,然而從目前的一些開源項目來看,仍然沒有特別簡便的方案。讓我選的話,我寧愿讓界面重新創建,比如重啟activity,或者remove所有view再添加回來(或者你可能想遍歷rootview,然后一個個檢查是否需要換膚然后set…)。

看完上述內容,你們對怎么在Android 應用中實現一個換膚功能有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

英德市| 伊春市| 新竹县| 库车县| 罗田县| 兴宁市| 郯城县| 揭西县| 庆云县| 稷山县| 香港| 额敏县| 门源| 乌鲁木齐市| 横峰县| 明星| 菏泽市| 策勒县| 怀化市| 手游| 徐汇区| 儋州市| 航空| 曲阳县| 锡林浩特市| 绥宁县| 石阡县| 松潘县| 陆良县| 红原县| 德昌县| 会同县| 新沂市| 长春市| 油尖旺区| 阿拉尔市| 册亨县| 绩溪县| 客服| 南康市| 崇礼县|