来源:http://www.2cto.com/kf/201501/366859.html
目前很多app都具有换肤功能,可以根据用户自己的喜好定制自己的界面,比如新浪微博,网易新闻等等。今天这里我就是要介绍一种机制实现app换肤。
我找了几款app换肤的应用,换肤基本都是更换了界面的Icon,背景图片,背景色等等,基本没有遇到更换布局的,其实布局也是可以更换的,但是觉得没有必要。所以这篇文章讲解的换肤也是指换icon,背景图片等资源。
通过网络搜索我发现网上上提供了大概这么集中换肤机制:
1、直接将皮肤包放入apk中,这种方案实现非常简单,但是不够灵活,而且还将apk搞大了。
2、将皮肤做成一个独立的apk文件,并和项目工程公用一个shareUsedId,并拥有相同的签名。这种方案较第一种方案就是灵活性比较大,缺点就是需要用户安装,新浪微博目前使用的就是这种方案。
我今天要介绍的这种方案和第二种比较类似,但是我的资源包是不要安装的,毕竟用户一般愿意装一些乱七八糟的应用。
在学习这篇文章之前最好学习我的前一篇文章《Android资源管理机制分析》,因为皮肤管理其实就是资源的管理。下面开始学习如何换肤吧
1、首先我们需要准备一个皮肤包,这个皮肤包里面不会包含任何Activity,里面只有资源文件,这里我为了简单,仅仅加入一个color.xml(其实就相当于Android系统中的framework_res.apk)
1
2
3
4
5
6
7
8
9
|
|
3、将需要换肤的Activity实现ISkinUpdate(这个可以自己随便定义名称)接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
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);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
/**
* 解析皮肤资源包
* com.skin.demo.SkinPackageManager
* @author yuanzeyao
* 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
{
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
* create at 2015年1月4日 下午1:45:48
*/
public
static
interface
loadSkinCallBack
{
public
void
startloadSkin();
public
void
loadSkinSuccess();
public
void
loadSkinFail();
}
}
|
接受换肤广播是在SkinApplication中注册的,当接收到此广播后,随即调用所有已经启动,并且需要换肤的Activity的updateTheme方法,从而实现换肤。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public
class
SkinApplication
extends
Application
{
private
static
SkinApplication mInstance=
null
;
public
ArrayList
new
ArrayList
@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();
}
}
}
|