android动态换肤详解

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;
	}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值