返回

深入剖析 Android LayoutInflater.inflate() 源码,解密其强大功能

Android

LayoutInflater.inflate():深入解析Android布局解析利器

概要

LayoutInflater.inflate() 是一个在 Android 开发中广泛应用的方法,用于将 XML 布局文件解析为视图对象,实现动态加载布局。虽然我们经常使用它,但对它的内部机制和特定实现可能了解不多。本文将深入剖析 LayoutInflater.inflate() 的源代码,揭示其强大功能和各个参数的含义。

源码分析

LayoutInflater.inflate() 方法位于 android.view.LayoutInflater 类中,接受三个参数:

  1. 要解析的 XML 布局资源 ID
  2. 将解析到的视图添加到哪个父视图中
  3. 是否将解析后的视图添加到父视图中
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
    synchronized (this) {
        View result = createView(mFactory, null, resource, root, attachToRoot,
                false /* dedustPendingInflatedViews */);
        if (result != null) {
            // The LayoutInflater took care of filling in onCreate/onFinishInflate
            return result;
        }
        return mInflater.inflate(resource, root, attachToRoot);
    }
}

从源码中可以看出,LayoutInflater.inflate() 方法首先调用 createView() 方法,负责创建视图对象。如果 createView() 方法返回 null,说明无法创建视图对象,这时 LayoutInflater.inflate() 方法会调用 mInflater.inflate() 方法,这是系统默认的 LayoutInflater 实现,将使用默认解析器解析 XML 布局文件。

createView() 方法的实现较为复杂,会根据不同情况调用不同方法创建视图对象。在大多数情况下,createView() 方法会调用 onCreateView() 方法创建视图对象。onCreateView() 方法会根据 XML 布局文件的根节点类型创建视图对象。比如,如果根节点是 <LinearLayout>,则会调用 createViewForLinearLayout() 方法创建视图对象。

private View onCreateView(Factory parent, String name, Context context,
        AttributeSet attrs, boolean ignoreId) {
    final String[] prefixList = mFactory2 != null ? mFactory2.getPrefixList() : null;
    View view = createViewFromTag(context, name, attrs, ignoreId, prefixList);
    if (view == null) {
        view = createViewFromTag(context, name, attrs, ignoreId,
                LayoutInflater.Factory2.TAG_PREFIX_DEFAULT);
    }
    return view;
}

createViewFromTag() 方法根据给定的标签名、属性集和前缀列表创建视图对象。首先检查前缀列表是否包含给定的标签名,如果有,则使用前缀列表中的前缀创建视图对象。如果没有,则使用默认前缀创建视图对象。

private View createViewFromTag(Context context, String name, AttributeSet attrs,
        boolean ignoreId, String[] prefixList) {
    // Note: the createView flow is split up into two methods to allow
    // creating views in both the context's and the layout's classloader.

    String[] parseResult = parseLayoutInflaterTag(name);
    final String viewName = parseResult[0];
    String prefix = parseResult[1];

    // If no prefix is specified, use the default.
    if (prefix == null) {
        prefix = "android.widget.";
    }

    View view = null;

    try {
        if (-1 == viewName.indexOf('.')) {
            view = createView(context, prefix, viewName, attrs, ignoreId,
                    false /*throwOnUnknownTag*/);
        } else {
            view = createView(context, viewName, attrs, ignoreId,
                    false /*throwOnUnknownTag*/);
        }
    } catch (ClassNotFoundException e) {
        // This can happen if the inflated XML file contains a tag
        // that is not in the context's classpath. We only want to
        // log this if the context actually has a classloader.
        ClassNotFoundException rethrow = new ClassNotFoundException(
                attrs.getPositionDescription()
                + ": Error inflating class " + prefix + viewName, e);
        if (context.getClassLoader() == null) {
            Slog.e(TAG, "The context must have a classloader set to"
                  + " inflate class " + prefix + viewName, rethrow);
        } else {
            throw rethrow;
        }
    }

    return view;
}

createView() 方法根据给定的类名、属性集和是否抛出未知标签异常创建视图对象。首先检查给定的类名是否包含点号,如果有,则使用给定的类名创建视图对象。如果没有,则使用给定的前缀和类名创建视图对象。

总结

通过对 LayoutInflater.inflate() 源代码的分析,我们发现其功能非常强大。它不仅可以解析 XML 布局文件,还可以根据不同情况创建不同类型的视图对象。同时,LayoutInflater.inflate() 方法还可以通过 attachToRoot 参数控制是否将解析后的视图添加到父视图中。

希望本文能帮助读者更深入地理解 Android 布局解析过程,更有效地使用 LayoutInflater.inflate() 进行布局解析。

常见问题解答

  1. LayoutInflater.inflate() 方法是如何创建视图对象的?

    • LayoutInflater.inflate() 方法首先会调用 createView() 方法,根据 XML 布局文件的根节点类型调用不同的方法创建视图对象。
  2. createView() 方法是如何创建视图对象的?

    • createView() 方法会根据给定的类名和属性集创建视图对象。如果给定的类名包含点号,则会使用给定的类名创建视图对象。如果没有,则会使用给定的前缀和类名创建视图对象。
  3. LayoutInflater.inflate() 方法中的 attachToRoot 参数有什么作用?

    • attachToRoot 参数控制是否将解析后的视图添加到父视图中。如果为 true,则会将解析后的视图添加到父视图中。如果为 false,则不会将解析后的视图添加到父视图中。
  4. LayoutInflater.inflate() 方法的适用场景有哪些?

    • LayoutInflater.inflate() 方法主要用于动态加载布局。比如,在 Fragment 中加载布局或在 Activity 中动态添加控件时。
  5. 如何提高 LayoutInflater.inflate() 方法的性能?

    • 避免在频繁更新的 UI 中使用 LayoutInflater.inflate() 方法。可以考虑使用 ViewBinding 或 ButterKnife 等库来提高性能。