前面写LayoutInflater时看到了LayoutInflater.Factory和LayoutInflater.Factory2接口
在LayoutInflater.createViewFromTag()方法中
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 && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
调用LayoutInflater.createView()创建View之前会判断是否有设置Factory/Factory2。若设置,通过Factory/Factory2创建View。当然最终流程还是走的createView()方法反射创建View,只不过在创建之前做了hook处理,下面看看源码流程。
LayoutInflater
private Factory mFactory;
private Factory2 mFactory2;
private Factory2 mPrivateFactory;
public void setFactory(Factory 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 = factory;
} else {
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}
}
public void setFactory2(Factory2 factory) {
//Factory/Factory2只能设置一次,最终都返回的包装类FactoryMerger
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;
//设置Factory2也会赋值给Factory
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}
/**
* @hide for use by framework
* 官方注释mPrivateFactory为framework使用
*/
public void setPrivateFactory(Factory2 factory) {
if (mPrivateFactory == null) {
mPrivateFactory = factory;
} else {
mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
}
}
public interface Factory {
@Nullable
View onCreateView(@NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs);
}
public interface Factory2 extends Factory {
@Nullable
View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs);
}
private static class FactoryMerger implements Factory2 {
private final Factory mF1, mF2;
private final Factory2 mF12, mF22;
FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
mF1 = f1;
mF2 = f2;
mF12 = f12;
mF22 = f22;
}
//Factory的抽象方法优先调用Factory.onCreateView()
@Nullable
public View onCreateView(@NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
View v = mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF2.onCreateView(name, context, attrs);
}
//Factory2的抽象方法优先调用Factory2.onCreateView()
@Nullable
public View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs) {
View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
: mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
: mF2.onCreateView(name, context, attrs);
}
}
其中mPrivateFactory标记为hide,use by framework。我们可自行设置Factory/Factory2。
下面跟一下setFactory()/setFactory2()方法调用处
LayoutInflaterCompat
@Deprecated
public static void setFactory(
@NonNull LayoutInflater inflater, @NonNull LayoutInflaterFactory factory) {
if (Build.VERSION.SDK_INT >= 21) {
inflater.setFactory2(factory != null ? new Factory2Wrapper(factory) : null);
} else {
final LayoutInflater.Factory2 factory2 = factory != null
? new Factory2Wrapper(factory) : null;
inflater.setFactory2(factory2);
final LayoutInflater.Factory f = inflater.getFactory();
if (f instanceof LayoutInflater.Factory2) {
forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
} else {
forceSetFactory2(inflater, factory2);
}
}
}
public static void setFactory2(
@NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
inflater.setFactory2(factory);
if (Build.VERSION.SDK_INT < 21) {
final LayoutInflater.Factory f = inflater.getFactory();
if (f instanceof LayoutInflater.Factory2) {
forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
} else {
forceSetFactory2(inflater, factory);
}
}
}
笔者sdk版本为29,setFactory()方法已被标记为弃用
继续跟进LayoutInflaterCompat.setFactory2()方法调用处
AppCompatDelegateImpl.installViewFactory()
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
LayoutInflaterCompat.setFactory2(layoutInflater, this)这里的this即AppCompatDelegateImpl实现了LayoutInflater.Factory2接口
AppCompatDelegateImpl是AppCompatActivity的代理类
AppCompatActivity.onCreate()
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
//这里调用
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
也就是说Activity继承自AppCompatActivity后便为LayoutInflater设置了Factory2接口,接口实例为AppCompatDelegateImpl,最终hook操作就在AppCompatDelegateImpl的接口方法中了。
AppCompatDelegateImpl.onCreateView()
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return createView(parent, name, context, attrs);
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return onCreateView(null, name, context, attrs);
}
AppCompatDelegateImpl.CreateView()
@Override
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
String viewInflaterClassName =
a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
if ((viewInflaterClassName == null)
|| AppCompatViewInflater.class.getName().equals(viewInflaterClassName)) {
mAppCompatViewInflater = new AppCompatViewInflater();
} else {
try {
Class<?> viewInflaterClass = Class.forName(viewInflaterClassName);
mAppCompatViewInflater =
(AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
.newInstance();
} catch (Throwable t) {
Log.i(TAG, "Failed to instantiate custom view inflater "
+ viewInflaterClassName + ". Falling back to default.", t);
mAppCompatViewInflater = new AppCompatViewInflater();
}
}
}
boolean inheritContext = false;
if (IS_PRE_LOLLIPOP) {
inheritContext = (attrs instanceof XmlPullParser)
? ((XmlPullParser) attrs).getDepth() > 1
: shouldInheritContext((ViewParent) parent);
}
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
true, /* Read read app:theme as a fallback at all times for legacy reasons */
VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
);
}
AppCompatViewInflater.createView()
final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
...
switch (name) {
case "TextView":
view = createTextView(context, attrs);
verifyNotNull(view, name);
break;
case "ImageView":
view = createImageView(context, attrs);
verifyNotNull(view, name);
break;
case "Button":
view = createButton(context, attrs);
verifyNotNull(view, name);
break;
case "EditText":
view = createEditText(context, attrs);
verifyNotNull(view, name);
break;
case "Spinner":
view = createSpinner(context, attrs);
verifyNotNull(view, name);
break;
case "ImageButton":
view = createImageButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckBox":
view = createCheckBox(context, attrs);
verifyNotNull(view, name);
break;
case "RadioButton":
view = createRadioButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckedTextView":
view = createCheckedTextView(context, attrs);
verifyNotNull(view, name);
break;
case "AutoCompleteTextView":
view = createAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "MultiAutoCompleteTextView":
view = createMultiAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "RatingBar":
view = createRatingBar(context, attrs);
verifyNotNull(view, name);
break;
case "SeekBar":
view = createSeekBar(context, attrs);
verifyNotNull(view, name);
break;
case "ToggleButton":
view = createToggleButton(context, attrs);
verifyNotNull(view, name);
break;
default:
view = createView(context, name, attrs);
}
if (view == null && originalContext != context) {
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
checkOnClickListener(view, attrs);
}
return view;
}
@NonNull
protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
return new AppCompatTextView(context, attrs);
}
@NonNull
protected AppCompatImageView createImageView(Context context, AttributeSet attrs) {
return new AppCompatImageView(context, attrs);
}
@NonNull
protected AppCompatButton createButton(Context context, AttributeSet attrs) {
return new AppCompatButton(context, attrs);
}
@NonNull
protected AppCompatEditText createEditText(Context context, AttributeSet attrs) {
return new AppCompatEditText(context, attrs);
}
...
AppCompatActivity设置的Factory2将系统封装的常用View变为同名的AppCompatView以此支持tint属性。
下面我们自己设置Factory2
class Factory2Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
layoutInflater.factory2 = object : LayoutInflater.Factory2 {
override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? {
//调用AppCompatDelegateImpl.CreateView()系统逻辑生成view
val view = delegate.createView(parent, name, context, attrs)
if (view != null && view is TextView) {
//这里打印出来是AppCompatTextView
Log.d("zjyx", view.javaClass.simpleName)
return AppCompatButton(context, attrs)
}
return view
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
return null
}
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_factory)
}
}
xml只设置了一个TextView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_factory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="text_view"
android:textColor="@android:color/holo_orange_light"
android:textSize="20sp" />
</RelativeLayout>
这里将TextView变为了AppCompatButton
借助Factory2接口,可以在BaseActivity中统一对所有View进行处理。包括改变字体,设置view背景色,统一替换等等。
网友评论