RecyclerView中Item不能居中显示

问题

使用View.inflate()加载RecyclerViewitem布局时,导致item内容无法居中显示.

   @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View inflate = View.inflate(parent.getContext(), R.layout.rv_item_dance_indicator, null);
        return new ViewHolder(inflate);
    }

当时脑子抽风了,想偷个懒,用这种方式加载item布局,结果发现item竟然不能居中显示,一直以为是item布局的问题,这里把布局文件也贴一下.



    


各种尝试还是找不到原因,真无语了,怎么就居中不了呢?

最后还是百度大法,搜索关键字recyclerview item不能居中显示,出来一大堆,说什么的都有,比如更改一下根布局,改成线性布局啊,一看有不靠谱,最后还是找到一点有用的信息,原文地址

解决方案

动手一试,改成如下代码,果然成了,可喜可贺.

   @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View inflate = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.rv_item_dance_indicator, parent, false);
        return new ViewHolder(inflate);
    }

当然学习不能不能只知其然而不知其所以然,抱着这个心态来瞄一瞄源码,这两种加载方式的区别到底在哪,以后到底该怎么使用?

源码分析

LayoutInflater.from

 /**
     * Obtains the LayoutInflater from the given context.
     */
    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;
    }

调用系统服务得到一个布局加载器.

LayoutInflater.inflate

 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }
        //传入布局资源id,并获得一个xml解析器,用以解析xml布局资源
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            //调用inflate开始解析xml
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

inflate方法接收3个参数

  • int resource 需要加载的布局资源文件
  • ViewGroup root 父布局
  • boolean attachToRoot 是否需要添加进父布局

然后调用内部的inflate方法

inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 
....
//这里只贴关键代码
// Temp is the root view that was found in the xml
                    //解析xml标签并返回一个view
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;
                   //root是最初传进来的父view
                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        //如果父view不为空,则会生成相应的布局参数
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            //如果attachToRoot为false,为view设置布局参数
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

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

                    // Inflate all children under temp against its context.
                    //递归加载子view
                    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.
                    //如果父view不为空且attachToRoot为true,则将生成的view添加到父view中
                    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很重要,如果传了,会为加载的view设置布局参数,并且attchToRootture还会将加载的布局添加到父view中.

在看一下generateLayoutParams方法

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

这里是实例化一个布局参数并读取xml中设置的宽高进行初始化,所以只有当你传入了父view,xml中定义layout_widthlayout_height才有效.否则给的应该是一个默认值.应该都是wrap_content,这里只是做一个猜测.

View.inflate()

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

还是调用 LayoutInflater.from(context),并传了两个参数,继续跟进去

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

这里又调用了3个参数的inflate方法.如果root为空,第三个参数为false,否则为true(即添加到父布局内部).

总结

以后不管在什么情况下加载布局,一律使用LayoutInflator.inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

有父布局则传父布局,需要加入到父布局第三个参数传true,否则传false.

你可能感兴趣的:(RecyclerView中Item不能居中显示)