View inflate 原理

澹台星剑
2023-12-01

由于经常会使用 

View view = inflater.inflate(R.layout.fragment_main, container, false); 或者
View view = inflater.inflate(R.layout.dialog_kp_user_product_item,  null);

  两个方法,但是不知道其有什么区别?如果搞错了会报

    Caused by: java.lang.IllegalStateException:The specified child already has a parent. You must call removeView() on the child's parent first.

 比如说我们在adapter ,把第三个参数改为true, 在动态添加布局的时候把 null 改为容器view,这样就会出现上面的错误?原因是传了true,表示立即把view 添加到viewGroup中,后面adapter 会再次把view 添加到viewGroup,这时候就会报错说 illegalStateException, 因为view已经被添加了,不能再次被添加。同理,对于

View view = inflater.inflate(R.layout.dialog_kp_user_product_item,  null);

也是如此,该方法一般再动态添加布局的时候用到,比如在动态添加布局时候,我们会用到此方法

public void setLayout(View view){
        View subLayout = LayoutInflater.from(this).inflate(R.layout.sub_layout, null);
        mLinearLayout.addView(subLayout);
    }

这里第二个参数传了null,源码追寻,

public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }


public void addView(View child, int index, LayoutParams params) {
        if (DBG) {
            System.out.println(this + " addView");
        }

        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }

        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

点击addView,往下走,会看到 addView (View child, int index, LayoutParams params),添加之前会测量一遍,在刷新,最后add,我们点进去addViewInner ,

 private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        if (mTransition != null) {
            // Don't prevent other add transitions from completing, but cancel remove
            // transitions to let them complete the process before we add to the container
            mTransition.cancel(LayoutTransition.DISAPPEARING);
        }

        if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " +
                    "You must call removeView() on the child's parent first.");
        }

        if (mTransition != null) {
            mTransition.addChild(this, child);
        }

        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }
        ......... 省略后面代码

重点来了,if (child.getParant () != null) 时候抛出一个异常,而内容就是我们刚刚报的异常;这样我们就明白了。inflater.inflate(R.layout.fragment_main, container, false) 打包 inflate 布局的时候不能立即给view添加父布局,否则会抛出异常。注意,这里的表述不能立即给view添加父布局,不是不添加,第三个参数 false 表示 延后添加,true 表示立即添加;同理,inflater.inflate(R.layout.dialog_kp_user_product_item, null);第二个参数为空自然没有父view ,也就不存在添加一说,当调用root.addView(subLayout)时候,root 会直接添加的,所以这里就不应该再传一个容器view,否则会重复添加,在调用addView的时候就会出错。

总结:一般,添加view的职责是系统做的,当我们调用addView的时候,这个添加任务系统就会帮我们做掉。我们仅仅是需要提供一个view 布局,通过inflate 实现,但是inflater 也能够帮忙添加布局,所以在inflater 就不需要添加布局了。

 

个人愚见,如有不正,欢迎指出。


参考:https://stackoverflow.com/questions/12567578/what-does-the-layoutinflater-attachtoroot-parameter-mean/45809756#45809756

 类似资料: