Android用于加载xml的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 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android LayoutInflater.inflate源码分析

    LayoutInflater.inflate源码详解 LayoutInflater的inflate方法相信大家都不陌生,在Fragment的onCreateView中或者在BaseAdapter的getView方法中我们都会经常用这个方法来实例化出我们需要的View. 假设我们有一个需要实例化的布局文件menu_item.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" an

  • Android中使用LayoutInflater要注意的一些坑

    前言 在平时的开发过程中,我们经常会用LayoutInflater这个类,比如说在Fragment$onCreateView和RecyclerView.Adapter$onCreateViewHolder中都会用到.它的用法也无非就是LayoutInflater.inflate(resourceId, root, attachToRoot),第一个参数没什么好说的,但第二个和第三个参数结合起来会带来一定的迷惑性.之前有时候会发现界面布局上出了一些问题,查了很久之后偶然的改动了这两个参数,发现问题

  • Android布局加载之LayoutInflater示例详解

    前言 Activity 在界面创建时需要将 XML 布局文件中的内容加载进来,正如我们在 ListView 或者 RecyclerView 中需要将 Item 的布局加载进来一样,都是使用 LayoutInflater 来进行操作的. LayoutInflater 实例的获取有多种方式,但最终是通过(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)来得到的,也就是说加载布局的 LayoutInflat

  • Android LayoutInflater深入分析及应用

    LayoutInflater解析 前言: 在Android中,如果是初级玩家,很可能对LayoutInflater不太熟悉,或许只是在Fragment的onCreateView()中模式化的使用过而已.但如果稍微有些工作经验的人就知道,这个类有多么重要,它是连接布局XMl和Java代码的桥梁,我们常常疑惑,为什么Android支持在XML书写布局? 我们想到的必然是Android内部帮我们解析xml文件,LayoutInflater就是帮我们做了这个工作. 首先LayoutInflater是一个

  • Android 老生常谈LayoutInflater的新认知

    现在看我文章的多数是一些老Android了,相信每个人使用起LayoutInflater都是家常便饭,信手拈来. 但即使是这样,我仍然觉得这个知识点有可以分析的地方,看完之后或许你对LayoutInflater又会有一些新的认识. 首先概括一下LayoutInflater是用来做什么的. 我们都知道,在开发Android应用程序的时候,编写布局基本都是通过xml文件来编写的.当然你也完全可以在代码中纯手写布局,但是写过的人都清楚,这样编写布局会非常麻烦. 那么通过xml编写的布局文件是如何转换成

  • Android开发中LayoutInflater用法详解

    本文实例讲述了Android开发中LayoutInflater用法.分享给大家供大家参考,具体如下: 在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById().不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化:而findViewById()是找xml布局文件下的具体widget控件(如Button.TextView等). 具体作用: 1.对于一个没有被载入或者想要动态载入的界面,都需要使用Layout

  • Android LayoutInflater加载布局详解及实例代码

    Android  LayoutInflater加载布局详解 对于有一定Android开发经验的同学来说,一定使用过LayoutInflater.inflater()来加载布局文件,但并不一定去深究过它的原理,比如 1.LayoutInflater为什么可以加载layout文件? 2.加载layout文件之后,又是怎么变成供我们使用的View的? 3.我们定义View的时候,如果需要在布局中使用,则必须实现带AttributeSet参数的构造方法,这又是为什么呢? 既然在这篇文章提出来,那说明这三

  • 基于Android LayoutInflater的使用介绍

    在android中,LayoutInflater有点类似于Activity的findViewById(id),不同的是LayoutInflater是用来找layout下的xml布局文件,并且实例化!而findViewById()是找具体xml下的具体 widget控件(如:Button,TextView等). 下面通过一个例子进行详细说明: 1.在res/layout文件夹下,添加一个xml文件dialog.xml 复制代码 代码如下: <LinearLayout xmlns:android=&qu

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

    1.在view的加载和绘制流程中:文章链接 我们知道,定义在layout.xml布局中的view是通过LayoutInflate加载并解析成Java中对应的View对象的.那么具体的解析过程是哪样的. 先看onCreate方法,如果我们的Activity是继承自AppCompactActivity.android是通过getDelegate返回的对象setContentView,这个mDelegate 是AppCompatDelegateImpl的实例. @Override protected

  • Java CopyOnWriteArrayList源码超详细分析

    目录 一.概述 二.类图 三.核心方法 1.add() 2.set() 3.remove() 4.get() 5.size() 四.总结 一.概述 CopyOnWriteArrayList是基于写时复制技术实现的,适用于读多写少场景下的线程安全的并发容器.读操作永远不会加锁,读读.读写都不会冲突,只有写写需要等待.写操作时,为了不影响其它线程的读取,它会进行一次自我复制,待数据写入完成后再替换array数组.array数组是被volatile修饰的,它被修改后可以被其他线程立刻发现. publi

  • Android图片加载利器之Picasso源码解析

    看到了这里,相信大家对Picasso的使用已经比较熟悉了,本篇博客中将从基本的用法着手,逐步的深入了解其设计原理. Picasso的代码量在众多的开源框架中算得上非常少的一个了,一共只有35个class文件,但是麻雀虽小,五脏俱全.好了下面跟随我的脚步,出发了. 基本用法 Picasso.with(this).load(imageUrl).into(imageView); with(this)方法 public static Picasso with(Context context) { if

  • pytorch加载自己的数据集源码分享

    目录 一.标准的数据集流程梳理 数据来源 二.实现加载自己的数据集 1. 保存在txt文件中(生成训练集和测试集,其实这里的训练集以及测试集也都是用文本文件的形式保存下来的) 2. 在继承dataset类LoadData的三个函数里调用train.txt以及test.txt实现相关功能 三.源码 一.标准的数据集流程梳理 分为几个步骤数据准备以及加载数据库–>数据加载器的调用或者设计–>批量调用进行训练或者其他作用 数据来源 直接读取了x和y的数据变量,对比后面的就从把对应的路径写进了文本文件

  • Android实现基于滑动的SQLite数据分页加载技术(附demo源码下载)

    本文实例讲述了Android实现基于滑动的SQLite数据分页加载技术.分享给大家供大家参考,具体如下: main.xml如下: <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_settings" android:orderInCategory="100" android:showAs

  • Android 2.3 拨号上网流程从源码角度进行分析

    通常,如果我们想使用SIM卡拨号上网功能,我们要在设置中进行简单的配置,步骤如下: 设置 ->无线和网络 ->移动网络 ->(已启用数据/数据漫游/接入点名称/仅使用2G网络/网络运营商) 我们必须选中其中的"已启用数据"选项,然后配置接入点名称后就可以上网了,当然有的设置中已经根据你的SIM卡类型默认设置了接入点,这时候你只选择"已启用数据"项后就可以完成上网功能设置. 这些设置步骤究竟做了哪些事情呢?我们现在就从源码的角度进行分析. 1. 首先

  • Android图片加载利器之Picasso基本用法

    今天开始我们来学习一下Picasso,计划包括以下几方面的内容: 图片加载利器之Picasso进阶 图片加载利器之Picasso源码解析 目前市场上比较流行的图片加载框架主要有UniversalImageLoader,Picasso,Glide,Fresco. 下面简单介绍一下这几个框架: UniversalImageLoader:这个可以说是非常非常经典的一个了,相信每个app的开发人员都使用过,只可惜作者已经停止该项目的维护了,所以不太推荐使用. Picasso:是Square公司出品的图片

  • Android 动态加载 so实现示例详解

    目录 背景 so动态加载介绍 从一个例子出发 so库检索与删除 动态加载so 结束了吗? ELF文件 扩展 总结 背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到native的需求,所以so库的动态化可以减少极大的包体积,自从2020腾讯的bugly团队发部关于动态化so的相关文章后,已经过去两年了,相关文章,经过两年的考验,实际上so动态加载也是非常成熟的一项技术了. 但是很遗憾,许多公司都还没有这方面的涉略又或者说不知

  • Android 资源加载使用伪代码示例分析

    目录 引言 Resource是什么? 基础概念 getText getDrawable 小结 Tips getColor 总结 引言 聊到 Android 的 资源加载 ,每一个开发同学都会非常熟悉,毕竟从使用来说,我们日常都会见到,比如 getText() 等等. 那如果此时问你,你知道 它们到底是怎么被加载的,内部会有什么处理吗? 为什么同一个drawable界面更改了透明度,其他界面也会生效? 如果你对上述问题依然存疑,那本文可能会对你有所帮助. 介于此,本篇将由浅入深,从源头理清 Res

随机推荐