知识点:
- 1.在Activity的OnCreate方法中会调用AppCompatDelegateImpl类的installViewFactory方法
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(this.mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
}
}
在AppCompatDelegateImpl类中实现了Factory2的接口,且其中只有一个方法
public interface Factory2 extends Factory {
public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}
Factory2 实现方法上主要是创建了AppCompatViewInflater类和调用了mAppCompatViewInflater.createView方法
public View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) {
if (this.mAppCompatViewInflater == null) {
this.mAppCompatViewInflater = new AppCompatViewInflater();
}
return this.mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext, IS_PRE_LOLLIPOP, true, VectorEnabledTintResources.shouldBeUsed());
mAppCompatViewInflater.createView中则是根据布局的标签创建出相应的基础控件
final View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
Context originalContext = context;
View view = null;
/**省略代码**/
switch(var12) {
case 0:
view = this.createTextView(context, attrs);
break;
case 1:
view = this.createImageView(context, attrs);
break;
case 2:
view = this.createButton(context, attrs);
break;
case 3:
view = this.createEditText(context, attrs);
break;
case 4:
view = this.createSpinner(context, attrs);
break;
case 5:
view = this.createImageButton(context, attrs);
break;
case 6:
view = this.createCheckBox(context, attrs);
break;
case 7:
view = this.createRadioButton(context, attrs);
break;
case 8:
view = this.createCheckedTextView(context, attrs);
break;
case 9:
view = this.createAutoCompleteTextView(context, attrs);
break;
case 10:
view = this.createMultiAutoCompleteTextView(context, attrs);
break;
case 11:
view = this.createRatingBar(context, attrs);
break;
case 12:
view = this.createSeekBar(context, attrs);
break;
default:
view = this.createView(context, name, attrs);
}
return (View)view;
}
- 2.Activity在setContentView时会使布局转换成XmlResourceParser
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
之后会调用createViewFromTag方法生成相关的view
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
这里优先是调用mFactory2.onCreateView生成控件,也就是知识点1设置的参数,其次是调用mFactory其实在设置mFactory2时默认也会给mFactory设值.注意若生成的View为空时则会调用自身的方法生成view。
换肤思路
由于Activity是实现了Factory的接口
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
我们可以在程序调用SetFactory2方法前也就是super.onCreate(savedInstanceState)代码前进行Factory的初始化,通过知识点1的代码可知,若**layoutInflater.getFactory()**不为空 则再创建,所以 添加以下代码
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
LayoutInflater layoutInflater = LayoutInflater.from(this);
LayoutInflaterCompat.setFactory2(layoutInflater, this);
super.onCreate(savedInstanceState);
}
然后实现OnCreateView方法
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (viewInflater == null) {
viewInflater = new CustomAppCompatViewInflater(context);
}
viewInflater.setName(name);
viewInflater.setAttrs(attrs);
return viewInflater.autoMatch();
}
其中CustomAppCompatViewInflater是自定义类,继承与AppCompatViewInflater类,在这个类中调用autoMatch方法根据控件类型创建自定义类
public View autoMatch() {
View view = null;
switch (name) {
case "LinearLayout":
view = new SkinnableLinearLayout(context, attrs);
break;
case "RelativeLayout":
// view = new SkinnableRelativeLayout(context, attrs);
break;
case "TextView":
view = new SkinnableTextView(context, attrs);
break;
case "Button":
// view = new SkinnableButton(context, attrs);
break;
case "ImageView":
view = new SkinnableImageView(context, attrs);
break;
}
return view;
}
其中自定义控件如下:
public class SkinnableTextView extends AppCompatTextView implements ViewsMatch {
AttrsBean attrsBean;//存储控件规定属性的字段和值
public SkinnableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
attrsBean = new AttrsBean();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SkinnableTextView, defStyleAttr, 0);
attrsBean.saveViewResource(typedArray, R.styleable.SkinnableTextView);
typedArray.recycle();
}
@Override
public void skinnableView() {
int key = R.styleable.SkinnableTextView[R.styleable.SkinnableTextView_android_background];
int backgroundResourceId = attrsBean.getViewResource(key);
if (backgroundResourceId > 0) {
if (SkinManager.getInstance().isDefaultSkin()) {
Drawable drawable = ContextCompat.getDrawable(getContext(), backgroundResourceId);
setBackground(drawable);
} else {
//调用SkinManager获取皮肤包的资源
final Object backgroundOrSrc = SkinManager.getInstance().getBackgroundOrSrc(backgroundResourceId);
if (backgroundOrSrc instanceof Integer) {
int color = (int) backgroundOrSrc;
setBackgroundColor(color);
} else {
setBackground((Drawable) backgroundOrSrc);
}
}
}
int textColorKey = R.styleable.SkinnableTextView[R.styleable.SkinnableTextView_android_textColor];
int textColorResourceId = attrsBean.getViewResource(textColorKey);
if (textColorResourceId > 0) {
if (SkinManager.getInstance().isDefaultSkin()) {
ColorStateList colorStateList = ContextCompat.getColorStateList(getContext(), textColorResourceId);
setTextColor(colorStateList);
} else {
int color = SkinManager.getInstance().getColor(textColorResourceId);
setTextColor(color);
}
}
}
}
皮肤包代码:
public void loaderSkinResources(String skinPath) {
try {
//创建资源管理器
AssetManager assetManager = AssetManager.class.newInstance();
Method declaredMethod = assetManager.getClass().getDeclaredMethod(ADD_ASSET_PATH, String.class);
//设置私有方法可访问
declaredMethod.setAccessible(true);
//执行addAssetPath方法 加载资源包到本地应用
declaredMethod.invoke(assetManager, skinPath);
//创建加载外部皮肤包资源
skinResources = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
//根据皮肤包文件获取包名
skinPackageName = application.getPackageManager().getPackageArchiveInfo(skinPath, PackageManager.GET_ACTIVITIES)
.packageName;
//无法获取皮肤包包名,加载内置资源
isDefaultSkin = TextUtils.isEmpty(skinPackageName);
} catch (Exception e) {
e.printStackTrace();
isDefaultSkin = true;
}
}
/**
* 通过id值获取资源的name和type
*/
private int getSkinResourceIds(int resourceId) {
if (isDefaultSkin) {
return resourceId;
}
String resourceEntryName = appResources.getResourceEntryName(resourceId);//"music_bg"
String resourceTypeName = appResources.getResourceTypeName(resourceId); //"drawable"
//动态获取皮肤包的指定资源
if (skinResources != null) {
int skinResourceId = skinResources.getIdentifier(resourceEntryName, resourceTypeName, skinPackageName);
// isDefaultSkin = skinResourceId == 0;
return skinResourceId == 0 ? resourceId : skinResourceId;
}
return resourceId;
}
public boolean isDefaultSkin() {
return isDefaultSkin;
}
public int getColor(int resourceId) {
int ids = getSkinResourceIds(resourceId);
return isDefaultSkin ? appResources.getColor(ids) : skinResources.getColor(ids);
}
public Drawable getDrawableOrMipMap(int resourceId) {
int ids = getSkinResourceIds(resourceId);
return isDefaultSkin ? appResources.getDrawable(ids) : skinResources.getDrawable(ids);
}