Android用于加載xml的LayoutInflater源碼超詳細(xì)分析
1.在view的加載和繪制流程中:文章鏈接
我們知道,定義在layout.xml布局中的view是通過(guò)LayoutInflate加載并解析成Java中對(duì)應(yīng)的View對(duì)象的。那么具體的解析過(guò)程是哪樣的。
先看onCreate方法,如果我們的Activity是繼承自AppCompactActivity。android是通過(guò)getDelegate返回的對(duì)象setContentView,這個(gè)mDelegate 是AppCompatDelegateImpl的實(shí)例。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } //getDelegate 返回的是AppCompatDelegateImpl的實(shí)例 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 是 系統(tǒng)布局文件 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 就是傳遞過(guò)來(lái)的layout資源id,系統(tǒng)通過(guò)XmlPullParser來(lái)解析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定義在控件下所有屬性,包括寬高顏色背景等。
先通過(guò)createViewFromTag拿到布局文件中的root view。
再通過(guò)rInflateChildren遍歷子View。
最后root.addView(temp, params);將布局文件的root view 添加到contentView中,成為它的一個(gè)子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轉(zhuǎn)換成對(duì)應(yīng)的java對(duì)象。 rInflateChildren(parser, temp, attrs, true); if (root != null && attachToRoot) { //將layout中定義的root view 加到contentView中 root.addView(temp, params); } }
createViewFromTag方法,通過(guò)name和attrs創(chuàng)建View對(duì)象。
再調(diào)用rInflateChildren 加載子View,通過(guò)循環(huán)遍歷,把整個(gè)layout樹(shù)轉(zhuǎn)換成Java的View對(duì)象。
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { rInflate(parser, parent, parent.getContext(), attrs, finishInflate); } //開(kāi)始遍歷子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是創(chuàng)建View對(duì)象的關(guān)鍵方法。
有兩種方式,一種是繼承自AppCompactActivity,會(huì)通過(guò)factory的onCreateView創(chuàng)建view。
另外一種是繼承自Activity,沒(méi)有設(shè)置factory,或者通過(guò)factory創(chuàng)建view失敗,則調(diào)用onCreateView方法進(jìn)行創(chuàng)建。
//將定義在xml的標(biāo)簽通過(guò)反射成對(duì)應(yīng)的java對(duì)象。 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { // 當(dāng)Activity繼承自AppCompactActivity時(shí),會(huì)在AppCompactActivity,onCreate時(shí)調(diào)用 // delegate.installViewFactory()設(shè)置factory,然后調(diào)用factory的方法創(chuàng)建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); } //當(dāng)Activity繼承自Activity時(shí),沒(méi)有設(shè)置factory時(shí),執(zhí)行下面的創(chuàng)建過(guò)程 //或者通過(guò)上面的方式?jīng)]有加載到View,也會(huì)調(diào)用下面的方法創(chuàng)建view對(duì)象。 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; }
先看第一種方法:調(diào)用factory的onCreateView方法,是通過(guò)調(diào)用mAppCompatViewInflater.createView創(chuàng)建的,根據(jù)name和attrs,直接調(diào)用View的構(gòu)造函數(shù)創(chuàng)建的對(duì)象。創(chuàng)建的都是一些系統(tǒng)內(nèi)置的view對(duì)象。
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; }
再看第二種方式:通過(guò)反射進(jìn)行創(chuàng)建。通過(guò)反射的方式,可以創(chuàng)建自定義的view對(duì)象。
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); //將得到的構(gòu)造函數(shù)保存的map中 sConstructorMap.put(name, constructor); final View view = constructor.newInstance(args); return view; }
通過(guò)以上兩種方式,就可以完成整個(gè)layout 的Java 對(duì)象轉(zhuǎn)換。
然后就可以調(diào)用view的繪制的方法,執(zhí)行view繪制流程。onlayout,onMeasure,ondraw。
app換膚的的框架可以通過(guò)設(shè)置自定義的Factory來(lái)實(shí)現(xiàn)。這塊有機(jī)會(huì)再寫(xiě)文章探討。
到此這篇關(guān)于Android用于加載xml的LayoutInflater源碼超詳細(xì)分析的文章就介紹到這了,更多相關(guān)Android LayoutInflater 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 監(jiān)聽(tīng)網(wǎng)絡(luò)狀態(tài)方法詳解
這篇文章主要介紹了Android 監(jiān)聽(tīng)網(wǎng)絡(luò)狀態(tài)方法詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07Android開(kāi)發(fā)之PopupWindow實(shí)現(xiàn)彈窗效果
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)之PopupWindow實(shí)現(xiàn)彈窗效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09使用android studio導(dǎo)入模塊的兩種方法(超詳細(xì))
這篇文章主要介紹了使用android studio導(dǎo)入模塊的兩種方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09Android開(kāi)發(fā)實(shí)現(xiàn)可拖動(dòng)排序的ListView功能【附源碼下載】
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)可拖動(dòng)排序的ListView功能,結(jié)合實(shí)例形式分析了Android列表拖動(dòng)排序原理與相關(guān)操作技巧,并附帶完整源碼供讀者下載參考,需要的朋友可以參考下2017-11-11Android筆記之:App應(yīng)用之啟動(dòng)界面SplashActivity的使用
當(dāng)前比較成熟一點(diǎn)的應(yīng)用基本上都會(huì)在進(jìn)入應(yīng)用之顯示一個(gè)啟動(dòng)界面.這個(gè)啟動(dòng)界面或簡(jiǎn)單,或復(fù)雜,或簡(jiǎn)陋,或華麗,用意不同,風(fēng)格也不同2013-04-04android自定義gradle插件并且發(fā)布到本地倉(cāng)庫(kù)詳細(xì)教程
這篇文章主要介紹了android自定義gradle插件并且發(fā)布到本地倉(cāng)庫(kù)詳細(xì)教程的相關(guān)資料,需要的朋友可以參考下2023-07-07android中SharedPreferences實(shí)現(xiàn)存儲(chǔ)用戶(hù)名功能
本篇文章主要介紹了android中SharedPreferences實(shí)現(xiàn)保存用戶(hù)名功能,詳細(xì)的介紹了SharedPreferences的功能,需要的朋友可以參考下2017-04-04android中圖片翻頁(yè)效果簡(jiǎn)單的實(shí)現(xiàn)方法
android中圖片翻頁(yè)效果簡(jiǎn)單的實(shí)現(xiàn)方法,需要的朋友可以參考一下2013-05-05Android超清晰6.0權(quán)限申請(qǐng)AndPermission
這篇文章主要介紹了Android超清晰6.0權(quán)限申請(qǐng)AndPermission,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11Android WebView實(shí)現(xiàn)文件下載功能
這篇文章主要為大家詳細(xì)介紹了Android WebView實(shí)現(xiàn)文件下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05