Android用于加载xml的LayoutInflater源码超详细分析

 更新时间:2022年08月26日 15:27:56   作者:niuyongzhi  
今天不想去聊一些Android的新功能,新特性之类的东西,特别想聊一聊这个老生常谈的话题:LayoutInflater,感兴趣的朋友来看看吧

1.在view的加载和绘制流程中:文章链接

我们知道,定义在layout.xml布局中的view是通过LayoutInflate加载并解析成Java中对应的View对象的。那么具体的解析过程是哪样的。

先看onCreate方法,如果我们的Activity是继承自AppCompactActivity。android是通过getDelegate返回的对象setContentView,这个mDelegate 是AppCompatDelegateImpl的实例。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 }
 //getDelegate 返回的是AppCompatDelegateImpl的实例
  public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
  }
  public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
    public static AppCompatDelegate create(@NonNull Activity activity,
            @Nullable AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, callback);
    }

在AppDelegateImpl中

  public void setContentView(int resId) {
        this.ensureSubDecor();
        //contentParent 是 系统布局文件 id 为content的view
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(android.R.id.content));
        contentParent.removeAllViews();
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);
        this.mOriginalWindowCallback.onContentChanged();
  }

resource 就是传递过来的layout资源id,系统通过XmlPullParser来解析xml。Root是上面得到的contentView。

 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
         final Resources res = getContext().getResources();
         final XmlResourceParser parser = res.getLayout(resource);
         try {
             return inflate(parser, root, attachToRoot);
         } finally {
             parser.close();
         }
  }

name 就是定义在布局文件中的控件名字,LinearLayout,TextView等,包括自定义的控件

attrs定义在控件下所有属性,包括宽高颜色背景等。

先通过createViewFromTag拿到布局文件中的root view。

再通过rInflateChildren遍历子View。

最后root.addView(temp, params);将布局文件的root view 添加到contentView中,成为它的一个子View。

 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
   final AttributeSet attrs = Xml.asAttributeSet(parser);
     final String name = parser.getName();
   //在layout.xml中找到的root view
    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
   // Create layout params that match root, if supplied
      params = root.generateLayoutParams(attrs);
    // Inflate all children under temp against its context.
    //遍历布局文件中定义的子view,将定义在xml的view转换成对应的java对象。
      rInflateChildren(parser, temp, attrs, true);
    if (root != null && attachToRoot) {
        //将layout中定义的root view 加到contentView中
         root.addView(temp, params);
    }
 }

createViewFromTag方法,通过name和attrs创建View对象。

再调用rInflateChildren 加载子View,通过循环遍历,把整个layout树转换成Java的View对象。

  final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }
//开始遍历子view
    void rInflate(XmlPullParser parser, View parent, Context context,
               AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
            .......
            final View view = createViewFromTag(parent, name, context, attrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            rInflateChildren(parser, view, attrs, true);
            viewGroup.addView(view, params);
         }
    }

createViewFromTag是创建View对象的关键方法。

有两种方式,一种是继承自AppCompactActivity,会通过factory的onCreateView创建view。

另外一种是继承自Activity,没有设置factory,或者通过factory创建view失败,则调用onCreateView方法进行创建。

 //将定义在xml的标签通过反射成对应的java对象。
     View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
                boolean ignoreThemeAttr) {
            // 当Activity继承自AppCompactActivity时,会在AppCompactActivity,onCreate时调用
            // delegate.installViewFactory()设置factory,然后调用factory的方法创建view
           View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }
            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }
            //当Activity继承自Activity时,没有设置factory时,执行下面的创建过程
			//或者通过上面的方式没有加载到View,也会调用下面的方法创建view对象。
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;
      }

先看第一种方法:调用factory的onCreateView方法,是通过调用mAppCompatViewInflater.createView创建的,根据name和attrs,直接调用View的构造函数创建的对象。创建的都是一些系统内置的view对象。

final View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs, boolean inheritContext.....){
  View view = null;
        // We need to 'inject' our tint aware Views in place of the standard versions
        switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = createButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "EditText":
                view = createEditText(context, attrs);
                verifyNotNull(view, name);
                break;
            .............
         return view;
}

再看第二种方式:通过反射进行创建。通过反射的方式,可以创建自定义的view对象。

public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs){
       Class<? extends View> clazz = null;
          clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);
          constructor = clazz.getConstructor(mConstructorSignature);
           constructor.setAccessible(true);
           //将得到的构造函数保存的map中
           sConstructorMap.put(name, constructor);
     final View view = constructor.newInstance(args);
     return view;
}

通过以上两种方式,就可以完成整个layout 的Java 对象转换。

然后就可以调用view的绘制的方法,执行view绘制流程。onlayout,onMeasure,ondraw。

app换肤的的框架可以通过设置自定义的Factory来实现。这块有机会再写文章探讨。

到此这篇关于Android用于加载xml的LayoutInflater源码超详细分析的文章就介绍到这了,更多相关Android LayoutInflater 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android后台模拟点击探索(附源码)

    Android后台模拟点击探索(附源码)

    这篇文章主要介绍了Android后台模拟点击探索(附源码),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Android性能测试关注的指标整理

    Android性能测试关注的指标整理

    在本篇文章里小编给各位整理的是关于Android性能测试关注的指标整理内容,有兴趣的朋友们学习下。
    2019-10-10
  • flutter直接上传文件到阿里云oss

    flutter直接上传文件到阿里云oss

    上传视频到oss,之前是走后端上传到oss,会有一个问题就是我要先上传给后端,后端再上传给oss就会导致上传多次,消耗时间过长影响用户体验,所以我参考文档写了直接上传到阿里云oss获取到文件访问路径。
    2021-05-05
  • Android 应用的全屏和非全屏实现代码

    Android 应用的全屏和非全屏实现代码

    这篇文章主要介绍了Android 应用的全屏和非全屏实现代码的相关资料,需要的朋友可以参考下
    2017-05-05
  • 安卓自定义流程进度图控件实例代码

    安卓自定义流程进度图控件实例代码

    本篇文章主要介绍了安卓自定义流程进度图控件实例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 阿里路由框架ARouter 源码解析之Compiler

    阿里路由框架ARouter 源码解析之Compiler

    这篇文章主要介绍了阿里路由框架ARouter 源码解析之Compiler,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • Android  回调详解及简单实例

    Android 回调详解及简单实例

    这篇文章主要介绍了Android 回调详解及简单实例的相关资料,需要的朋友可以参考下
    2017-01-01
  • Android仿QQ空间主页面的实现

    Android仿QQ空间主页面的实现

    今天模仿安卓QQ空间,打开程序的启动画面和导航页面我就不做了,大家可以模仿微信的那个做一下,很简单。这次主要做一下主页面的实现,感兴趣的朋友可以参考下
    2013-01-01
  • Android 自定义View步骤

    Android 自定义View步骤

    这篇文章主要介绍了Android 自定义View步骤 的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • 基于SQLite的Android登录APP

    基于SQLite的Android登录APP

    这篇文章主要为大家详细介绍了基于SQLite的Android登录APP,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-01-01

最新评论