1、LayoutInflater源码解析
- LayoutInflater#inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
- createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr)
创建View方式:首先通过mFactory、mFactory等,如果其都为null,则通过name反射创建View。
public interface Factory {
/**
* 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.
*
* <p>
* Note that it is good practice to prefix these custom names with your
* package (i.e., com.coolcompany.apps) to avoid conflicts with system
* names.
*
* @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(String name, Context context, AttributeSet attrs);
}
public interface Factory2 extends Factory {
/**
* Version of {@link #onCreateView(String, Context, AttributeSet)}
* that also supplies the parent that the view created view will be
* placed in.
*
* @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);
}
复制代码
自定义LayoutInflater,即通过实现Factory2在自定义的tag填充过程中执行自己的一些操作。 而由于LayoutInflater是系统服务,且是单例,因此需要通过LayoutInflater#cloneInContext克隆出一个实例,再setFactory。
- rInflateChildren
/**
* Recursive method used to inflate internal (non-root) children. This
* method calls through to {@link #rInflate} using the parent context as
* the inflation context.
* <strong>Note:</strong> Default visibility so the BridgeInflater can
* call it.
*/
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
* <p>
* <strong>Note:</strong> Default visibility so the BridgeInflater can
* override it.
*/
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
复制代码
rInflate也是调用createViewFromTag创建子View,通过递归调用rInflate创建所有子View。
2、自定义LayoutInflater实现小红书引导页
来源博客:传送门public class MyLayoutInflater extends LayoutInflater {
private ParallaxViewImpl parallaxLayout;
protected MyLayoutInflater(Context context, ParallaxViewImpl parallaxLayout) {
super(context);
this.parallaxLayout = parallaxLayout;
setFactory2(new MyFactory(this,parallaxLayout));
}
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new MyLayoutInflater(newContext, parallaxLayout);
}
}
复制代码
public class MyFactory implements LayoutInflater.Factory2 {
private LayoutInflater layoutInflater;
private ParallaxViewImpl parallaxLayout;
private final String[] sClassPrefix = {
"android.widget.",
"android.view.",
"android.webkit."
};
public MyFactory(MyLayoutInflater myLayoutInflater, ParallaxViewImpl parallaxLayout) {
this.layoutInflater = myLayoutInflater;
this.parallaxLayout = parallaxLayout;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (name.contains(".")) {
return createViewWithPrefix(name, null, context, attrs);
} else {
for (String prefix : sClassPrefix) {
View view = createViewWithPrefix(name, prefix, context, attrs);
if (view != null) {
return view;
}
}
}
return null;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
private View createViewWithPrefix(String name, String prefix, Context context, AttributeSet attrs) {
try {
View view = layoutInflater.createView(name, prefix, attrs);
if (prefix == null) {
setViewTag(view, context, attrs);
parallaxLayout.getParallaxViews().add(view);
}
return view;
} catch (Exception e) {
return null;
}
}
private void setViewTag(View view, Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.animation);
float x_in = a.getFloat(R.styleable.animation_xin, 0f);
float x_out = a.getFloat(R.styleable.animation_xout, 0f);
float y_in = a.getFloat(R.styleable.animation_yin, 0f);
float y_out = a.getFloat(R.styleable.animation_yout, 0f);
TransBean bean = new TransBean(x_in, x_out, y_in, y_out);
view.setTag(view.getId(), bean);
a.recycle();
}
}
复制代码
public class MyFragment extends Fragment implements ParallaxViewImpl {
private List<View> parallaxViews = new ArrayList<>();
public static MyFragment newInstance(int i) {
Bundle bundle = new Bundle();
bundle.putInt("resId", i);
MyFragment fragment = new MyFragment();
fragment.setArguments(bundle);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
int resId = getArguments().getInt("resId");
MyLayoutInflater layoutInflater = new MyLayoutInflater(getActivity(),this);
return layoutInflater.inflate(resId, null);
}
@Override
public List<View> getParallaxViews() {
return parallaxViews;
}
}
复制代码