参考 http://www.2cto.com/kf/201501/366859.html
本项目是以插件化开发思想进行的,主要工作和代码如下
资源文件,这里以color资源为例
1、首先我们需要准备一个皮肤包,这个皮肤包里面不会包含任何Activity,里面只有资源文件,这里我为了简单,仅仅加入一个color.xml(其实就相当于Android系统中的framework_res.apk)
<!--?xml version="1.0" encoding="utf-8"?-->
<resources>
<color name="main_btn_color">#E61ABD</color>
<color name="main_background">#38F709</color>
<color name="second_btn_color">#000000</color>
<color name="second_background">#FFFFFF</color>
</resources>
2、将该资源打包成apk文件,放入sd卡中(实际项目你可以从我网络下载)
3、将需要换肤的Activity实现ISkinUpdate(这个可以自己随便定义名称)接口
public class MainActivity extends Activity implements ISkinUpdate,OnClickListener
{
private Button btn_main;
private View main_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
SkinApplication.getInstance().mActivitys.add(this);
btn_main=(Button)this.findViewById(R.id.btn_main);
btn_main.setOnClickListener(this);
main_view=this.findViewById(R.id.main_view);
}
@Override
protected void onResume() {
super.onResume();
if(SkinPackageManager.getInstance(this).mResources!=null)
{
updateTheme();
Log.d("yzy", "onResume-->updateTheme");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
//Toast.makeText(this, "change skin", 1000).show();
File dir=new File(Environment.getExternalStorageDirectory(),"plugins");
File skin=new File(dir,"SkinPlugin.apk");
if(skin.exists())
{
SkinPackageManager.getInstance(MainActivity.this).loadSkinAsync(skin.getAbsolutePath(), new loadSkinCallBack() {
@Override
public void startloadSkin()
{
Log.d("yzy", "startloadSkin");
}
@Override
public void loadSkinSuccess() {
Log.d("yzy", "loadSkinSuccess");
MainActivity.this.sendBroadcast(new Intent(SkinBroadCastReceiver.SKIN_ACTION));
}
@Override
public void loadSkinFail() {
Log.d("yzy", "loadSkinFail");
}
});
}
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void updateTheme()
{
// TODO Auto-generated method stub
if(btn_main!=null)
{
try {
Resources mResource=SkinPackageManager.getInstance(this).mResources;
Log.d("yzy", "start and mResource is null-->"+(mResource==null));
int id1=mResource.getIdentifier("main_btn_color", "color", "com.skin.plugin");
btn_main.setBackgroundColor(mResource.getColor(id1));
int id2=mResource.getIdentifier("main_background", "color","com.skin.plugin");
main_view.setBackgroundColor(mResource.getColor(id2));
//img_skin.setImageDrawable(mResource.getDrawable(mResource.getIdentifier("skin", "drawable","com.skin.plugin")));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
SkinApplication.getInstance().mActivitys.remove(this);
super.onDestroy();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.getId()==R.id.btn_main)
{
Intent intent=new Intent(this,SecondActivity.class);
this.startActivity(intent);
}
}
}
这段代码里面主要看onOptionsItemSelected,这个方法里面,通过资源apk路径,拿到该资源apk对应Resources对象。我们直接看看SkinPacakgeManager里面做了什么吧
/**
* 解析皮肤资源包
* com.skin.demo.SkinPackageManager
* @author yuanzeyao <br>
* create at 2015年1月3日 下午3:24:16
*/
public class SkinPackageManager
{
private static SkinPackageManager mInstance;
private Context mContext;
/**
* 当前资源包名
*/
public String mPackageName;
/**
* 皮肤资源
*/
public Resources mResources;
private SkinPackageManager(Context mContext)
{
this.mContext=mContext;
}
public static SkinPackageManager getInstance(Context mContext)
{
if(mInstance==null)
{
mInstance=new SkinPackageManager(mContext);
}
return mInstance;
}
/**
* 异步加载皮肤资源
* @param dexPath
* 需要加载的皮肤资源
* @param callback
* 回调接口
*/
public void loadSkinAsync(String dexPath,final loadSkinCallBack callback)
{
new AsyncTask<string,void,resources>()
{
protected void onPreExecute()
{
if(callback!=null)
{
callback.startloadSkin();
}
};
@Override
protected Resources doInBackground(String... params)
{
try {
if(params.length==1)
{
String dexPath_tmp=params[0];
PackageManager mPm=mContext.getPackageManager();
PackageInfo mInfo=mPm.getPackageArchiveInfo(dexPath_tmp,PackageManager.GET_ACTIVITIES);
mPackageName=mInfo.packageName;
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath_tmp);
Resources superRes = mContext.getResources();
Resources skinResource=new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
SkinConfig.getInstance(mContext).setSkinResourcePath(dexPath_tmp);
return skinResource;
}
return null;
} catch (Exception e) {
return null;
}
};
protected void onPostExecute(Resources result)
{
mResources=result;
if(callback!=null)
{
if(mResources!=null)
{
callback.loadSkinSuccess();
}else
{
callback.loadSkinFail();
}
}
};
}.execute(dexPath);
}
/**
* 加载资源的回调接口
* com.skin.demo.loadSkinCallBack
* @author yuanzeyao <br>
* create at 2015年1月4日 下午1:45:48
*/
public static interface loadSkinCallBack
{
public void startloadSkin();
public void loadSkinSuccess();
public void loadSkinFail();
}
}
调用loadSkinAsync后,如果成功,就会发送一个换肤广播,并将当前皮肤apk的路径保存到sp中,便于下次启动app是直接加载该皮肤资源。接受换肤广播是在SkinApplication中注册的,当接收到此广播后,随即调用所有已经启动,并且需要换肤的Activity的updateTheme方法,从而实现换肤。
public class SkinApplication extends Application
{
private static SkinApplication mInstance=null;
public ArrayList<iskinupdate> mActivitys=new ArrayList<iskinupdate>();
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
mInstance=this;
String skinPath=SkinConfig.getInstance(this).getSkinResourcePath();
if(!TextUtils.isEmpty(skinPath))
{
//如果已经换皮肤,那么第二次进来时,需要加载该皮肤
SkinPackageManager.getInstance(this).loadSkinAsync(skinPath, null);
}
SkinBroadCastReceiver.registerBroadCastReceiver(this);
}
public static SkinApplication getInstance()
{
return mInstance;
}
@Override
public void onTerminate() {
// TODO Auto-generated method stub
SkinBroadCastReceiver.unregisterBroadCastReceiver(this);
super.onTerminate();
}
public void changeSkin()
{
for(ISkinUpdate skin:mActivitys)
{
skin.updateTheme();
}
}
}