TestTheme
這是一個實現app主題動態切換的例子程序,示例了兩種方式:
1、通過切換app的Theme屬性;
2、通過安裝主題apk包實現切換;
通過切換app的Theme屬性實現主題動態切換的核心邏輯:
在Style中創建兩套Theme(如appTheme和appThemeLight),在attr文件中創建需要的動態改變的屬性(如backgroundStyle),
然後在前面以創建的Theme(appTheme和appThemeLight)中使用自定義的屬性(backgroundStyle);
最後在代碼中通過context.setTheme(int resId)動態設置主題爲自己創建某個主題(appTheme或appThemeLight);
注意:context.setTheme(int resId)必須在Activity的setContentView()之前調用;
**XML佈局文件中的調用方法**:
style="?backgroundStyle"
**代碼中的調用方法**:
某些情況下,有些組件的屬性需要在代碼中進行動態設置,這個時候只依靠上面的做法無法完成主題的切換;,
此時需要在代碼中動態讀取當前的Theme對象,並通過該Theme對象與在attr中定義的屬性(如drawableStyle),
獲取當前Theme下自定義屬性(drawableStyle)所鏈接的ResourceId,並將該ResourceId設置給對應的組件;
TypedArray array = getActivity().getTheme()
.obtainStyledAttributes(new int[] {R.attr.drawableStyle});
int drawableResId = array.getResourceId(0, 0);
ivImage.setImageResource(drawableResId);
通過安裝主題apk包實現切換核心代碼邏輯是:
1、查找所有與規定的主題apk包名相同的apk:
PackageManager pm = getActivity().getPackageManager();
List<PackageInfo> listPackages = pm.getInstalledPackages(PackageManager.PERMISSION_GRANTED);
List<PackageInfo> listSkins = new ArrayList<PackageInfo>();
for (PackageInfo packageInfo : listPackages) {
if(packageInfo.packageName.startsWith(SKIN_PACKAGE_PREFIX)){
listSkins.add(packageInfo);
}
}
2、根據包名,獲得包名所對應的apk的resource資源文件
public void onThemeChanged(String newThemePackageName){
if(TextUtils.isEmpty(newThemePackageName)) return;
try {
Context con = this.getActivity().createPackageContext(newThemePackageName, CONTEXT_IGNORE_SECURITY);
Resources res = con.getResources();
changeStyle(res);
} catch (Exception e) {
e.printStackTrace();
}
}
public void changeStyle(Resources res){
mListView.setBackgroundColor(res.getColor(R.color.bg_listview));
}
由於該程序只是主程序,(主程序本身的resource可以理解爲默認主題),並沒有提供主題apk程序,特在此說明一下:
主題apk不需要實現任何功能,只需要對照主程序的resource完成主題apk的resource;
已color文件爲例,在主程序的color文件中有bg_listview這個顏色(且主程序代碼中以該顏色爲listView的主題背景),
那麼在主題apk的color文件中同樣指定一個名爲bg_listview的顏色,那麼在主程序將主題切換爲該主題apk時,會自動
讀取該主題apk下的這個bg_listview作爲listView的主題背景;
源碼地址:github
程序截圖: