Android 入门宝典 - inflate 生成视图 & 动态加载视图

inflate

inflate 方法用于在运行时动态生成 view,它的参数含义如下:
@params resource:布局 xml 文件的 id
@params root:为生成的 view 指定的父容器
@params attachToRoot:是否添加到父容器

聪明的你会发现,设置 root 再设置 attachToRoot 是不是有点啰嗦?其实不会。那是因为 root 的主要作用之一是形成 view 中使用的正确的 LayoutParams;而 attachToRoot 则是为了方便直接添加到 root(内部使用了 root.addView()):

@param attachToRoot Whether the inflated hierarchy should be attached to the root parameter? If false, root is only used to create the correct subclass of LayoutParams for the root view in the XML.

attachToRoot 判断视图层次结构是否附属于 root 属性,为 false 的话则 root 只用于生成正确的 LatyoutParams 子类

// temp是在xml中找到的根视图
final View temp = createViewFromTag(root, name, inflaterContext, attrs);

ViewGroup.LayoutParams params = null;

if (root != null) {
   
    // 创建与根匹配的布局参数(如果提供)
    params = root.generateLayoutParams(attrs);
    if (!attachToRoot) {
   
        // 如果不attach,则为temp设置布局参数。(如果attach,我们在下面使用addView)
        temp.setLayoutParams(params);
    }
}

fragment 的 attachToRoot 为什么设为 false:LayoutInflater与attachToRoot杂谈笔记

各个参数重载方法的详解

inflate(resource, null):生成不带 LayoutParams 的 view 实例
inflate(resource, root):生成带有正确 LayoutParams 的 view,并添加到了 root 中(attachToRoot 设置为了 true,调用了 root.addView() 添加),等同于 inflate(parser, root, true),从源码可以看出:

// 传入的 root 非 null,则 root != null 为 true,相当于调用了 inflate(parser, root, true)
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
   
	return inflate(parser, root, root != null);
}
// 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);
}

inflate(resource, root, false):返回带有正确 LayoutParams 的 view,需要手动添加

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

总而言之,inflate 使用 root 参数则返回一个带有正确 LayoutParams 的 View。LayoutParams 为父布局在布局时提供参考信息,使用 LayoutParams 才能将 View 正确添加到父布局中

可以看到,无论哪种重载方法,最终都调用 inflate(parser, root, attachToRoot) 重载方法。

使用方式

构造函数使用 LayoutInflater.from(context).inflate() 生成视图,或者使用 context: LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) 或 activity:LayoutInflater inflater = activity.getLayoutInflater() 获得 inflater 实例,再使用 inflate() 方法

API 源码注释:

/**
 * Instantiates a layout XML file into its corresponding {@link android.view.View}
 * objects. It is never used directly. Instead, use
 * {@link android.app.Activity#getLayoutInflater()} or
 * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
 * that is already hooked up to the current context and correctly configured
 * for the device you are running on.  For example:
 *
 * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService
 *      (Context.LAYOUT_INFLATER_SERVICE);</pre>
 * 
 * <p>
 * To create a new LayoutInflater with an additional {@link Factory} for your
 * own views, you can use {@link #cloneInContext} to clone an existing
 * ViewFactory, and then call {@link #setFactory} on it to include your
 * Factory.
 * 
 * <p>
 * For performance reasons, view inflation relies heavily on pre-processing of
 * XML files that is done at build time. Therefore, it is not currently possible
 * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
 * it only works with an XmlPullParser returned from a compiled resource
 * (R.<em>something</em> file.)
 * 
 * @see Context#getSystemService
 */

大致的意思是:将 XML 布局实例化到相关的 Oject 中,可使用 Activity#getLayoutInflater() 或 Context#getSystemService 检索一个标准 inflater 实例,实例已经和 activity 或 context 挂钩,和正在运行的设备挂钩。

inflater 高度依赖提前处理的 XML 文件,XML 文件在编译资源时就已经给出,所以目前不可能使用 LayoutInflater 在运行时动态使用 XmlPullParser 将未经加工的 XML 布局进行实例化。

错误

当我们 inflate 自定义 view 的时候,使用 inflate(resource, root) 最终不能强制转换成我们自定义的 view 类,出现如下错误:

CircleView cv = (CircleView) LayoutInflater.from(this).inflate(R.layout.cv, layout);

Caused by: java.lang.ClassCastException: android.widget.RelativeLayout cannot be cast to com.example.customization.CircleView

inflate 返回的应该是 inflated 的 XML 文件的根视图,而错误原因却是 RelativeLayout,root 的根布局。这是因为:

@return The root View of the inflated hierarchy. If root was supplied and attachToRoot is true, this is root; otherwise it is the root of the inflated XML file.

@return 实例化层次结构根视图。如果提供了 root 且 attachToRoot 为 true,则为 root。否则,它是 inflated 的 XML 文件的根视图。

源码中表现为:

View result = root;
...
return result;

改为如下,则能正常转换:

CircleView cv = (CircleView) LayoutInflater.from(this).inflate(R.layout.cv, layout, false);

inflate详解

LayoutParams

View 用来告诉父布局它所需要的布局方式。

ViewGroup 的 LayoutParams 基类只描述它的大小宽高,不同的 LayoutParams 子类则可以描述不同 ViewGroup 的子类属性。例如 AbsoluteLayout 有它的子类 LayoutParams 可以用来添加 X 和 Y 坐标值。如图:
在这里插入图片描述
以下是 ViewGroup.LayoutParams 源码的注释:

基本LayoutParams类仅描述了视图的宽度和高度大小。对于每个维度,它可以指定以下其中一项:

  • FILL_PARENT(在API级别8和更高版本中重命名为MATCH_PARENT),这意味着该视图希望与其父视图一样大&#
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值