栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android RecyclerView item视图 、动态添加View时视图宽度问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android RecyclerView item视图 、动态添加View时视图宽度问题

一、场景:

当我们在RecyclerView 或者 动态给View添加视图时会使用 LayoutInflater 将视图布局解析成View,然后添加到根布局中。LayoutInflater inflate方法使用不当就会导致视图宽度有问题,比如inflate的布局中给定了宽高,但是最终展示还是充斥整个布局。或者出现java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

二、问题

1、RecyclerView item视图 、动态添加View时视图宽度问题
2、java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

三、解决

使用LayoutInflater.from(parent.context).inflate(R.layout.adapter_navigation, parent,false)同时解决上面两个问题。

四、分析

LayoutInflater inflate 最终调用的都是此方法

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

1、源码片段

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,创建LayoutParams,params = root.generateLayoutParams(attrs) 这就是问题的所在。

    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }
        public LayoutParams(Context c, AttributeSet attrs) {
            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
            setbaseAttributes(a,
                    R.styleable.ViewGroup_Layout_layout_width,
                    R.styleable.ViewGroup_Layout_layout_height);
            a.recycle();
        }
        protected void setbaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
            width = a.getLayoutDimension(widthAttr, "layout_width");
            height = a.getLayoutDimension(heightAttr, "layout_height");
        }

2、源码片段

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

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

注意 root.addView(temp, params); 方法,我们在看ViewGroup的addView方法调用的

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

里面的片段

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

这便是问题根源,那为什么child的parent会有值呢?看下面

        // tell our children
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }
    
    void assignParent(ViewParent parent) {
        if (mParent == null) {
            mParent = parent;
        } else if (parent == null) {
            mParent = null;
        } else {
            throw new RuntimeException("view " + this + " being added, but"
                    + " it already has a parent");
        }
    }
    
    protected ViewParent mParent;

所以当需要连续重复使用同一个布局时 inflate中的第三个参数 boolean attachToRoot 必须设置为false

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/778159.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号