android-自定义换肤(1) https://blog.csdn.net/tiangaopan/article/details/104895134
前面说到本质上是通过实现LayoutInflater.Factory2,在onCreateView()方法中进行处理
这就需要我们找到对应的控件,控件又有系统控件和自定义控件,这里我们先说系统控件,自定义后面会说处理方法
通过看源码,我们可以找到系统控件在这三个包下,
private static final String[] mClassPrefixList = {"android.widget.", "android.view.", "android.webkit.",}
private static final Class[] mConstructorSignature = new Class[]{Context.class, AttributeSet.class};
private static final HashMap<String, Constructor<? extends View>> mConstructorMap = new HashMap<>();
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//反射获取
View view = createViewFromTag(name, context, attrs);
//自定义view
if (view == null) {
view = createView(name, context, attrs);
}
//筛选符合属性的attrs
mSkinAttribute.load(view, attrs);
return view;
}
private View createViewFromTag(String name, Context context, AttributeSet attrs) {
//包含自定义控件
if (-1 != name.indexOf(".")) {
return null;
}
View view = null;
for (String s : mClassPrefixList) {
//mClassPrefixList[i] + name 组成全类名
view = createView(s + name, context, attrs);
if (view != null) {
break;
}
}
return view;
}
private View createView(String name, Context context, AttributeSet attrs) {
Constructor<? extends View> constructor = mConstructorMap.get(name);
if (constructor == null) {
try {
//通过全类名拿到class
Class<? extends View> aClass = context.getClassLoader().loadClass(name).asSubclass(View.class);
//获取构造方法
constructor = aClass.getConstructor(mConstructorSignature);
mConstructorMap.put(name, constructor);
} catch (Exception e) {
e.printStackTrace();
}
}
if (constructor != null) {
try {
return constructor.newInstance(context, attrs);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
对于已经通过反射获取到构造方法的,可以使用map进行存储,没必要每次都进行一次获取
public class SkinAttribute {
private static final List<String> sAttribute = new ArrayList<>();
static {
sAttribute.add("background");
sAttribute.add("src");
sAttribute.add("textColor");
sAttribute.add("drawableLeft");
sAttribute.add("drawableTop");
sAttribute.add("drawableRight");
sAttribute.add("drawableBottom");
}
private List<SkinView> skinViews = new ArrayList<>();
private Typeface mSkinTypeface;
public SkinAttribute(Typeface skinTypeface) {
mSkinTypeface = skinTypeface;
}
public void load(View view, AttributeSet attrs) {
List<SkinPain> skinPains = new ArrayList<>();
int attributeCount = attrs.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
//获取属性名字
String attributeName = attrs.getAttributeName(i);
if (sAttribute.contains(attributeName)) {
//获取属性值
String attributeValue = attrs.getAttributeValue(i);
//写死的情况
if (attributeValue.startsWith("#")) {
continue;
}
int resId;
//系统自带的 attributeValue = "?1313123"
if (attributeValue.startsWith("?")) {
int attrId = Integer.parseInt(attributeValue.substring(1));
//存在数组的可能
resId = SkinThemeUtils.getResId(view.getContext(), new int[]{attrId})[0];
} else {
//attributeValue = "@21321213"
resId = Integer.parseInt(attributeValue.substring(1));
}
if (resId != 0) {
SkinPain skinPain = new SkinPain(attributeName, resId);
skinPains.add(skinPain);
}
}
}
if (!skinPains.isEmpty() || view instanceof TextView || view instanceof SkinViewSupport) {
SkinView skinView = new SkinView(view, skinPains);
skinView.applySkin(mSkinTypeface);
skinViews.add(skinView);
}
}
public void applySkin() {
for (SkinView skinView : skinViews) {
skinView.applySkin(mSkinTypeface);
}
}
public void setTypeface(Typeface skinTypeface) {
this.mSkinTypeface = skinTypeface;
}
static class SkinView {
View view;
List<SkinPain> skinPains;
public SkinView(View view, List<SkinPain> skinPains) {
this.view = view;
this.skinPains = skinPains;
}
public void applySkin(Typeface typeface) {
applyTypeface(typeface);
applySkinSupport();
for (SkinPain skinPair : skinPains) {
Drawable left = null, top = null, right = null, bottom = null;
switch (skinPair.attributeName) {
case "background":
Object background = SkinResources.getInstance().getBackground(skinPair.resId);
//Color
if (background instanceof Integer) {
view.setBackgroundColor((Integer) background);
} else {
ViewCompat.setBackground(view, (Drawable) background);
}
break;
case "src":
background = SkinResources.getInstance().getBackground(skinPair.resId);
if (background instanceof Integer) {
((ImageView) view).setImageDrawable(new ColorDrawable((Integer)
background));
} else {
((ImageView) view).setImageDrawable((Drawable) background);
}
break;
case "textColor":
((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList
(skinPair.resId));
break;
case "drawableLeft":
left = SkinResources.getInstance().getDrawable(skinPair.resId);
break;
case "drawableTop":
top = SkinResources.getInstance().getDrawable(skinPair.resId);
break;
case "drawableRight":
right = SkinResources.getInstance().getDrawable(skinPair.resId);
break;
case "drawableBottom":
bottom = SkinResources.getInstance().getDrawable(skinPair.resId);
break;
default:
break;
}
if (null != left || null != right || null != top || null != bottom) {
((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right,
bottom);
}
}
}
//自定义view替换
private void applySkinSupport() {
if (view instanceof SkinViewSupport) {
((SkinViewSupport) view).applySkin();
}
}
//字体换肤
private void applyTypeface(Typeface typeface) {
if (view instanceof TextView) {
((TextView) view).setTypeface(typeface);
}
}
}
static class SkinPain {
String attributeName;
int resId;
public SkinPain(String attributeName, int resId) {
this.attributeName = attributeName;
this.resId = resId;
}
}
}
通过attributeName来知道是background,textcolor等属性,通过resid可以知道是资源名,这时候去获取资源包中的对应resid,即可实现相应的换肤
还有个需要注意的是,每个界面都应该进行相应的换肤,所以我们需要去实现Application.ActivityLifecycleCallbacks,这里去进行加载和移除
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//更新状态栏
SkinThemeUtils.updateStatusBarColor(activity);
//更新字体
Typeface skinTypeface = SkinThemeUtils.getSkinTypeface(activity);
try {
LayoutInflater layoutInflater = LayoutInflater.from(activity);
Field mFactorySet = LayoutInflater.class.getDeclaredField("mFactorySet");
mFactorySet.setAccessible(true);
mFactorySet.setBoolean(layoutInflater, false);
SkinLayoutFactory skinLayoutFactory = new SkinLayoutFactory(activity, skinTypeface);
//添加自定义创建view 工厂
layoutInflater.setFactory2(skinLayoutFactory);
//注册观察者
SkinManager.getInstance().addObserver(skinLayoutFactory);
mFactoryMap.put(activity, skinLayoutFactory);
} catch (Exception e) {
e.printStackTrace();
}
}
在这里我们对对应的setFactory2进行替换,有个需要注意的是mFactorySet这个属性,如果是true的话会抛出异常,同时该变量是私有的,所以我们需要通过反射找到该变量将其更改为false
public void setFactory2(Factory2 factory) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}