1、切换本地主题theme
例如:
attrs.xml文件
<resources>
<!-- 控制app背景色 format:颜色值、资源引用 -->
<attr name="custom_attr_app_bg" format="color|reference" />
<!-- 控制app标题栏背景色 format:颜色值、资源引用 -->
<attr name="custom_attr_app_title_layout_bg" format="color|reference" />
<!-- 用户头像显示占位Drawable format:颜色值、资源引用 -->
<attr name="custom_attr_user_photo_place_holder" format="color|reference" />
<!-- 用户昵称字体颜色 format:颜色值、资源引用 -->
<attr name="custom_attr_nickname_text_color" format="color|reference" />
<!-- 用户备注字体颜色 format:颜色值、资源引用 -->
<attr name="custom_attr_remark_text_color" format="color|reference" />
<!-- 用户头像显示的透明度 format:尺寸值、资源引用 -->
<attr name="custom_attr_user_photo_alpha" format="dimension|reference" />
</resources>
style.xml 文件
<resources>
<!-- 主题:白天 -->
<style name="theme_day" parent="Theme.AppCompat.Light.NoActionBar">
<item name="titlebar_bg">#1EABF0</item>
<item name="titlebar_text_color">#FFFFFF</item>
<item name="body_bg">#F3F3F3</item>
<item name="body_text_color">#333333</item>
</style>
<!-- 主题:夜晚 -->
<style name="theme_night" parent="Theme.AppCompat.Light.NoActionBar">
<item name="titlebar_bg">#000000</item>
<item name="titlebar_text_color">#FFFFFF</item>
<item name="body_bg">#888888</item>
<item name="body_text_color">#EAEAEA</item>
</style>
</resources>
activity_xxx.xml布局文件
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/custom_attr_app_title_layout_bg"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/custom_attr_nickname_text_color"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?custom_attr_nickname_text_color"/>此处省略attr是可以的
</LinearLayout>
xxxActivity文件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int theme=SharedPreUtils.getInstance().setCurrentTheme(Theme.Night);
if(theme == Theme.Night) setTheme(R.style.theme_night); //必须在setContentView之前设置
else setTheme(R.style.theme_day)
setContentView(R.layout.activity_main);
mTvToolBarTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//重建,会先销毁当前实例,然后创建新的实例,生命周期基本一致。
调用该方法会额外调用onSaveInstanceState和onRestoreInstanceState这两个方法
recreate();
SharedPreUtils.getInstance().setCurrentTheme(Theme.Night);
}
});
}
缺点:
1、如果有图片背景的,少还可以,多的话会增加apk的体积
2、没办法动态加载
2、动态加载资源实现切换主题
通过查看Android-Skin-Loader源码,可以了解到通过网络加载的资源就是一个apk文件,没有java代码,只有resource里面的资源不一样,资源名称必须相同
流程
1、通过资源路径和名字可以+反射得到resources对象
AssetManager:提供对应用程序原始资源文件的访问
public Resources getSkinResources(Context context){
/**
* 插件apk路径
*/
String apkPath = Environment.getExternalStorageDirectory()+"/skin.apk";
AssetManager assetManager = null;
try {
AssetManager assetManager = AssetManager.class.newInstance();
AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(assetManager, apkPath);
} catch (Throwable th) {
th.printStackTrace();
}
return new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.imageView);
TextView textView = (TextView) findViewById(R.id.text);
/**
* 插件资源对象
*/
Resources resources = getSkinResources(this);
/**
* 获取图片资源
*/
Drawable drawable = resources.getDrawable(resources.getIdentifier("night_icon", "drawable","com.tzx.skin"));
/**
* 获取Color资源
*/
int color = resources.getColor(resources.getIdentifier("night_color","color","com.tzx.skin"));
imageView.setImageDrawable(drawable);
textView.setText(text);
textView.setTextColor(color );
}
2、对于view的更改,更改后退出重新进来为保证主题还是退出时的,最好在View创建的时候就直接使用皮肤资源包中的资源文件。实现LayoutInflater.Factory重写onCreateView
在该方法里可以把view都放进对象集合中,方便以后遍历view更换主题
public class MyFactory implements LayoutInflater.Factory {
@Nullable
@Override
public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
return null;
}
}
view更换主题的流程(首先得到资源包中的mResources)
1、根据 view的属性名称(android:background="") 找到 资源id(@color/white的id)
2、再根据 资源id 找到 资源名称(@color/white)
3、再根据 资源名称 通过mResources.getIdentifier(resName, “color”, skinPackageName);得到资源包中的资源id
4、最后通过mResources.getColor(trueResId);就得到了资源包中的颜色
public int getColor(int resId){
int originColor = context.getResources().getColor(resId);
if(mResources == null || isDefaultSkin){
return originColor;
}
String resName = context.getResources().getResourceEntryName(resId);
int trueResId = mResources.getIdentifier(resName, "color", skinPackageName);
int trueColor = 0;
try{
trueColor = mResources.getColor(trueResId);
}catch(NotFoundException e){
e.printStackTrace();
trueColor = originColor;
}
return trueColor;
}