LayoutInflater之inflate()简单分析

Android  源码分析  2024年10月27日 am8:08发布3周前更新 91es.com站长
31 0 0

前言

简单记录一下AndroidLayoutInflaterinflate()的使用以及源码的简单跟踪。

记录于此,方便自己查阅。

正文

LayoutInflater初始化

对于LayoutInflater,我们并不陌生,经常使用。

//方法1
LayoutInflater inflater_1 = getLayoutInflater(); // 调用Activity的getLayoutInflater()
//方法2
LayoutInflater inflater_2 = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//方法3
LayoutInflater inflater_3 = LayoutInflater.from(mContext);

尤其是方法3,写ListView,RecyclerView等的适配器时加载Item_layout.xml时。

上面初始化LayoutInflater效果都一样,下面简单跟踪一下。

Activity.java
frameworks\base\core\java\android\app\Activity.java
@NonNull
public LayoutInflater getLayoutInflater() {
    return getWindow().getLayoutInflater();
}
public Window getWindow() {
    return mWindow;
}

这里的mWindow是PhoneWindow对象。

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, ActivityConfigCallback activityConfigCallback) {
    attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    //初始化mWindow
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    //略
}
PhoneWindow.java
public PhoneWindow(Context context) {
    super(context);
    mLayoutInflater = LayoutInflater.from(context);
}

public PhoneWindow(Context context, Window preservedWindow,
        ActivityConfigCallback activityConfigCallback) {
    this(context);
    //略
}
@Override
public LayoutInflater getLayoutInflater() {
    return mLayoutInflater;
}

从这里可以看到,方法的获取mLayoutInflater跟方法3一样,只不是是在PhoneWindow中初始化。

接着看

mLayoutInflater = LayoutInflater.from(context);
LayoutInflater.java
\frameworks\base\core\java\android\view\LayoutInflater.java
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;
}

哈哈哈,这个不是方法2么。

小结
  1. 方法1---》方法3 ---》方法2,(---》表示调用关系)

  2. 三种方法都是一样的效果,最终都是通过getSystemService()获取

inflate()介绍

在LayoutInflater中inflate()有多个,参数不一样。一共有三个,不过最终都调用到第三个。

第一个
 public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
    //如果root不为null,就attachToRoot
     return inflate(parser, root, root != null);
 }
第二个
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    final XmlResourceParser parser = res.getLayout(resource);
    try {
        //是否attachToRoot,要看参数attachToRoot
        return inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}
第三个
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 {
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
            }
            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }
            final String name = parser.getName();
            //是否有merge
            if (TAG_MERGE.equals(name)) {
                if (root == null || !attachToRoot) {
                    throw new InflateException("<merge /> can be used only with a valid "
                            + "ViewGroup root and attachToRoot=true");
                }
                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                //一般都走这
                //创建临时View
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                ViewGroup.LayoutParams params = null;
                //[重点]如果root不为null,才会走这
                if (root != null) {
                    params = root.generateLayoutParams(attrs);
                    //[重点]如果attachToRoot为false,表示不附加,仅仅设置LayoutParams
                    if (!attachToRoot) {
                        temp.setLayoutParams(params);
                    }
                }
                rInflateChildren(parser, temp, attrs, true);
                //如果attachToRoot为true,把temp添加到root中。
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }
                 //root为null时,返回temp
                //attachToRoot为false时,返回时临时创建的view,否则返回root
                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 {
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        return result;
    }
}

重点关注root是否为null和attachToRoot是true还是false。

分下面3种情况

情况1

root为null时,返回的result是temp

情况2

root不为null,attachToRoot为true时,返回的是root

View result = root;

params = root.generateLayoutParams(attrs);

//attachToRoot为true
//附加,就添加到root中
if (root != null && attachToRoot) {
    root.addView(temp, params);
}
情况3

root不为null,attachToRoot为false时,返回的是temp

params = root.generateLayoutParams(attrs);

//attachToRoot = false
//不附加,直接设置params
if (!attachToRoot) {
    temp.setLayoutParams(params);
}

result = temp;
小结
  1. Fragment和ListView或RecyclerVIew的适配器中,不需要附加到root中(root不为null,且attachToRoot为false)

推荐使用

LayoutInflater.from(MediaApp.getContext()).inflate(R.layout.layout_item_media, parent, false)

当然也可以使用

LayoutInflater.from(MediaApp.getContext()).inflate(R.layout.layout_item_media, null)

但容易出行《Android ListView子item高度定长固定值无效问题详解》这个问题,因为LayoutParams没有被应用到temp中。

  1. Activity中setContentView()是需要附加到root中(root不为null,且attachToRoot为true)

Activity的setContentView()最终调用的是PhoneWindow的。

 #PhoneWindow.java中
 public void setContentView(int layoutResID) {
     //略
    mLayoutInflater.inflate(layoutResID, mContentParent);
     //略
 }

参考文章

  1. Android ListView子item高度定长固定值无效问题详解

  2. Android中获得 LayoutInflater 实例的三种方式

 历史上的今天

  1. 2022: [代码片段]Activity手动设置全屏和非全屏(0条评论)
  2. 2021: 裴多菲:我愿意是激流(0条评论)
  3. 2019: 胡适:赠与今年的大学毕业生(0条评论)
  4. 2017: 自定义View和自定义属性(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

Android Socket之服务端封装

前言之前记录了Android中Socket的简单使用,也就是对数据的简单收发操作。这简单的对SocketServer进行封装。记录于此,方便自己查阅。正文SocketServer服务端一般不需要我们写,大多数是作为客户端去绑定其他的,比如Tbox。但服务端跟客户端差不多,因此也一起整理一下...

[摘]音视频学习系列第(三)篇---wav文件的存储和解析

什么是wavwav是一种无损的音频文件格式,wav文件有两部分,第一部分是文件头,记录一些重要的参数信息,如音频的采样率,通道数,数据位宽,第二部分是数据部分,数据部分可以是PCM,也可以是其它的编码格式的数据为什么要将音频存储wav格式存储为该格式,音乐播放器可以通过读取wav头,识别出它...

Android换肤之Toast和Dialog

前言前面介绍过Android静态换肤日夜模式的切换(原文:《Android静态换肤-日夜主题切换之继承Activity记录 》),从中我们知道,Toast和Dialog存在无法切换成功。原因是Toast或者Dialog是需要时才去初始化的,此时换肤插件没法获取到View。PS:日夜模式不存在...

JNI动态注册封装C语言版

前言在上一篇《JNI调用Java方法》的动态注册并调用Java方法,上次也说了可以把公共部分封装一下。说干就干,今天就把上次代码封装一下。正文我这不介绍完整的动态注册,有需要可以看《JNI动态注册》。这里只是个人流水账。Hello.javapackage com.biumall.dyn...

余秋雨:什么是文化?

关于文化的几个“傻问题”文化很重要,这很少有人否认,但是,大家往往躲开了一个起点性的问题,那就是 ——文化到底是什么?文化的定义是什么?对于文化,我们心里一定早就储藏着大量疑问。而且,随着时间的推移,疑问越来越多。普遍人心的最初疑问最重要,但也最难回答。按照民间说法,这样的问题可称为“傻问题...

AudioTrack简单使用

前言之前也记录过简单记录过AudioTrack的使用,但太久了,而且部分接口已经废弃。今天有空,重新记录一下。主要是提供够自己看的。正文这里主要是播放raw中pcm文件的代码片段,分别做了MODE_STREAM和MODE_STATIC两种方式。MODE_STATIC隐藏内容!付费...