LayoutInflater的inflate方法学习

LayoutInflater的inflate方法很常用,用于动态加载布局。inflate方法有四种调用方式:

  1. public View inflate(int resource, ViewGroup root);
  2. public View inflate(XmlPullParser parser, ViewGroup root);
  3. public View inflate(int resource, ViewGroup root, boolean attachToRoot);
  4. public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot);

实质上最后都是调用第4个方法,内部原理即用XmlPullParser解析xml文件,然后返回view对象。

接下来针对我们最常用的public View inflate(int resource, ViewGroup root, boolean attachToRoot)方法进行说明。
首先,参数说明。
1. resource:布局的资源id
2. root:填充的根视图
3. attachToRoot:是否将创建的师傅绑定到根视图中

那么问题来了?
inflate(layoutId, null )
inflate(layoutId, root, false)
inflate(layoutId, root, true)
以上三种方式到底有什么区别?接下来通过一个简单的示例来说明。
首页放置一个listview。下面是list_item.xml的内容。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="50dp">

    <TextView  android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/text_tv" android:textSize="16sp" android:text="Hello world!"/>
    </LinearLayout>

main_activity.java的内容如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main);

        ListView myListView = (ListView)findViewById(R.id.list_view);
        myListView.setAdapter(new MyAdapter(this));
    }

    private class MyAdapter extends BaseAdapter {

        private LayoutInflater inflater;

        public MyAdapter(Context context) {
            inflater = LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            return 10;
        }

        @Override
        public Object getItem(int position) {
            return position;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView == null) {
                convertView = inflater.inflate(R.layout.list_item,null);
            }

            return convertView;
        }
    }
    }

区别在于getView中的布局加载方式:
①convertView = inflater.inflate(R.layout.list_item,null);
②convertView = inflater.inflate(R.layout.list_item,parent,false);
③convertView = inflater.inflate(R.layout.list_item,parent,true);

三者的运行结果分别是这样的:
方式①如图。可见list_item根布局的属性没有生效。

方式②:

方式③:
程序crash。log如下:

接下来,回到inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) 方法中看看为什么会这样。

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

                if (DEBUG) {
                    System.out.println("-----> start inflating children");
                }
                // Inflate all children under temp
                rInflate(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);
                }

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

从源码中可以发现,如果root为null,因为layout_width,layout_height是相对父级视图设置的,必须与父级的LayoutParams一致。而此时的getLayoutParams为null,list_item根布局的属性会被忽略掉,而使用默认的wrap_content方式。所以设置的高度被忽略。如果要使高度生效,可以将根布局的layout_height改为wrap_content, 而将textview的layout_height改为50dp。如果root不为null,attachToRoot为false,则可以正确地处理。

如果root不为null,attachToRoot为true,则会将改布局通过addview绑定到root视图里去,而此时的root是我们的ListView,ListView为AdapterView的子类,listview不支持addview方法,因此方式③就报错了。但是如果换一种场景,假设根布局是LinearLayout,用方式③是正确的,相当于在原有布局中动态加载布局。

如果root不为null,attachToRoot为true则返回root;attachToRoot为false则返回加载的view本身。

看到这里,平时在使用inflate方法时,属性不生效、或者参数到底该怎么传也就明白了。

你可能感兴趣的:(android)