推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

xml节点有哪些属性 从XML变成View,它经历了什么?

   2023-07-27 网络整理佚名2130
核心提示:类停留一下,发现这里似乎只注册各种系统服务的地方。读取xml文件并创建View对象再去源码查看一下,发现两个方法其实只有一个方法是核心,另一个只是做了一下封装,让我们少传入一个参数。方法,该方法比较长,这里只贴出核心步骤。上面这个方式我们需要重点记一下第一部分代码,我们的到的结论是,然后看下后面的代码我们就明白的创建,这里省略了大部分代码中的代码对象的转换过程全部结束~

。(。冰)

@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}

继续关注y

public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != ? fetcher.getService(ctx) : ;
}

这时候我们在y班呆了一会儿,发现里面好像只有注册各种系统服务的地方。

我们发现

.ICE注册码。

static {
……
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext);
}});
……
}

private static void registerService(String serviceName, Class serviceClass,
ServiceFetcher serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

然后我们终于发现实现类是

此时我们可以休息一下,喝口水,去趟洗手间,进入下一阶段~

读取xml文件并创建View对象

public View inflate(@LayoutRes int resource, @able ViewGroup root)
public View inflate(@LayoutRes int resource, @able ViewGroup root, boolean attachToRoot)

再次去源码查看,发现这两个方法只有一个是核心,另一个只是一点封装,这样我们就可以少传入一个参数。

public View inflate(@LayoutRes int resource, @able ViewGroup root) {
return inflate(resource, root, root != );

}

那么让我们重点关注 (@ int , @able root, ) 的源代码

public View inflate(@LayoutRes int resource, @able ViewGroup root, boolean attachToRoot) {
final Resources res = getContext.getResources;
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}

final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close;
}
}

我们看到,首先将resId指向的xml文件转换为res对象,然后执行(, root, )方法。 这个方法比较长,这里只贴出核心步骤。

public View inflate(XmlPullParser parser, @able ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {

final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;

try {
……

if (TAG_MERGE.equals(name)) {
if (root == || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}

rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);

ViewGroup.LayoutParams params = ;

if (root != ) {
……
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
……

// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);

// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != && attachToRoot) {
root.addView(temp, params);
}

// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == || !attachToRoot) {
result = temp;
}
}

} ……
省略异常处理部分
……

return result;
}
}

上面的步骤还是很长,我们分几个部分来分析。

第一部分

 if (TAG_MERGE.equals(name)) {
if (root == || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}

rInflate(parser, root, inflaterContext, attrs, false);

}

如果xml根标签是merge,root不能为空,必须为true。

然后执行 (, root, , attrs, false)

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(" cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" 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;
}
}

我们需要重点关注上面的方法

1. 遍历该节点的子节点
2. 子节点有 "requestFocus"、"tag"、""、"include"
3. 子节点不能是 "merge"
4. 子节点的其他情况,则是各种 View 的标签
5. View 标签和 "include" 标签会创建 View 对象
6. 遍历结束以后执行 parent.onFinishInflate

如果有子节点则执行。

的源码与(,root,)类似,读取xml对应的文件,转换成并遍历其中的标签。

一层层调用后,我们可以找到最终创建View的代码

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);

代码的第一部分,我们的结论是,

(, name, , attrs) 负责创建View对象。

第二部分

 // Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);

ViewGroup.LayoutParams params = ;

if (root != ) {
……
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
……

// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);

// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != && attachToRoot) {
root.addView(temp, params);
}

// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == || !attachToRoot) {
result = temp;
}

因为这里排除了merge标签,所以这里的根标签一定是一个View,所以调用(root, name, , attrs)方法创建一个View。

这再次证实了第一部分中得出的结论(,name,attrs)负责创建View对象。

然后看后面的代码就明白了

(@int , @able root, ) 三个参数之间的关系

只有当root不这样做时,才会读取xml和布局的属性。 (这里可以解释为什么我们有时使用加载的xml根标签属性总是无效)

如果为 True,则返回根对象。 否则,返回由xml创建的根标签指定的View

创建视图对象

通过以上判断,我们终于找到了最核心的方法

private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}

有一个layer被包裹并设置为false,这意味着它会受到Theme的影响。

我们在(, name, , attrs, false)中找到创建View的代码

 View view;
if (mFactory2 != ) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != ) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = ;
}

if (view == && mPrivateFactory != ) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}

if (view == ) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, , attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}

return view;

这里有 、 、 三个对象,看起来都可以创建View。对于.app来说,这三个对象都是或者空的实现(下一节会讲这个)所以我们直接看

 final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, , attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}

这里需要说明一下,如果name属性中包含.,则说明这是一个自定义View,并且系统自带了View,我们可以省略类路径,但是自定义View不能省略。

对于自定义View的创建,这里省略大部分代码

public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor constructor = sConstructorMap.get(name);
……
try {

final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
mConstructorArgs[0] = lastContext;
return view;

} ……
}

仅仅看到.(args),我们就已经明白反射是用来创建View对象的。

对于各种内置View,我们发现其实现类中存在重载


public class PhoneLayoutInflater extends LayoutInflater {
private static final String sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};

@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != ) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack
// at it.
}
}

return super.onCreateView(name, attrs);
}
}

查看代码中

protected View onCreateView(View parent, String name, AttributeSet attrs)
throws ClassNotFoundException {
return onCreateView(name, attrs);
}

protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}

我们可以看到,对于系统内置的View,会依次添加在View的标签前面

“..”、“..”、“.app”、“.view”。

然后通过反射创建一个View。

(文章略有删减,可点击原文查看~)

至此,Xml到View对象的转换过程就全部结束了~~~

看到这里的童鞋们真是辛苦了! 这个技能你掌握了吗?

最近的文章:

今日问题:

谁能画出这个过程的图吗?

如果你画得有把握,就扔进马仔的学习群,马仔会给你发红包!

快来Mazai社区解锁新姿势吧!社区升级:最大化你的学习效率

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON