android換膚功能 如何動態獲取控制元件中背景圖片的資源id?

NO IMAGE

這個是在在做一個換膚功能時遇到的問題。  

對於換膚,網上都有示例,可以從別的面板安裝包中讀取所要的資源,前提是你必須先持有這個資源的引用名稱,像R.drawable.background(喂,這不是廢話嘛)。這個換膚的方案原理就是,自身應用的資源名稱是R.drawable.background,那面板包中應該也是這個名稱,然後通過這個名稱獲取該資源在面板包中的具體id,程式碼:


//先獲取本地資源引用名稱,type name是R.drawable.background中的"drawable",entry name是"background"
String resTypeName = getContext().getResources().getResourceTypeName(id);
String resEntryName = getContext().getResources().getResourceEntryName(id);
//然後建立面板包所在應用的Context
Context apk = getContext().createPackageContext(packageName,
Context.CONTEXT_IGNORE_SECURITY)
//然後就是獲取面板包中的資源id了
int drawavleId = apk.getResources().getIdentifier(resEntryName, resTypeName,
apk.getPackageName());  

這個換膚方案中,每個Activity在切換面板時,需要遍歷整個layout,判斷控制元件如果id中包含“skin”的字元,意味這個控制元件是需要換膚的控制元件,這個控制元件的id可以先儲存下來。

遍歷檢視的程式碼 


private List<Integer> skinViewList = new ArrayList<Integer> ();  private void scanViewGroup(ViewGroup group, List<Integer> skinViewList, Resources res) {
//first we need check if this ViewGroup have a background
if(group.getId() != View.NO_ID
&& res.getResourceEntryName(group.getId()).contains(SKIN_PATTERN)
&& !skinViewList.contains(group)) {
skinViewList.add(group.getId());
}
//second check its child view
View child;
for(int i = 0; i < group.getChildCount(); i  ) {
child = group.getChildAt(i);
if(child instanceof ViewGroup) {
scanViewGroup((ViewGroup)child, skinViewList, res);
} else if(child.getId() == View.NO_ID) {
return;
} else {
int viewId = child.getId();
String entryName = res.getResourceEntryName(viewId);
Log("scanViewGroup(), entryName of this childView : "   entryName);
if(entryName.contains(SKIN_PATTERN) && !skinViewList.contains(child))
skinViewList.add(child.getId());
}
}
}

問題來了,本地應用中,你持有一個控制元件,比如Button,它的id可以直接呼叫button.getId()方法獲取,但是它的背景圖片background呢,我們可以呼叫button.getBackground()方法獲取其物件,但是卻沒有方法可以獲取這個資源圖片的引用名稱,也就無法得到它的具體id了。後面想到的方案就是,在每次Activity初始化的時候,我們事先遍歷每一個控制元件的屬性集AttributeSet,有需要換膚的控制元件,將其android:background這個屬性的值儲存下來,為此,需要過載Activity的onCreateView(String name, Context context, AttributeSet attrs)方法,這個方法我的理解是在Activity中每個控制元件(包括LinearLayout、TextView、Button等等)初始化前會呼叫,我也打了log,進行了驗證,其中attrs引數就是該控制元件的屬性集,這就是我們需要的,程式碼: 


//先判斷前面掃描的skinViewList是否為空,不為空意味著有控制元件需要換膚
if(skinViewList != null && skinViewList.size() > 0) {
int viewId = -1, backgroundId = -1;
for(int i = 0; i < attrs.getAttributeCount(); i  ) {
if(attrs.getAttributeName(i).equals("id")) {
viewId = attrs.getAttributeResourceValue(i, -1);
}
if(attrs.getAttributeName(i).equals("background")) {
backgroundId = attrs.getAttributeResourceValue(i, -1);
}
}
//check if background drawable need save
if(viewId != -1 && backgroundId != -1 &&
drawableIdList != null && !drawableIdList.containsKey(viewId)) {
drawableIdList.put(viewId, backgroundId);
Log("add to drawableIdList, viewId = "   viewId
", backgroundId = "   backgroundId);
}
}

有了這個backgroundId,就能獲取該資源的引用名稱R.drawable.background,然後我們就能通過名稱從其他包獲取對應的資原始檔了,從而可以執行換膚操作。而且,通過這個方法,不只可以獲取圖片資源的id,也能獲取字串如R.string.title,字型顏色如R.color.red,字型大小如R.dimens.text_size_small等等屬性,從而擴大換膚的範圍。