日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

LayoutInflater的工作原理,從解析XML布局文件到創(chuàng)建Java對(duì)象,再到構(gòu)建View樹(shù)

來(lái)源: 責(zé)編: 時(shí)間:2024-05-07 09:07:11 161觀看
導(dǎo)讀LayoutInflater在Android中是一個(gè)非常重要的組件,主要負(fù)責(zé)將XML布局文件實(shí)例化為對(duì)應(yīng)的View對(duì)象。LayoutInflater是一個(gè)抽象類,不能直接通過(guò)new的方式獲取其實(shí)例,需要通過(guò)Activity.getLayoutInflater()或Context.getSyst

LayoutInflater在Android中是一個(gè)非常重要的組件,主要負(fù)責(zé)將XML布局文件實(shí)例化為對(duì)應(yīng)的View對(duì)象。LayoutInflater是一個(gè)抽象類,不能直接通過(guò)new的方式獲取其實(shí)例,需要通過(guò)Activity.getLayoutInflater()或Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)來(lái)獲取與當(dāng)前Context已經(jīng)關(guān)聯(lián)且正確配置的標(biāo)準(zhǔn)LayoutInflater。WJc28資訊網(wǎng)——每日最新資訊28at.com

在實(shí)際工作中,有時(shí)會(huì)根據(jù)情況在代碼中自定義控件或者加載布局文件,這就需要用到LayoutInflater。它的作用是用來(lái)獲得布局文件View對(duì)象的。例如,在BaseAdapter的getView方法中,LayoutInflater經(jīng)常被用來(lái)獲取整個(gè)View并返回。WJc28資訊網(wǎng)——每日最新資訊28at.com

View itemView= LayoutInflater.from(context).inflate(R.layout.layout_list_item,container,false);

通過(guò)LayoutInflater.from靜態(tài)函數(shù)獲得一個(gè)LayoutInflater實(shí)例,其實(shí)是個(gè)PhoneLayoutInflater對(duì)象:WJc28資訊網(wǎng)——每日最新資訊28at.com

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

LayoutInflater服務(wù)

LAYOUT_INFLATER_SERVICE服務(wù)跟AMS、WMS等服務(wù)不同,完全是APP虛擬的一個(gè)服務(wù),主要作用是:在本地為調(diào)用者創(chuàng)建PhoneLayoutInflater對(duì)象,ContextImpl在注冊(cè)這個(gè)“服務(wù)”的時(shí)候,將工作委托給PolicyManager,利用makeNewLayoutInflater構(gòu)建LayoutInflater。WJc28資訊網(wǎng)——每日最新資訊28at.com

registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {    public Object createService(ContextImpl ctx) {        return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());    }});    public static LayoutInflater makeNewLayoutInflater(Context context) {    return sPolicy.makeNewLayoutInflater(context);}

PolicyManager進(jìn)一步調(diào)用com.android.internal.policy.impl.Policy對(duì)象的makeNewLayoutInflater構(gòu)建PhoneLayoutInflater。WJc28資訊網(wǎng)——每日最新資訊28at.com

private static final String POLICY_IMPL_CLASS_NAME ="com.android.internal.policy.impl.Policy";public LayoutInflater makeNewLayoutInflater(Context context) {    return new PhoneLayoutInflater(context);}

圖片圖片WJc28資訊網(wǎng)——每日最新資訊28at.com

構(gòu)建View樹(shù)

  1. 「讀取 XML 布局文件」:LayoutInflater 讀取 XML 布局文件。XML 文件描述了 UI 的層次結(jié)構(gòu)和視圖組件的屬性。
  2. 「解析 XML」:LayoutInflater 使用 XML 解析器(如 XmlPullParser)來(lái)解析 XML 文件。遍歷 XML 文件中的每一個(gè)元素(通常是一個(gè)視圖組件,如 TextView、Button 等)和屬性。
  3. 「創(chuàng)建 Java 對(duì)象」:LayoutInflater 使用反射來(lái)創(chuàng)建對(duì)應(yīng)的 Java 對(duì)象。例如,如果 XML 中有一個(gè) TextView 元素,LayoutInflater 就會(huì)創(chuàng)建一個(gè) TextView 的實(shí)例。并根據(jù) XML 中的屬性來(lái)設(shè)置實(shí)例的初始狀態(tài)。
  4. 「構(gòu)建 View 樹(shù)」: 隨著 XML 的解析和 Java 對(duì)象的創(chuàng)建,LayoutInflater 會(huì)將這些對(duì)象組織成一個(gè)樹(shù)形結(jié)構(gòu),即 View 樹(shù)。樹(shù)形結(jié)構(gòu)反映了 XML 布局文件中定義的 UI 層次結(jié)構(gòu)。
  5. 「處理布局參數(shù)」: 如果 inflate 方法被調(diào)用時(shí)傳入了父 ViewGroup,并且第三個(gè)參數(shù)為 true,LayoutInflater 會(huì)自動(dòng)為新創(chuàng)建的視圖設(shè)置布局參數(shù)(LayoutParams)。參數(shù)通常與父 ViewGroup 的類型相關(guān),以確保視圖能夠正確地在其父容器中布局。
  6. 「返回根視圖」:LayoutInflater 返回構(gòu)建好的 View 樹(shù)的根視圖。根視圖可以被添加到任何 ViewGroup 中,以顯示在 UI 上。

LayoutInflater源碼中按照上面的流程來(lái)構(gòu)建View,同時(shí)添加了些特殊標(biāo)簽的處理邏輯,比如merge、include、stubview等。WJc28資訊網(wǎng)——每日最新資訊28at.com

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

XmlResourceParser是包含了XML文件信息的一個(gè)對(duì)象,通過(guò)XmlResourceParser將TAG信息取出,遞歸創(chuàng)建View。WJc28資訊網(wǎng)——每日最新資訊28at.com

public XmlResourceParser getLayout(int id) throws NotFoundException {    return loadXmlResourceParser(id, "layout");}
XmlResourceParser loadXmlResourceParser(int id, String type)        throws NotFoundException {    synchronized (mAccessLock) {        TypedValue value = mTmpValue;        <!--獲取一個(gè)TypedValue-->        if (value == null) {            mTmpValue = value = new TypedValue();        }        <!--利用id 查詢layout,并填充TypedValue-->        getValue(id, value, true);        <!--根據(jù)布局文件的路徑,返回解析xml文件-->        if (value.type == TypedValue.TYPE_STRING) {            return loadXmlResourceParser(value.string.toString(), id,                    value.assetCookie, type);        }    }}

TypedValue是與xml定義的資源對(duì)應(yīng)的值,getValue獲取對(duì)應(yīng)xml資源:WJc28資訊網(wǎng)——每日最新資訊28at.com

public void getValue(int id, TypedValue outValue, boolean resolveRefs)        throws NotFoundException {    boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);}

mAssets是一個(gè)AssetManager對(duì)象:WJc28資訊網(wǎng)——每日最新資訊28at.com

final boolean getResourceValue(int ident,int density, TypedValue outValue, boolean resolveRefs) {   <!--加載資源-->    int block = loadResourceValue(ident, (short) density, outValue, resolveRefs);    if (block >= 0) {        if (outValue.type != TypedValue.TYPE_STRING) {            return true;        }        outValue.string = mStringBlocks[block].get(outValue.data);        return true;     }      return false;  }

AssetManager通過(guò)native函數(shù)加載xml文件信息:WJc28資訊網(wǎng)——每日最新資訊28at.com

static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, jint ident,jshort density,jobject outValue,jboolean resolve){    ...<!--獲取native AssetManager對(duì)象-->    AssetManager* am = assetManagerForJavaObject(env, clazz);    <!--獲取ResTable資源表,這里應(yīng)該有緩存 不能每次都弄一次吧? 所有資源的唯一表嗎?-->    const ResTable& res(am->getResources());    Res_value value;    ResTable_config config;    uint32_t typeSpecFlags;    <!--通過(guò)ResTable獲取資源-->    ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);   ...    uint32_t ref = ident;    if (resolve) {    <!--是否需要二次解析資源-->        block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);    ...    }    return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config) : block;}

res.getResource并不是是每次都加載一遍,第一次加載后就能獲得單例ResTable,后面用的都是這個(gè)緩存,只不過(guò)ResTable不會(huì)緩存全部資源,對(duì)于布局、圖像資源等,緩存的都是引用,如果是真實(shí)資源的引用話,還需要通過(guò)res.resolveReference來(lái)解析真正的資源。WJc28資訊網(wǎng)——每日最新資訊28at.com

const ResTable* AssetManager::getResTable(bool required) const{    <!--緩存 ResTable,如果非空直接返回-->    ResTable* rt = mResources;    if (rt) {  return rt;   }   ...<!--多個(gè)apk的話,會(huì)有多個(gè)-->    const size_t N = mAssetPaths.size();    for (size_t i=0; i<N; i++) {        Asset* ass = NULL;        ResTable* sharedRes = NULL;        bool shared = true;        <!--找到Asset的路徑-->        const asset_path& ap = mAssetPaths.itemAt(i);        Asset* idmap = openIdmapLocked(ap);        <!--這里的路徑一般都不是目錄-->        if (ap.type != kFileTypeDirectory) {                   if (i == 0) {                  <!--第一個(gè)一般是框架層的系統(tǒng)資源,用的較多,不想每次都解析,需要緩存-->                sharedRes = const_cast<AssetManager*>(this)->mZipSet.getZipResourceTable(ap.path);            }            if (sharedRes == NULL) {                ass = const_cast<AssetManager*>(this)->mZipSet.getZipResourceTableAsset(ap.path);                if (ass == NULL) {                <!--打開(kāi)resources.arsc文件-->                    ass = const_cast<AssetManager*>(this)->openNonAssetInPathLocked("resources.arsc",  Asset::ACCESS_BUFFER,  ap);                    if (ass != NULL && ass != kExcludedAsset) {                        ass = const_cast<AssetManager*>(this)->mZipSet.setZipResourceTableAsset(ap.path, ass);                    }}                if (i == 0 && ass != NULL) {                    <!--緩存第一個(gè)asset-->                    sharedRes = new ResTable();                    sharedRes->add(ass, (void*)(i+1), false, idmap);                    sharedRes = const_cast<AssetManager*>(this)->mZipSet.setZipResourceTable(ap.path, sharedRes);                } } }         ...                 if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {            if (rt == NULL) {                mResources = rt = new ResTable();                updateResourceParamsLocked();            }            if (sharedRes != NULL) {                rt->add(sharedRes);            } else {                rt->add(ass, (void*)(i+1), !shared, idmap);            }  }  .. }    return rt;}

通過(guò)上面的操作,完成了resources.arsc文件的解析,獲得了一個(gè)ResTable對(duì)象,該對(duì)象包含了應(yīng)用程序的全部資源信息(動(dòng)態(tài)加載的先不考慮),之后就可以通過(guò)ResTable的getResource來(lái)獲得指定資源,而對(duì)于xml布局文件,這里獲得的就是一個(gè)引用,需要res.resolveReference二次解析,之后就得到了id對(duì)應(yīng)的資源項(xiàng)。xml布局文件對(duì)應(yīng)的資源項(xiàng)的值是一個(gè)字符串,其實(shí)是一個(gè)布局文件路徑,指向一個(gè)經(jīng)過(guò)編譯的二進(jìn)制格式保存的xml資源文件。有了這個(gè)Xml資源文件的路徑之后,會(huì)再次通過(guò)loadXmlResourceParser來(lái)對(duì)該Xml資源文件進(jìn)行解析,從而得到布局文件解析對(duì)象XmlResourceParser。WJc28資訊網(wǎng)——每日最新資訊28at.com

XmlResourceParser loadXmlResourceParser(String file, int id,    int assetCookie, String type) throws NotFoundException {    if (id != 0) {        try {...                  <!--解析xml文件-->            XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);            if (block != null) {                int pos = mLastCachedXmlBlockIndex+1;                if (pos >= num) pos = 0;                mLastCachedXmlBlockIndex = pos;                XmlBlock oldBlock = mCachedXmlBlocks[pos];                if (oldBlock != null) {                    oldBlock.close();                }                <!--緩存-->                mCachedXmlBlockIds[pos] = id;                mCachedXmlBlocks[pos] = block;                <!--返回-->                return block.newParser();         ...

返回XmlResourceParser對(duì)象,進(jìn)而來(lái)實(shí)例化各種View:WJc28資訊網(wǎng)——每日最新資訊28at.com

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {    synchronized (mConstructorArgs) {        final AttributeSet attrs = Xml.asAttributeSet(parser);        Context lastContext = (Context)mConstructorArgs[0];        mConstructorArgs[0] = mContext;        View result = root;        try {            int type;            final String name = parser.getName();            <!--Merge標(biāo)簽的根布局不能直接用LayoutInflater進(jìn)行inflate-->            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, attrs, false);            } else {                View temp;                if (TAG_1995.equals(name)) {                    temp = new BlinkLayout(mContext, attrs);                } else {                <!--利用tag創(chuàng)建View-->                    temp = createViewFromTag(root, name, attrs);                }                ViewGroup.LayoutParams params = null;                if (root != null) {                    <!--是否有container來(lái)輔助,或者添加到container中,或者輔助生成布局參數(shù)-->                    params = root.generateLayoutParams(attrs);                    if (!attachToRoot) {                        temp.setLayoutParams(params);                    }                }                <!--如果有必要,遞歸生成子View,并添加到temp容器中-->                rInflate(parser, temp, attrs, true);                    <!--是否需要添加到root的container容器總-->                if (root != null && attachToRoot) {                    root.addView(temp, params);                }                <!--如果不添加root中,返回結(jié)果就是infate出的根布局View,否則就是root根布局-->                if (root == null || !attachToRoot) {                    result = temp;                }            }        }         return result;     }}

inflate的主要作用是生成layout的根布局文件,并且根據(jù)參數(shù)看看是否需要添加container容器中,之后根據(jù)需要調(diào)用rInflate遞歸生成子View。WJc28資訊網(wǎng)——每日最新資訊28at.com

void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {    final int depth = parser.getDepth();    int type;    <!--遞歸解析-->    while (((type = parser.next()) != XmlPullParser.END_TAG ||            parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {        if (type != XmlPullParser.START_TAG) {            continue;        }        final String name = parser.getName();        if (TAG_REQUEST_FOCUS.equals(name)) {            parseRequestFocus(parser, parent);        } else if (TAG_INCLUDE.equals(name)) {            // inclue標(biāo)簽,不能用在getDepth() == 0            if (parser.getDepth() == 0) {                throw new InflateException("<include /> cannot be the root element");            }            parseInclude(parser, parent, attrs);        } else if (TAG_MERGE.equals(name)) {            <!--merge標(biāo)簽必須是布局的根元素,因此merge使用方式一定是被inclue-->            throw new InflateException("<merge /> must be the root element");        } else if (TAG_1995.equals(name)) {            final View view = new BlinkLayout(mContext, attrs);            final ViewGroup viewGroup = (ViewGroup) parent;            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);            rInflate(parser, view, attrs, true);            viewGroup.addView(view, params);                        } else {            <!--創(chuàng)建View,如果有必要,接著遞歸-->            final View view = createViewFromTag(parent, name, attrs);            final ViewGroup viewGroup = (ViewGroup) parent;            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);            rInflate(parser, view, attrs, true);            <!--添加View-->            viewGroup.addView(view, params);        }    }    if (finishInflate) parent.onFinishInflate();}

rInflate主要作用是開(kāi)啟遞歸遍歷,生成View樹(shù),createViewFromTag的主要作用是利用反射生成View對(duì)象,最終將View數(shù)顯示到屏幕上。WJc28資訊網(wǎng)——每日最新資訊28at.com

WJc28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-86982-0.htmlLayoutInflater的工作原理,從解析XML布局文件到創(chuàng)建Java對(duì)象,再到構(gòu)建View樹(shù)

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 基于Spring Boot 3.x與Flowable的順序會(huì)簽?zāi)J綄?shí)踐

下一篇: @Async注解失效的 9 種場(chǎng)景

標(biāo)簽:
  • 熱門焦點(diǎn)
  • 俄羅斯:將審查iPhone等外國(guó)公司設(shè)備 保數(shù)據(jù)安全

    iPhone和特斯拉都屬于在各自領(lǐng)域領(lǐng)頭羊的品牌,推出的產(chǎn)品也也都是數(shù)一數(shù)二的,但對(duì)于一些國(guó)家而言,它們的產(chǎn)品可靠性和安全性還是在限制范圍內(nèi)。近日,俄羅斯聯(lián)邦通信、信息技術(shù)
  • 一加首款折疊屏!一加Open渲染圖出爐:罕見(jiàn)單手可握小尺寸

    8月5日消息,此前就有爆料稱,一加首款折疊屏手機(jī)將會(huì)在第三季度上市,如今隨著時(shí)間臨近,新機(jī)的各種消息也開(kāi)始浮出水面。據(jù)悉,這款新機(jī)將會(huì)被命名為&ldquo;On
  • K6:面向開(kāi)發(fā)人員的現(xiàn)代負(fù)載測(cè)試工具

    K6 是一個(gè)開(kāi)源負(fù)載測(cè)試工具,可以輕松編寫、運(yùn)行和分析性能測(cè)試。它建立在 Go 和 JavaScript 之上,它被設(shè)計(jì)為功能強(qiáng)大、可擴(kuò)展且易于使用。k6 可用于測(cè)試各種應(yīng)用程序,包括 Web
  • 從 Pulsar Client 的原理到它的監(jiān)控面板

    背景前段時(shí)間業(yè)務(wù)團(tuán)隊(duì)偶爾會(huì)碰到一些 Pulsar 使用的問(wèn)題,比如消息阻塞不消費(fèi)了、生產(chǎn)者消息發(fā)送緩慢等各種問(wèn)題。雖然我們有個(gè)監(jiān)控頁(yè)面可以根據(jù) topic 維度查看他的發(fā)送狀態(tài),
  • 如何使用JavaScript創(chuàng)建一只圖像放大鏡?

    譯者 | 布加迪審校 | 重樓如果您曾經(jīng)瀏覽過(guò)購(gòu)物網(wǎng)站,可能遇到過(guò)圖像放大功能。它可以讓您放大圖像的特定區(qū)域,以便瀏覽。結(jié)合這個(gè)小小的重要功能可以大大改善您網(wǎng)站的用戶體驗(yàn)
  • 騰訊蓋樓,字節(jié)拆墻

    來(lái)源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之&ldquo;想重溫暴刷深淵、30+技能搭配暴搓到爽的游戲體驗(yàn)嗎?一起上晶核,即刻暴打!&rdquo;曾憑借直播騰訊旗下代理格斗游戲《DNF》一
  • 自律,給不了Keep自由!

    來(lái)源 | 互聯(lián)網(wǎng)品牌官作者 | 李大為編排 | 又耳 審核 | 谷曉輝自律能不能給用戶自由暫時(shí)不好說(shuō),但大概率不能給Keep自由。近日,全球最大的在線健身平臺(tái)Keep正式登陸港交所,努力
  • OPPO K11搭載長(zhǎng)壽版100W超級(jí)閃充:26分鐘充滿100%

    據(jù)此前官方宣布,OPPO將于7月25日也就是今天下午14:30舉辦新品發(fā)布會(huì),屆時(shí)全新的OPPO K11將正式與大家見(jiàn)面,將主打旗艦影像,和同檔位競(jìng)品相比,其最大的賣
  • 滴滴違法違規(guī)被罰80.26億 共存在16項(xiàng)違法事實(shí)

    滴滴違法違規(guī)被罰80.26億 存在16項(xiàng)違法事實(shí)開(kāi)始于2121年7月,歷經(jīng)一年時(shí)間,網(wǎng)絡(luò)安全審查辦公室對(duì)“滴滴出行”網(wǎng)絡(luò)安全審查終于有了一個(gè)暫時(shí)的結(jié)束。據(jù)“網(wǎng)信
Top 主站蜘蛛池模板: 太白县| 眉山市| 台东市| 叙永县| 嫩江县| 阜新| 汉沽区| 原阳县| 资兴市| 大英县| 宝山区| 卢龙县| 新化县| 昆山市| 汉源县| 胶南市| 刚察县| 庐江县| 怀来县| 寻乌县| 贡嘎县| 乳源| 宣化县| 蓬莱市| 祁东县| 南溪县| 定日县| 新邵县| 巴彦县| 石台县| 峨山| 白玉县| 蕲春县| 随州市| 天津市| 遂溪县| 乌兰县| 江陵县| 沁源县| 麻江县| 通州区|