網站首頁 編程語言 正文
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來實現。這塊有機會再寫文章探討。
原文鏈接:https://blog.csdn.net/niuyongzhi/article/details/125985262
相關推薦
- 2022-11-21 小白也能看懂的Redis遍歷鍵和數據庫管理詳解_Redis
- 2022-12-04 C#目錄和文件管理操作詳解_C#教程
- 2022-01-10 設置Radio不可點擊遇到的問題
- 2022-02-20 Hive-SQL查詢連續活躍登錄用戶思路詳解_MsSql
- 2022-12-10 Android入門之ScrollView的使用教程_Android
- 2022-10-01 Android11及以上文件讀寫權限申請詳細介紹_Android
- 2023-01-31 C#實現偽裝文件夾功能_C#教程
- 2022-10-08 Python中集合創建與使用詳解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支