android换皮肤思路总结

转自:http://blog.csdn.net/tangnengwu/article/details/22801107

前段时间公司有做换皮肤的项目,经过网上搜罗,查看资料,我个人总结三种换皮肤的方法。

    网上说的最多的就是使用android:sharedUserId标签来共享资源,但是经我测试无论用不用这个标签资源都可以访问,而且Launcher换皮肤的时候不能用这个标签来共享进程。

第一种方法先上代码:

MainActivity.java

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.app;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.util.HashMap;  
  5. import java.util.Map;  
  6. import android.os.Bundle;  
  7. import android.app.Activity;  
  8. import android.content.Context;  
  9. import android.content.pm.PackageManager.NameNotFoundException;  
  10. import android.util.Log;  
  11. import android.view.LayoutInflater;  
  12. import android.view.View;  
  13. import android.view.View.OnClickListener;  
  14. import android.widget.Button;  
  15. public class MainActivity extends Activity {  
  16.     private Context skinContext;  
  17.     private Map<String,Map<String, Object>> resMap;  
  18.     private Button change;  
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState)   
  21.     {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.           
  25.         try {  
  26.             skinContext = this.createPackageContext("com.skin",   
  27.                     CONTEXT_IGNORE_SECURITY|CONTEXT_INCLUDE_CODE);  
  28.         } catch (NameNotFoundException e) {  
  29.             // TODO Auto-generated catch block  
  30.             skinContext=null;  
  31.             e.printStackTrace();  
  32.         }  
  33.           
  34.         change = (Button) findViewById(R.id.button1);  
  35.         change.setOnClickListener(new OnClickListener() {  
  36.               
  37.             @Override  
  38.             public void onClick(View v) {  
  39.                 // TODO Auto-generated method stub  
  40.                 loadSkinRes();  
  41.                 View view = getLayoutFromSkin("skin_main");  
  42.                 if(view !=null)  
  43.                     setContentView(view);  
  44.             }  
  45.         });  
  46.           
  47.     }  
  48.     private void loadSkinRes()  
  49.     {  
  50.         if(skinContext != null)  
  51.         {  
  52.             resMap = getSkinResourcesId("com.skin");  
  53.         }  
  54.         else  
  55.         {  
  56.             resMap=null;  
  57.         }  
  58.     }  
  59.     /** 
  60.      * 获取皮肤包中的layout 
  61.      * 并转化为VIEW 
  62.      * @param layoutName 
  63.      * @return 
  64.      */  
  65.     private View getLayoutFromSkin(String layoutName)  
  66.     {  
  67.         View view;  
  68.         if(resMap == null)  
  69.             return null;  
  70.         Map<String, Object> temp = resMap.get("layout");  
  71.         int viewId = (Integer) temp.get(layoutName);  
  72.         if(viewId != 0)  
  73.         {  
  74.             //引用皮肤包资源转化万恶哦View  
  75.             LayoutInflater inflater =LayoutInflater.from(skinContext);  
  76.             view = inflater.inflate(skinContext.getResources().getLayout(viewId), null);  
  77.         }  
  78.         else  
  79.         {  
  80.             view = null;  
  81.         }  
  82.         return view;  
  83.     }  
  84.     /** 
  85.      * 取得对应包的所有资源的ID 
  86.      * 存在MAP中 
  87.      * @param packageName 
  88.      * @return 
  89.      */  
  90.     private Map<String,Map<String, Object>> getSkinResourcesId(String packageName)  
  91.     {  
  92.         Map<String, Object> temp =  null;  
  93.         Map<String,Map<String, Object>> resMap =new HashMap<String,Map<String,Object>>();  
  94.         try {  
  95.                 //取得皮肤包中的R文件  
  96.                 Class<?> rClass = skinContext.getClassLoader().loadClass(packageName+".R");  
  97.                 //取得记录各种资源的ID的类  
  98.                 Class<?>[] resClass =rClass.getClasses();  
  99.                 String className,resourceName;  
  100.                 int resourceId=0;  
  101.                 for(int i=0;i<resClass.length;i++)  
  102.                 {  
  103.                     className = resClass[i].getName();  
  104.                     //取得该类的资源  
  105.                     Field field[] = resClass[i].getFields();  
  106.                     for(int j =0;j < field.length; j++)  
  107.                     {  
  108.                         resourceName = field[j].getName();  
  109.                         try {  
  110.                             resourceId = field[j].getInt(resourceName);  
  111.                         } catch (IllegalArgumentException e) {  
  112.                             // TODO Auto-generated catch block  
  113.                             e.printStackTrace();  
  114.                         } catch (IllegalAccessException e) {  
  115.                             // TODO Auto-generated catch block  
  116.                             e.printStackTrace();  
  117.                         }  
  118.                         if(resourceName!=null && !resourceName.equals(""))  
  119.                         {  
  120.                             temp =new HashMap<String, Object>();  
  121.                             temp.put(resourceName, resourceId);  
  122.                             Log.i("DDDDD""className:"+className+"  resourceName:"+resourceName+"  " +  
  123.                                     "resourceId:"+Integer.toHexString(resourceId));  
  124.                         }  
  125.                     }  
  126.                     //由于内部类的关系className应该是com.skin.R$layout的形式  
  127.                     //截掉前面的包名和.R$以方便使用  
  128.                     className = className.substring(packageName.length()+3);  
  129.                     resMap.put(className, temp);  
  130.                 }  
  131.             } catch (ClassNotFoundException e) {  
  132.                 // TODO Auto-generated catch block  
  133.                 e.printStackTrace();  
  134.             }  
  135.         return resMap;  
  136.         }  
  137.       
  138.   
  139. }  

上面代码中只写了访问layout的方法,其他的资源也可以用类似的方法访问到。

activity_main.xml


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:paddingBottom="@dimen/activity_vertical_margin"  
  6.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  7.     android:paddingRight="@dimen/activity_horizontal_margin"  
  8.     android:paddingTop="@dimen/activity_vertical_margin"  
  9.     tools:context=".MainActivity" >  
  10.   
  11.     <TextView  
  12.         android:id="@+id/textView1"  
  13.         android:layout_width="wrap_content"  
  14.         android:layout_height="wrap_content"  
  15.         android:text="@string/thisres" />  
  16.   
  17.     <Button  
  18.         android:id="@+id/button1"  
  19.         style="?android:attr/buttonStyleSmall"  
  20.         android:layout_width="wrap_content"  
  21.         android:layout_height="wrap_content"  
  22.         android:layout_alignRight="@+id/textView1"  
  23.         android:layout_below="@+id/textView1"  
  24.         android:layout_marginTop="44dp"  
  25.         android:text="换皮肤" />  
  26.   
  27. </RelativeLayout>  

以下为皮肤包中的布局skin_main.xml:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:paddingBottom="@dimen/activity_vertical_margin"  
  6.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  7.     android:paddingRight="@dimen/activity_horizontal_margin"  
  8.     android:paddingTop="@dimen/activity_vertical_margin"  
  9.     tools:context=".MainActivity" >  
  10.   
  11.     <TextView  
  12.         android:id="@+id/textView1"  
  13.         android:layout_width="wrap_content"  
  14.         android:layout_height="wrap_content"  
  15.         android:text="@string/hello_world" />  
  16.   
  17.     <ImageView  
  18.         android:id="@+id/imageView1"  
  19.         android:layout_width="wrap_content"  
  20.         android:layout_height="wrap_content"  
  21.         android:layout_alignLeft="@+id/textView1"  
  22.         android:layout_below="@+id/textView1"  
  23.         android:layout_marginTop="32dp"  
  24.         android:src="@drawable/ic_launcher" />  
  25.   
  26. </RelativeLayout>  

在不用android:sharedUserId标签的时候也可以换成皮肤包的布局。

程序运行后:

点击“换皮肤”后


其实这种反射机制的换皮肤思路android提供能更强大的api,这也是我说的第二种方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      *  换皮肤 获取各种资源 
  3.      * @param skinPackageContext 
  4.      * @param resourceType 
  5.      * @param resourceName 
  6.      * @param packgeName 
  7.      * @return 
  8.      */  
  9.     private int getResourcesFromSkin(Context skinPackageContext,String resourceType,  
  10.             String resourceName,String packgeName)  
  11.     {  
  12.         int id=0;  
  13.         try  
  14.         {  
  15.             id=skinPackageContext.getResources().getIdentifier(resourceName,  
  16.                     resourceType, packgeName);  
  17.         }  
  18.         catch(Exception e)  
  19.         {  
  20.             id=0;  
  21.         }  
  22.         return id;  
  23.     }  
  24.     /** 
  25.      *  取得Raw中的xml 
  26.      * @param skinPackageContext 
  27.      * @param resourceName 
  28.      * @return 
  29.      */  
  30.     public InputStream getXmlInRawFromSkin(Context skinPackageContext,String resourceName)  
  31.     {  
  32.           
  33.         int id=getResourcesFromSkin(skinPackageContext,"raw",  
  34.                 resourceName,"com.flyaudio.skin");  
  35.         if(id==0)  
  36.         {  
  37.             id=getgetResourcesFromSkin(this,"raw",resourceName,"com.android.launcher");  
  38.             return this.getResources().openRawResource(id);  
  39.         }  
  40.         return skinPackageContext.getResources().openRawResource(id);  
  41.     }  
  42.     /** 
  43.      * 取得bool值 
  44.      * @param skinPackageContext 
  45.      * @param resourceName 
  46.      * @return 
  47.      */  
  48.     public boolean getBoolFromSkin(Context skinPackageContext,String resourceName)  
  49.     {  
  50.         int id=getResourcesFromSkin(skinPackageContext,"bool",  
  51.                 resourceName,"com.flyaudio.skin");  
  52.         if(id==0)  
  53.         {  
  54.             id=getgetResourcesFromSkin(this,"bool",resourceName,"com.android.launcher");  
  55.             if(id!=0)  
  56.                 return this.getResources().getBoolean(id);  
  57.         }  
  58.         else  
  59.             return skinPackageContext.getResources().getBoolean(id);  
  60.         return false;  
  61.     }  
  62.     /** 
  63.      *  取得皮肤包中的字符串 
  64.      * @param skinPackageContext 
  65.      * @param resourceName 
  66.      * @return 
  67.      */  
  68.     public String getStringFromSkin(Context skinPackageContext,String resourceName)  
  69.     {  
  70.         int id=getResourcesFromSkin(skinPackageContext,"string",  
  71.                 resourceName,"com.flyaudio.skin");  
  72.         if(id==0)  
  73.         {  
  74.             id=getResourcesFromSkin(this,"string",resourceName,"com.android.launcher");  
  75.             if(id!=0)  
  76.                 return this.getResources().getString(id);  
  77.         }  
  78.         else  
  79.             return skinPackageContext.getResources().getString(id);  
  80.         return null;  
  81.     }  
  82.     /** 
  83.      *  取得皮肤包中整型参数 
  84.      * @param skinPackageContext 
  85.      * @param resourceName 
  86.      * @return 
  87.      */  
  88.     public Integer getIntegerFromSkin(Context skinPackageContext,String resourceName)  
  89.     {  
  90.         int id=getResourcesFromSkin(skinPackageContext,"integer",  
  91.                 resourceName,"com.flyaudio.skin");  
  92.         if(id==0)  
  93.         {  
  94.             id=getgetResourcesFromSkin(this,"integer",resourceName,"com.android.launcher");  
  95.             return this.getResources().getInteger(id);  
  96.         }  
  97.         return skinPackageContext.getResources().getInteger(id);  
  98.     }  
  99.     /** 
  100.      *  取得皮肤包中Drawable资源 
  101.      * @param skinPackageContext 
  102.      * @param resourceName 
  103.      * @return 
  104.      */  
  105.     public Drawable getDrawableFromSkin(Context skinPackageContext,String resourceName)  
  106.     {  
  107.         int id =getResourcesFromSkin(skinPackageContext,"drawable",  
  108.                 resourceName,"com.flyaudio.skin");  
  109.         if(id==0)  
  110.         {  
  111.             id=getResourcesFromSkin(this,"drawable",resourceName,"com.android.launcher");  
  112.             return this.getResources().getDrawable(id);  
  113.         }  
  114.         return skinPackageContext.getResources().getDrawable(id);  
  115.     }  
  116.     /** 
  117.      *  取得皮肤包中color资源 
  118.      * @param skinPackageContext 
  119.      * @param resourceName 
  120.      * @return 
  121.      */  
  122.     public int getColorFromSkin(Context skinPackageContext,String resourceName)  
  123.     {  
  124.         int id =getResourcesFromSkin(skinPackageContext,"color",  
  125.                 resourceName,"com.flyaudio.skin");  
  126.         if(id==0)  
  127.         {  
  128.             id=getResourcesFromSkin(this,"color",resourceName,"com.android.launcher");  
  129.             return this.getResources().getColor(id);  
  130.         }  
  131.         return skinPackageContext.getResources().getColor(id);  
  132.     }  
  133.       
  134.     /** 
  135.      *  取得皮肤包中layout资源 
  136.      * @param skinPackageContext 
  137.      * @param resourceName 
  138.      * @return 
  139.      */  
  140.     public View getLayoutFromSkin(Context skinPackageContext,String resourceName)  
  141.     {  
  142.         int viewId=getResourcesFromSkin(skinPackageContext,"layout",  
  143.                 resourceName,"com.flyaudio.skin");  
  144.         LayoutInflater inflater=null;  
  145.         View view=null;  
  146.         if(viewId !=0)  
  147.         {  
  148.             inflater = LayoutInflater.from(skinPackageContext);  
  149.             view =inflater.inflate(skinPackageContext.getResources().getLayout(viewId), null);  
  150.         }  
  151.         else  
  152.         {  
  153.             viewId=getgetResourcesFromSkin(this,"layout",resourceName,"com.android.launcher");  
  154.             inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  155.             view =inflater.inflate(getResources().getLayout(viewId), null);  
  156.         }  
  157.         return view;  
  158.     }  
  159.     /** 
  160.      * 取得皮肤包中ID 
  161.      * @param skinPackageContext 
  162.      * @param resourceName 
  163.      * @return 
  164.      */  
  165.     public int getIdFromSkin(Context skinPackageContext,String resourceName,String type)  
  166.     {  
  167.         int id=0;  
  168.         id=getResourcesFromSkin(skinPackageContext,type,  
  169.                 resourceName,"com.flyaudio.skin");  
  170.         if(id==0)  
  171.         {  
  172.             id=getResourcesFromSkin(this,type,resourceName,"com.android.launcher");  
  173.         }  
  174.         return id;  
  175.     }  

以上代码是我在做Launcher换皮肤的时候使用的一些方法,android提供的这个api其实在Launcher程序的壁纸部分也有用到。

上面2中方法要求资源名字是一样的,资源数量可以不一样,第一种方法因为我们已经把整个资源包的资源ID都记录到Map里面了,而第二种的话,我没去详细了解getIdentifier()的处理机制,有知道的同学,可以留言讨论。

第三种方法就是修改frameworks,一个程序运行用到的一般都会有2套资源,一是android 自带的,也就是访问的时候我们用的android:开头的资源,二当然是我们自己的程序的资源。既然我们可以访问android的资源,我们当然也可以访问除了android资源和程序本身的资源以外的资源。

在 frameworks/base/core/java/android/app/ActivityThread.java中有对资源访问的描述。

其实getResources方法返回的结果就是在这里定义的,mResources的赋值代码为:

mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
                    Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);

我们可以在这里修改取得资源的优先级,让程序优先读取皮肤包的资源,这里就不多做解释。

详细请见:http://blog.csdn.net/luoshengyang/article/details/8791064


总结:

 以上我所知的三种方法均测试成功过,但不保证绝对正确。也欢迎各位同学指正,转载请注明出处。

 另附上方法一的程序:

传送门:http://download.csdn.net/detail/tangnengwu/7136273

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值