LayoutInflate渲染view源码分析

一般来说,加载渲染试图view所用方法无非两种:
第一种是View类中的静态inflate()方法;
第二种就是LayoutInflater.from(context).inflate()方法
那么这两种方法有什么区别呢?
首先我们看一下View.inflate()方法,点进去看源码:

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

那么这个方法和LayoutInflate方法到底有什么区别呢?我们继续点击factory.inflate(resource, root)进去看一下


 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);------注意处。
    }
这块很明显了,View.inflate(contex,resource,root)最后其实还是调用的LayoutInflater.from(context).inflate(resource, root, attachToRoot),只是这个布尔值attachToRoot直接为了root != null。attachToRoot很清楚的翻译:是否绑定到root。用View.inflater渲染时,如果传入了有意义的root,那么最后的attachToRoot=true。
LayoutInflater.from(context).inflate(resource, root, attachToRoot)这个方法大家肯定不陌生,在listview或者recyclerView对viewholder绑定时都要用到这个方法来渲染出item进行绑定。那么我们一直在adapter中是这样写的:LayoutInflater.from(context).inflate(resource, root, false)。那么最后这个参数有什么影响呢?我们不妨将自己项目中的参数进行修改,改为传递true进去运行,结果如下:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

错误信息提示我们这个childview已经有了自己的父集view,你必须先去remove掉child的第一个父集view。
什么意思?我们什么时候去将我们渲染出来的child添加到其他view中了呢?深入下去:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            //解析xml布局文件获取属性
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();

                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }

                if (TAG_MERGE.equals(name)) {
                    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;
                    //第二处
                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    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);
                            //-参数的设置,如果root为null,就不会设置view的参数、
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

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

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // 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);
                        //listview或者recyclerview中传false是因为不需要自己去addchild,最后listview等都会自己去加载自己渲染出来的child。
                    }

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

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            return result;
        }
    }

分开来讲第一处:

 //第一处
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
很简单的一个英语翻译:这个temp就是从你传入的xml文件中找到的根节点,什么意思?这个temp就是每一个渲染出来的view,在recyclerview和listview中就是每一个item。

继续第二处分析:

//第二处
 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)
         temp.setLayoutParams(params);
         //-参数的设置,如果root为null,就不会设置view的参数、
         }
        }
如果root不为null,它会去给你获取到属性值,如果attachToRoot是false,他才会给你渲染出来的temp设置进去。所以如果单独去渲染一个view出来,想要获取到他的LayoutParams,那么在调方法时,就要注意root和attachToRoot的参数的传入了,不然获取LayoutParams只会返回null。

OK,最后看一下第三处:

  //第三处
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                        //listview或者recyclerview中传false是因为不需要自己去addchild,最后listview等都会自己去加载自己渲染出来的child。
                        // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                    }
这地方就是The specified child already has a parent. You must call removeView() on the child's parent first.的真正原因了。如果root不为null且attachToRoot=true,他会直接将你渲染出来的view(temp)添加到root中,但是listview和recyclerview他的内部都会将渲染出来的item添加到其内部,所以如果这里就直接给temp添加了一个parent,那么在listview等添加时就会直接报错。
那么root能否提供null呢?很显然整个方法块下来,root是否传null对view的渲染没有影响,仅仅是在给view设置参数和添加parent是会进行判断。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cesium 是一个基于 WebGL 的开源地球渲染引擎,主要用于在浏览器中呈现三维地理数据。它使用了一些复杂的渲染技术和算法来实现高性能和高质量的地球渲染效果。下面是对 Cesium 渲染源码的简要分析: 1. Shader:Cesium 使用了大量的着色器程序来实现不同的渲染效果,如光照、纹理映射、阴影等。着色器程序是在 GPU 上执行的,并且可以通过 GLSL(OpenGL Shading Language)语言进行编写。Cesium 的着色器程序通常是在运行时动态生成的,以适应不同的地图数据和渲染需求。 2. 地形渲染:Cesium 支持高度精细的地形渲染,它使用了基于切片的地形渲染技术。在渲染过程中,Cesium 会将地球表面分割成小块切片,并根据每个切片的高度数据和纹理信息生成相应的网格和纹理。这些切片可以根据需要进行加载和卸载,以实现地图数据的动态加载和显示。 3. 纹理映射:Cesium 使用了纹理映射技术来实现地球表面的贴图效果。它可以将不同类型的纹理(如卫星图像、地形纹理、气候数据等)映射到地球表面的不同部分,以实现真实的地貌效果。Cesium 通过加载和解析各种类型的地图数据,将其转换为纹理信息,并将其应用到地球表面的相应区域。 4. 光照和阴影:Cesium 支持实时的光照和阴影效果,以增强地球渲染的真实感。它使用了基于物理的光照模型,考虑了光源的位置、光照强度、表面材质等因素,并通过计算每个顶点和像素的光照值来实现逼真的光照效果。此外,Cesium 还支持动态阴影的生成,可以根据光源的位置和地形的形状计算出地球表面上的阴影效果。 5. 动态渲染:Cesium 支持动态渲染技术,可以在实时交互的情况下实现高性能的地球渲染效果。它使用了一些优化技术,如级联阴影映射、视锥剔除、LOD(Level of Detail)等,以减少渲染负载并提高渲染效率。此外,Cesium 还支持动态加载和卸载地图数据,以实现在不同的视角和缩放级别下的快速渲染和响应。 需要注意的是,Cesium 的源码非常庞大和复杂,涉及到多个模块和子系统。以上只是对其渲染部分的简要分析,实际的源码分析需要深入研究 Cesium 的代码库和文档,并对 WebGL 和图形渲染技术有一定的了解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值