学习笔记-inflate详解

layoutInflater.inflate()是常常运用的一个体系办法,运用它将XML加载为一个View目标,来详细扒一扒它的源码。

1. i单元测试nflate

inflate一共有四个重载方javaee法:

  1. inflate(java模拟器int resource, ViewGroup root)
  2. inflate(XmlPullParser parser, Vi源码交易平台ewGroup root)
  3. inflate(int re源码中的图片source, ViewGroup root, boolean attachToRootjava语言)
  4. infljava环境变量配置ate(XmlPu单元测试能发现约80的软件缺陷llParser parser, ViewGroup root, boolean attachToRoot)

前三个办法最终都调用到了第四个重载办法,不同的便是第三个办法中有一源码之家tryIn变量值flatePelement是什么意思recompilJavaed()elements办法,后续读取资源ID,转化为XmlResourjava编译器ceParse源码编辑器r之后,还是调用了第四个重载办法,一java语言切的逻辑都是在第四个重载办法之中。

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    return inflate(resource, root, root != null);
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
  	return inflate(parser, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
    if (view != null) {
      return view;
    }
    XmlResourceParser parser = res.getLayout(resource);
    try {
      return inflate(parser, root, attachToRoot);
    } finally {
      parser.close();
    }
}

进入t变量名的命名规矩elements中文翻译ry源码精灵永久兑换码InflelementanimationelementuiatePrecompiled()查看这个办法会有一个if (!mUseCo源码网站mpiledView)的退出判别。查这个变量的赋单元测单元测试家长评语试常用办法值的当地,默许的初始化办法是设置为fal变量名的命名规则se,给它赋值为true的当源码编辑器下载地只要一个,在setPrecompiledLayoutsEnabledForTesting(),这是一个element滑板只针对单元测试开放的办法。

所以tryInflatePrecompiled()办法一般不单元测试的主要内容会走到。

class LayoutInflater {
    private boolean mUseCompiledView;
    private void initPrecompiledViews() {
        // Precompiled layouts are not supported in this release.
        boolean enabled = false;
        initPrecompiledViews(enabled);
    }
    private void initPrecompiledViews(boolean enablePrecompiledViews) {
        mUseCompiledView = enablePrecompiledViews;
        ...
    }
    /**
     * @hide for use by CTS tests
     */
    @TestApi
    public void setPrecompiledLayoutsEnabledForTesting(boolean enablePrecompiledLayouts) {
        initPrecompiledViews(enablePrecompiledLayouts);
    }
    View tryInflatePrecompiled(int resource, Resources res, ViewGroup root, boolean attachToRoot) {
        if (!mUseCompiledView) {
            return null;
        }
        ...
    }
}

1.1. XmlResourcelement滑板eParser

前面经过调用res.getLayout(rejava训练source)变量泵LayoutResjava编译器转化为了XmlResourel源码中的图片ementanimationceParseelements中文翻译r目标。

Xmlelements中文翻译ResourcePar源码网站ser是XML资源的解析接口,简element翻译略来说便是一个XML变量的定义资源解析器,用于解析xml资源,在后面会大面积用到,这儿对它的办法源码编辑器做一些简略介绍。

  1. int getEventType()

    回来当时的事件类型,首要类型类型包括:单元测试

    • int START_DOCUMENT = 0:XML文Ele源码编辑器下载ment单元测试的主要内容档开始标变量值

    • int END_D单元测试常用方法OCUMENT = 1javascript变量与函数XML文档结束标志

    • injavaeet START_TAG = 2:XML标签element滑板开始标志,比方<LinearLayout

    • int END_TA单元测试集成测试体系测试G = 3:xml 标签结束标志,比方</LinearLayout>或许/>

  2. St变量名的命名规则ring gjava是什么意思etAttributeValue(String namespace,String name)

    回来指定的特点名对应的elementui特点变量与函数值,假如没有运用命名空间,则第一个参数传入null。

  3. String getNjavascriptame()

    回来单元测试当时 TAG 的名字,变量名的命名规java面试题比方&lt变量的界说;Lielementary是什么意思nearLayoutjava面试题回来LinearLayout

  4. int nextelement滑板()

光标移动到下一个标签。

  1. int getDepth()

    获取当时标签的深度,根布局深度为1,根Java布局的直接子控件深度为2 ,以此类推,每多一层深度加1。

1变量之间的关系.2. 第四个重载办法

这个办法中是inflate的首要逻辑。

在这个办法中源码中的图片,首要是对merge做了特别处理,直接调用了rInflate()持续遍历。

merge并不是一个ViewGroup或许View,它相当于声明晰一些视图等待被添加到另一个ViewGroup中,能够削减许多不必要的布局。(运用merge的时分能够运用tools:paren源码编辑器下载tTaelement滑板g特点进行预览添加到对应ViewGroup的效果)

关于不是merge的状况,则调用Cr源码网站eateViewFromTag()创立根视图之后,根视图将javaee作为root调用rI源码之家nflateChildren()持续遍历。这几个办法后文都有介绍。

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        。。。
        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser); //特点调集
        View result = root;
        try {
            //校验START_TAG标签
            advanceToRootNode(parser);
            final String name = parser.getName();
            if (TAG_MERGE.equals(name)) {
                //运用merge的时分,inflate必须有root且attachToRoot==true
                if (root == null || !attachToRoot) {
                    throw new InflateException("<merge /> 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 = null;
                //inflate的时分假如没有传root,给根view设置的特点会悉数失效
                if (root != null) {
                    // 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)
                        //假如attachToRoot==false,直接将param设置给根视图
                        //假如attachToRoot==true,经过addView设置给根视图
                        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 != null && attachToRoot) {
                    root.addView(temp, params);
                }
                // Decide whether to return the root that was passed in or the
                // top view found in xml.
                //假如root != null && attachToRoot,会直接将root传回去,其他状况下将根视图传回去
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }
        }
        return result;
    }
}

接着从这个办法还能够Element看到平常运用infljava语言ate的几个特性:

  • melementaryergejava编译器并不是视图,对它设源码编变量之间的关系程器置的标签都是无效的
  • 假如根布局是merge,运用inflate加载时,root不能为空且attchToRoot不能是false
  • 假如root为空,根视图的一切标签都将失单元测试常用方法效,原因应该是必须根据root的类型源码中的图片去创立不同的layoutParam
  • 源码交易平台root != njava是什么意思ull && atta变量类型有哪些chT变量类型有哪些oRoot,根视图会直接添加到root,而且变量值relement翻译oot将作为回来值,不然根源码编辑器视图作为回来值。

1.3. rInflate

接着往下看,rInelementaryfl单元测试家长评语ateChildren()调用的还是rInflate()办法,两个的差异也便是context获取不太同。

rInflate()中首单元测试用例先是一个大循环,看这个循环条件(源码编辑器(type = pelementanimationarser.n变量值ext()) != X源码之家mlPullParsejava面试题r.END_TAG ||parser.getDe变量值pth单元测试能发现约80的软件缺陷() > depthJava),只要两者同时不满足(即走到了END标签,而且深度不大于parenelementst的深度)才会退出循环element翻译,一般来说,也便是走到parentEND_TAG标签退出循环。

循环内部,对requestFocustagelementaryinclude标签做了特别的处源码交易平台理,其他状况下调用createViewFromTag()创立当时View。而且调用rInflateChildrejava面试题n()进行递归往下变量是什源码中的图片么意思调用。假如不是ViewGroup变量名,会在源码编辑器下载下一次递java语言归中读取到当时viewEND_TAG标签,也便是源码编辑器进不去循环之中java环境变量配置

源码之家以能够确定的是,每一层rInflatElemente()调用的时源码网站候,创立出来的都只要parent的直接子View。最终创立出来的子源码view都会被添加到对应的pa单元测试用例rent

final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
                            boolean finishInflate) throws XmlPullParserException, IOException {
    rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
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)) {
            //requestFocus:parent获得焦点
            pendingRequestFocus = true;
            consumeChildElements(parser); //跳过到parent层级
        } else if (TAG_TAG.equals(name)) {
            //tag: 调用parent.setTag(key, value)设置tag标签
            parseViewTag(parser, parent, attrs);
        } else if (TAG_INCLUDE.equals(name)) {
            //添加include 标签
            if (parser.getDepth() == 0) {
                throw new InflateException("<include /> cannot be the root element");
            }
            parseInclude(parser, context, parent, attrs);
        } else if (TAG_MERGE.equals(name)) {
            //merge只能是根标签
            throw new InflateException("<merge /> must be the root element");
        } else {
            // createViewFromTag创立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);
        }
    }
    if (pendingRequestFocus) {
        parent.restoreDefaultFocus();
    }
    if (finishInflate) {
        parent.onFinishInflate();
    }
}

1.4. cre单元测试变量之间的关系的首要内容ateViewFromT单元源码1688测试是什么ag

前面的办法中,多变量是什么意思变量是什么意思经过调用crea单元测试家长评语teViewFromTag()创立了控件。

这个办法中,首要特别处理变量是什么意思view标签,将clas源码编辑器s变量英语径作为name读取出来。之后读取主题。

分为两步进行了测验创立,首要是调用tryCreateV源码编辑器下载iew()进行创立。假如回来NULL,接着判别途径名小数单元测试点的数量,分别走不同变量名的办法,回想一下,没有小数点的标签,也便elementary是什么意思是体系自带的各种view,在XML中不需求输入全途径而只需elementary翻译求称号即可。

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
                       boolean ignoreThemeAttr) {
    if (name.equals("view")) {
        //小写view,能够直接传入class途径,将name重置掉
        name = attrs.getAttributeValue(null, "class");
    }
    //读取主题
    if (!ignoreThemeAttr) {
        final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
        final int themeResId = ta.getResourceId(0, 0);
        if (themeResId != 0) {
            context = new ContextThemeWrapper(context, themeResId);
        }
        ta.recycle();
    }
    try {
        View view = tryCreateView(parent, name, context, attrs);
        if (view == null) {
            ...
            try {
                if (-1 == name.indexOf('.')) {
                    view = onCreateView(context, parent, name, attrs);
                } else {
                    view = createView(context, name, null, attrs);
                }
            }
        }
        return view;
    }
    ...
}

1.4.1. tryCr源码编辑器下载eate源码1688View

接着看下怎么经过tryCreate变量英语View()测验创立的。

办法首要判别blink标签,elementanimation是留下java怎么读来的一个小彩蛋,Let’s party like变量泵 it’s 1995!

后续,调用mFactorelementary翻译ymFactory2JavamPrivat变量的定义eFactory源码之家创立办法代理出源码年代去。

public final View tryCreateView(@Nullable View parent, @NonNull String name,
                                @NonNull Context context,
                                @NonNull AttributeSet attrs) {
    if (name.equals(TAG_1995)) {
        // Let's party like it's 1995!
        return new BlinkLayout(context, attrs);
    }
    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);
    }
    return view;
}

为了快速找到这几个工厂完成办法在javae变量名的命名规则e哪里,直接打个断点找一下。

学习笔记-inflate详解

能够看到,element变量ary首要调用的mFactory2的完成AppCompatDelegateImplmPrivat源码精灵永久兑换码eFactelement是什么意思ory的完成Activielementarytyjavascript

mPrivateFactory的完成Activity的逻辑会集在FragmentActivity中,elementary是什么意思其间对fragjava怎样读ment标签做特别处理,而且能够自己经过源码编程器Activity去代理阻拦创立。这儿不向里边探索,首要来看下mFact单元测试能发现约80的软件缺陷ory2的完成AppCjava培训ompatDelegateImpl

AppCompat变量名Delegatjava模拟器eImpl中层层传递,最终在AppComelementanimationpatDelegateI单元测试mpl.createView()中代理给了AppCompatViewInflater变量名现。

AppCompatViewInflater.c源码编辑器reateView()中能够看到,对标签为TextView的直接创立回来了AppCompatTextViewelementary翻译

这可elements不便是AppCompatXXX之类的控件转化的当地!意外收成。也就搞清楚了,为什么XML中就主动转化成对应的单元测试AppCompatXXX,而自界说View变量值时分需求直接单元测试评语怎样写承继AppCom源码编程器patXXX

要是在这儿没有阻拦,一般就回来了NULL,交由Layoutelement是什么意思Inelementary翻译flelementary是什么意思ater创立。

//AppCompatDelegateImpl
public View createView(View parent, final String name, @NonNull Context context,
                       @NonNull AttributeSet attrs) {
    ...
    if (mAppCompatViewInflater == null) {
        mAppCompatViewInflater = new AppCompatViewInflater();
        ...
    }
    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
final View createView(View parent, final String name, @NonNull Context context,
                      @NonNull AttributeSet attrs, boolean inheritContext,
                      boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
    ...
    View view = null;
    // We need to 'inject' our tint aware Views in place of the standard framework versions
    switch (name) {
        case "TextView":
            view = createTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ImageView":
            view = createImageView(context, attrs);
            verifyNotNull(view, name);
            break;
        ...
    }
    return view;
}
//AppCompatViewInflater
protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
    return new AppCompatTextView(context, attrs);
}

1.4.2. createV源码精灵永久兑换码iew

tryCreateView(变量之间的关系)假如没有成功创立,会持续往下走,根据有没有小数点进入不同的onCreateView()或许createView(),首要单元测试能发现约80的软件缺陷来看下onCreateView()是怎么处理体系控件的。

1.4.2.1. onjava是什么意思CreateView

LayoutInflater中,onCreateView()也是层层传递,最终直接调用了createView,只是将包名前缀android.vijava培训ewelementary.作为perfix特点带上进入了createView(),看到这也就知道,之所以不变量之间的java培训关系需求写源码精灵永久兑换码完整包名,是因为java是什么意思主动进行了补充。

但走到这,有一个大疑惑源码交易平台,体系组件并不止android.view这一个包名,查了一下单元测试中设计测试用例的根据是,发现LayoutInflater有一个完成类PhoneLayou源码时代tInflater重写了onCr变量英语eateView()办法,在里边分别测验了android.widgetandroie变量名lements中文翻译d.webkitandroid变量与函数.app的包名进行创源码年代建,也便是说,平常运用的LayoutInflater其实是Ph变量的界说oneLayoutInflater目标。

//LayoutInflater
protected View onCreateView(String name, AttributeSet attrs)
        throws ClassNotFoundException {
    return createView(name, "android.view.", attrs);
}
public class PhoneLayoutInflater extends android.view.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 != null) {
                    return view;
                }
            } 
        }
        return super.onCreateView(name, attrs);
    }
}

1.4.2.2. createView

crea变量值teView()终于看到了创立逻辑elements中文翻译,首要在缓存中查找有没有创立器。

单元测试的主要内容如没有找到,就测验经过包名去加载CLASS目标并拿到它的加载器缓存起来,这儿运用的便是刚才传入的prefix + nameprefix便是oncreate源码之家View()中添加的完整包名前缀。

之后便是调用newInstance()创立单元测试目标,传入的参数Object[],共有两位,第一位是Context,第二位是AttrelementsibuteSet。对应的便是View(Context context, AttributeSet attrs)结构办法。这儿也就对应了假如自界说Vieelementsw只是完成了Conte单元测试xt单元测试的结构办法,代码中动态Element创立不会单元测试犯错,可是不能在XML运用。

另一个点,便是这儿反射并没有更改访问权限的检查,所以JAVA中需求给CLASS设置publicKotlin中默许是elementuip单元测试是什么ublic=_=)。

public final View createView(NonNull Context viewContext, NonNull String name,
                             @Nullable String prefix, @Nullable AttributeSet attrs)
        throws ClassNotFoundException, InflateException {
    ...
    Constructor<? extends View> constructor = sConstructorMap.get(name);
    if (constructor != null && !verifyClassLoader(constructor)) {
        constructor = null;
        sConstructorMap.remove(name);
    }
    Class<? extends View> clazz = null;
    try {
        if (constructor == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                    mContext.getClassLoader()).asSubclass(View.class);
            constructor = clazz.getConstructor(mConstructorSignature);
            constructor.setAccessible(true);
            sConstructorMap.put(name, constructor);
        }
        Object lastContext = mConstructorArgs[0];
        mConstructorArgs[0] = viewContext;
        Object[] args = mConstructorArgs;
        args[1] = attrs;
        try {
            final View view = constructor.newInstance(args);
            return view;
        } finally {
            mConstructorArgs[0] = lastContext;
        }
    }
    ...
}

1.5. parseIncelementanimatio源码精灵永久兑换码nlude

前面看到过,关于include标签做了element滑板特别处理单元测试家长评语

逻辑和inflate根本一致,读取到了layoutid之后,先调用源码网站tryInfl变量ate源码交易平台Precompiled(),之前介绍过这个办法只是为了测试用。

然后java模拟器根据是不是merge标签,走element滑板不同源码的分支进入到rInflate(),进入创立流程。

private void parseInclude(XmlPullParser parser, Context context, View parent,
                          AttributeSet attrs) throws XmlPullParserException, IOException {
    int type;
    int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
    ...
    final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
            (ViewGroup) parent, /*attachToRoot=*/true);
    if (precompiled == null) {
        final XmlResourceParser childParser = context.getResources().getLayout(layout);
        try {
            ...
            if (TAG_MERGE.equals(childName)) {
                rInflate(childParser, parent, context, childAttrs, false);
            } else {
                final View view = createViewFromTag(parent, childName,
                        context, childAttrs, hasThemeOverride);
                final ViewGroup group = (ViewGroup) parent;
                // Inflate all children.
                rInflateChildren(childParser, view, childAttrs, true);
                ...
                group.addView(view);
            }
        } finally {
            childParser.close();
        }
    }
}

2. Inflaelement滑板te获取

  1. 经过体系服务获变量是什么意思

    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    
  2. 经过f变量英语rom获取

    LayoutInflater静态办法获取,也是经过体系服务获取。

    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }
    
  3. 经过activity获取java语言

    acti单元测试家长评语vityPhoneWindow中获取,PhoneWindow中也是经过from获取。

    public LayoutInflater getLayoutInflater() {
        return getWindow().getLayoutInflater();
    }
    public class PhoneWindow extends Window implements MenuBuilder.Callback {
        private LayoutInflater mLayoutInflater;
        public PhoneWindow(Context context) {
            super(context);
            mLayoutInflater = LayoutInflater.from(context);
        }
        public LayoutInflater getLayoutInflater() {
            return mLayoutInflater;
        }
    }
    
  4. 经过View获取

    View留有infl变量泵ate办法,经过from获取LayoutInflater

    public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
        LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
    }
    

假如觉得这篇文章不错,请点个赞支elementary是什么意思撑一下。

发表评论

提供最优质的资源集合

立即查看 了解详情