LayoutInflater.SetFactory()学习
一:基本概念
LayoutInflater.setFactory():
LayoutInflater提供了两个方法,分别是setFactory()和setFactory2(),其中setFactory2()是在sdk>11的时候引入的;
LayoutInflaterFactory:
仅有一个onCreateView()方法,用来创建View;
public interface LayoutInflaterFactory {
/**
* Hook you can supply that is called when inflating from a LayoutInflater.
* You can use this to customize the tag names available in your XML
* layout files.
*
* @param parent The parent that the created view will be placed
* in; <em>note that this may be null</em>.
* @param name Tag name to be inflated.
* @param context The context the view is being created in.
* @param attrs Inflation attributes as specified in XML file.
*
* @return View Newly created view. Return null for the default
* behavior.
*/
public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}
LayoutInflaterCompat:LayoutInflater的兼容类;
二:自定义LayoutInflaterFactory
用处:
我们可以在onCreateView中根据name属性来做一些逻辑操作,如把TextView替换成自定义View或者其他控件,这样做的好处可以提高效率,因为系统创建要有一些逻辑要走,最终通过反射来创建,发射会影响性能;
public class FactoryActivity extends AppCompatActivity {
private static final String TAG = "FactoryActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory() {
//重写onCreateView()
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
Log.i(TAG, "name = " + name);
int n = attrs.getAttributeCount();
for (int i = 0; i < n; i++) {
Log.e(TAG, attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));
}
return null;
}
});
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_factory);
}
}
会打印出信息如下:
The Activity's LayoutInflater already has a Factory installed so we can not install AppCompat's
The Activity's LayoutInflater already has a Factory installed so we can not install AppCompat's
原因如下:因为AppCompatActivity内部也调用了setFactory,所以我们再自己调用setFactory就会出现上面的信息;
影响: 如果我们在系统之前调用了setFactory,就会打印上面的info信息,并且造成其setFactroy不会生效,会造成没有办法使用一些新的特性,比如tint等
AppCompatActivity.onCreate()
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
AppCompatActivity-> AppCompatDelegate ->AppCompatDelegateImplV7
可以看出AppCompatDelegateImplV7实现了LayoutInflaterFactory;
**AppCompatDelegateImplV7.installViewFactory() **
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
//注意这里,如果设置了自定义的Factory,那么该方法不会被调用;从而AppCompatDelegateImplV7.onCreateView方法也不会被调用;
LayoutInflaterCompat.setFactory(layoutInflater, this);
} else {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
AppCompatDelegateImplV7.onCreateView()
@Override
public final View onCreateView(View parent, String name,
Context context, AttributeSet attrs) {
// First let the Activity's Factory try and inflate the view
//首先使用Activity的Factory来加载View;
final View view = callActivityOnCreateView(parent, name, context, attrs);
if (view != null) {
return view;
}
//如果Activity的Factory不能处理,那么就调用AppCompatDelegateImplV7.createView()
// If the Factory didn't handle it, let our createView() method try
return createView(parent, name, context, attrs);
}
解决方法: 在自己设置factory中,调用系统的创建view的代码;
LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory()
{
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs)
{
//你可以在这里直接new自定义View
//你可以在这里将系统类替换为自定义View
//appcompat 创建view代码
AppCompatDelegate delegate = getDelegate();
View view = delegate.createView(parent, name, context, attrs);
return view;
}
});
原因解释:AppCompatDelegate.createView会通过一系列方法调用链,最后走到如下的代码,这部分代码会把控件自动的替换成Appcompat的控件,从而支持一些新的特性;所以只要调用了AppCompatDelegate.createView()就能保留新特性
switch (name) {
case "TextView":
view = new AppCompatTextView(context, attrs);
break;
case "ImageView":
view = new AppCompatImageView(context, attrs);
break;
case "Button":
view = new AppCompatButton(context, attrs);
break;
case "EditText":
view = new AppCompatEditText(context, attrs);
break;
case "Spinner":
view = new AppCompatSpinner(context, attrs);
break;
case "ImageButton":
view = new AppCompatImageButton(context, attrs);
break;
case "CheckBox":
view = new AppCompatCheckBox(context, attrs);
break;
case "RadioButton":
view = new AppCompatRadioButton(context, attrs);
break;
case "CheckedTextView":
view = new AppCompatCheckedTextView(context, attrs);
break;
case "AutoCompleteTextView":
view = new AppCompatAutoCompleteTextView(context, attrs);
break;
case "MultiAutoCompleteTextView":
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
break;
case "RatingBar":
view = new AppCompatRatingBar(context, attrs);
break;
case "SeekBar":
view = new AppCompatSeekBar(context, attrs);
break;
}