Android LayoutInflater开发中的用法

先看一下下面代码运行结果

MainActivity

public class MainActivity extends Activity {

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

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        ListView listView = (ListView) findViewById(R.id.listview);
        listView.setAdapter(new InflateAdapter(this));
    }
}
activity_main.xml

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

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></ListView>

</LinearLayout>


inflate_layout_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/inflate_item_tv"
    android:layout_width="100dp"
    android:layout_height="50dp"
    android:text="LayoutItem"
    android:gravity="center"
    android:background="#0ff"/>
inflate_layout_parent.xml

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

    <TextView
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:text="LayoutParent"
        android:gravity="center"
        android:background="#ff0"/>
</LinearLayout>
InflateAdapter

public class InflateAdapter extends BaseAdapter {
    private LayoutInflater mInflater = null;

    public InflateAdapter(Context context) {
        mInflater = LayoutInflater.from(context);
    }

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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View[] viewList = {
                mInflater.inflate(R.layout.inflate_layout_item, null),
//                mInflater.inflate(R.layout.inflate_layout_item, parent),
                mInflater.inflate(R.layout.inflate_layout_item, parent, false),
//                mInflater.inflate(R.layout.inflate_layout_item, parent, true),
                mInflater.inflate(R.layout.inflate_layout_item, null, true),
                mInflater.inflate(R.layout.inflate_layout_item, null, false),

                mInflater.inflate(R.layout.inflate_layout_parent, null),
//                mInflater.inflate(R.layout.inflate_layout_parent, parent),
                mInflater.inflate(R.layout.inflate_layout_parent, parent, false),
//                mInflater.inflate(R.layout.inflate_layout_parent, parent, true),
                mInflater.inflate(R.layout.inflate_layout_parent, null, true),
                mInflater.inflate(R.layout.inflate_layout_parent, null, false),
        };

        convertView = viewList[position];
        return convertView;
    }

}

运行结果


当打开上面viewList数组中任意一行注释都会抛出异常(java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView)

为什么注释掉的有问题,其它的能正常工作呢,带着这些问题去看源码到底是怎么个回事?

LayoutInflater初始化

Activity

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);
        ......
        }

PhoneWindow

 public PhoneWindow(Context context, Window preservedWindow) {
        this(context);
        ......
        }
public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

LayoutInflater
 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;
    }
开发中得到LayoutInflate对象有两种方式

LayoutInflater lif = LayoutInflater.from(Context context);

LayoutInflater lif = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
from方法仅仅是对getSystemService的一个安全封装而已


得到LayoutInflater对象之后我们就是传递xml然后解析得到View,如下方法:

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

inflate()方法一般接收两个参数,第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。这样就成功成功创建了一个布局的实例,之后再将它添加到指定的位置就可以显示出来了。


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

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
得到XmlResourceParser接口的实例(LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的)


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

            final Context inflaterContext = mContext;
            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) {  //如果一开始就是END_DOCUMENT,那说明xml文件有问题
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName(); //这里得到name一定是START_TAG,也就是xml文件里的root节点
                
                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }

                if (TAG_MERGE.equals(name)) {
                    //处理merge tag的情况(merge在APP的xml是用作性能优化)
		    //root必须非空且attachToRoot为true,否则抛异常结束(APP使用merge时注意事项,
                    //merge的xml并不代表某个具体的view,只是将它包起来的其他xml的内容 加到某个上层ViewGroup中。)
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
		//递归inflate方法调运      
                    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);//根据Tag节点来创建View对象,这里只创建根布局

                    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); //根据root来获取对应的LayoutParams实例
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)  还原了原有的root根节点属性值
                            temp.setLayoutParams(params);//解释了上面mInflater.inflate(R.layout.inflate_layout_item, parent, false)情况
                        }
                    }

                    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);//root非空且attachToRoot=true,则将xml布局view添加到root(容器)上
                    }

                    // 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;
        }
    }
上面源码分析可以得知下面结论

  1. inflate(xmlId,null)  只创建并返回temp对应的View
  2. inflate(xmlId,parent)  创建temp的View,然后执行root.addView(temp, params),最后返回root
  3. inflate(xmlId,parent,false) 创建temp的View,然后执行temp.setLayoutParams(params),最后返回是temp
  4. inflate(xmlId,parent,true) 创建temp的View,然后执行root.addView(temp, params),最后返回root
  5. inflate(xmlId,null,false) 创建并返回temp对应的View
  6. inflate(xmlId,null,true) 创建并返回temp对应的View
所以在开发中传入不同参数,返回View是不一样的


现在分析一下上面注释掉代码报错的原因:

(java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView),抛异常是在AdapterView上

public class ListView extends AbsListView 

public abstract class AbsListView extends AdapterView<ListAdapter>

AdapterView
@Override
    public void addView(View child) {
        throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
    }

    /**
     * This method is not supported and throws an UnsupportedOperationException when called.
     *
     * @param child Ignored.
     * @param index Ignored.
     *
     * @throws UnsupportedOperationException Every time this method is invoked.
     */
    @Override
    public void addView(View child, int index) {
        throw new UnsupportedOperationException("addView(View, int) is not supported in AdapterView");
    }

上面可以知道,这个inflate()方法不能同时存在parent和attachToRoot=true,否则就会报错,因为AdapterView源码中调用了root.addView(temp, params);而此时的root是我们的ListView






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值