素材巴巴 > 程序开发 >

Android实现动态换肤-原理篇

程序开发 2023-09-07 16:53:05

学习是一个过程。

文章目录

Activity中LayoutInflater加载布局总体时序图

在这里插入图片描述

LayoutInflater源码讲解(api28)

LayoutInflater设置Factory2

实现方式

我们可以以apk的方式来打包资源,这个apk资源包中只存在资源文件,然后我们通过动态加载技术来加载apk资源包,实现动态换肤的功能。

public class SkinResourceManager {private static SkinResourceManager skinManager = new SkinResourceManager();private Context context;private Resources resources;//获取到资源包中的包名private String skinPackageName;private SkinResourceManager(){}public static SkinManager getInstance(){return skinManager;}public void init(Context context){this.context = context;}/*** 根据资源包的 存储路劲去加载资源* @param path*/public void loadSkinApk(String path){//真正的目的是得到资源包的资源对象try {PackageManager packageManager = context.getPackageManager();PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES);skinPackageName = packageArchiveInfo.packageName;AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class);addAssetPath.setAccessible(true);//在assetManager中执行addAssetPath方法addAssetPath.invoke(assetManager,path);//创建一个资源对象  管理资源包里面的资源resources = new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());} catch (Exception e) {e.printStackTrace();}}//写匹配各种资源的方法/*** 去匹配颜色* @param resId 需要去匹配ID* @return  匹配到的资源ID(在资源包中的资源ID)*/public int getColor(int resId){if(resourceIsNull()){return resId;}//获取到资源名字和资源类型String resourceTypeName = context.getResources().getResourceTypeName(resId);String resourceEntryName = context.getResources().getResourceEntryName(resId);//去资源包的资源对象中去匹配int identifier = resources.getIdentifier(resourceEntryName,resourceTypeName, skinPackageName);if(identifier == 0){return resId;}return resources.getColor(identifier);}/*** 从资源包中拿到drawable的资源id*/public Drawable getDrawable(int id){if(resourceIsNull()){//获取到这个id在当前应用中的Drawable对象return ContextCompat.getDrawable(context,id);}//获取到资源id的类型String resourceTypeName = context.getResources().getResourceTypeName(id);//获取到的就是资源id的名字String resourceEntryName = context.getResources().getResourceEntryName(id);//就是colorAccent这个资源在外置APK中的idint identifier = resources.getIdentifier(resourceEntryName, resourceTypeName, skinPackageName);if(identifier == 0){return ContextCompat.getDrawable(context,id);}return resources.getDrawable(identifier);}/*** 从资源包中拿到drawable的资源id*/public int getDrawableId(int id){if(resourceIsNull()){return id;}//获取到资源id的类型String resourceTypeName = context.getResources().getResourceTypeName(id);//获取到的就是资源id的名字String resourceEntryName = context.getResources().getResourceEntryName(id);//就是colorAccent这个资源在外置APK中的idint identifier = resources.getIdentifier(resourceEntryName, resourceTypeName, skinPackageName);if(identifier == 0){return id;}return identifier;}public boolean resourceIsNull(){if(resources == null){return true;}return false;}public Resources getResources() {return resources;}
 }
 
public class SkinActivity extends AppCompatActivity {public SkinFactory skinFactory;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {skinFactory = new SkinFactory(getDelegate());getLayoutInflater().setFactory2(skinFactory);super.onCreate(savedInstanceState);}public void apply(){skinFactory.apply();}@Overrideprotected void onResume() {super.onResume();skinFactory.apply();}
 }

LayoutInflater源码总结

  1. Activity与AppCompatActivity都是调用LayoutInflater进行View的创建,但是AppCompatActivity的View是用Factory2进行创建的,我们可以用这种机制来实现替换动态换肤的实现。
  2. LayoutInflater的Factory2是带有重设校验的,它是不支持重复设置参数的,我们可以有两种方式来设置我们的Factory2。
  3. 在LayoutInflater里面有个1995闪烁layout,我们可以通过在标签中使用blink标签,来达到闪烁布局的效果。
  4. 创建View是通过反射进行创建的,为了加快构造方法的创建速度,将之前生成的构造方法进行缓存。

标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。