レイアウト階層の秘密を減らすためのマージタグのソースコード分析(Android Q)
私は「Androidレンダリングパフォーマンスの最適化-あなたが知る必要があるすべて!「この記事でも説明されているように、Androidのレイアウトを最適化するためにレベルビューツリーを減らすために使用されるタグをマージします。
レイアウトでマージタグを使用して、不要なレベルを減らします。
使用するシーン
マージタグはレイアウトレベルを最適化できるので、その主な使用シナリオは何ですか?
- マージの最も一般的な使用法は、includeタグを使用することです。このように、レイアウトを再利用する場合、冗長なレイアウトのネストは追加されず、includeタグのみに起因する問題が解決されます。
- さらに、結合されたビューをカスタマイズする場合(たとえば、FrameLayout、LinearLayoutなどから継承する場合)、通常、カスタムレイアウトを作成し、インデックスIDを使用してカスタムビューに追加します。現時点では、マージタグがそうでない場合使用すると、非表示になります。ネストのレベルが追加されます。
たとえば、コンテナビューをカスタマイズしました。クラス名は、LinearLayoutから継承されたTestLayoutです。このカスタムコントロールの塗りつぶしレイアウトでは、マージタグを使用できます。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</merge>
TestLayoutはLinearLayoutを継承するため、それ自体がLinearLayoutです。レイアウトのマージをLinearLayoutに変更すると、2つのLinearLayoutネストが発生し、そのうちの1つは冗長になります。
タグの原則とソースコード分析をマージする
では、マージタグのレベルを下げる原則は何ですか?
その原理を知るための最も直接的な方法は、Androidのソースコードを確認することです。次に、ソースコードを通してその実装原理を分析しましょう。
前回の記事「AndroidQLayoutInflaterレイアウト生成ビューソースコード詳細説明」の分析を読むと、Androidレイアウトによって生成されたビューオブジェクトがLayoutInflaterサービスを介して処理され、LayoutInflaterのinflateメソッドが責任を負っていることがわかります。レイアウトを解析し、Viewオブジェクトを生成します。マージタグの処理はこのプロセスに含まれています。LayoutInflaterのinflateメソッドを見てみましょう。
LayoutInflaterのinflateメソッド
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
……
final AttributeSet attrs = Xml.asAttributeSet(parser);
……
try {
……
//如果根标签是 merge 标签
if (TAG_MERGE.equals(name)) {
//如果根节点是 merge,并且 root 是 null 或者 attachToRoot == false,则抛出异常。
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//遍历布局并生成View
rInflate(parser, root, inflaterContext, attrs, false);
} else {
//非 merge 跟标签,则创建根标签所代表的 View 对象。
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
//获取根标签 View 对象的视图属性,这里会使用根标签所设置的属性来生成属性对象。
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
//对根标签设置它的属性
temp.setLayoutParams(params);
}
}
……
//以根标签对象为根,递归遍历它的 xml 子视图。
rInflateChildren(parser, temp, attrs, true);
……
}
}
}
}
ルートタグとしてマージタグを使用するレイアウトファイルの場合:
- マージタグのレイアウトファイルがビューとして生成される場合、rootが存在し、attachToRootがtrueである必要があります。そうでない場合、InflateExceptionがスローされます。
- 次に、rInflateメソッドを呼び出して、レイアウト内のサブラベルをトラバースし、ビューを生成します。
- ここでは、root(ビューツリー上のレイアウトファイルの親ビュー)とattrsがrInflateメソッドに渡され、レイアウトファイルのサブビューが作成されます。
ルートタグとして非マージタグを使用するレイアウトファイルの場合:
- ルートラベルで表されるViewオブジェクトを作成します。マージはビューコンポーネントではないため、これはマージタグでは作成されません。
- ルートラベルビューオブジェクトのビュープロパティを取得します。ここでは、ルートラベルによって設定されたプロパティを使用してプロパティオブジェクトを生成し、そのプロパティをルートラベルに設定します。これもマージタグ中には実行されないため、マージタグに設定された属性は有効になりません。
- ルートタグオブジェクトをルートとして取得し、そのxmlサブビューを再帰的にトラバースします。タグをマージするとき、マージタグのサブタグをトラバースするためのルートとして(パラメーターとして)ルートを取ります。これがマージタグの背後にある秘密です!
LayoutInflaterのRInflateメソッド
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 String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
……
}
…… ……
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
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);
}
}
……
}
このメソッドの目的は、xmlレイアウトファイルのルートタグのサブタグで表されるViewコンポーネントオブジェクトをトラバースして作成することです。
- rInflateメソッドは、xmlレイアウトファイルのコンテンツを再帰的に解析し、ビューを作成して、それを親に追加します。
- ここで、マージタグはレイアウトファイルのルートノードでなければならないことに注意してください。
さて、ここで、私たちはすでにマージタグの秘密を知っています。
総括する
要約しましょう:
-
マージタグを使用して、ビューツリーのレベルを下げ、Androidのレイアウトを最適化できます。
-
Androidレイアウトによって生成されたビューオブジェクトは、LayoutInflaterサービスを介して処理されます。LayoutInflaterのinflateメソッドは、レイアウトの解析とViewオブジェクトの生成を担当します。
-
マージタグを使用する場合、マージタグはレイアウトファイルのルートノードである必要があります。
-
マージタグのレイアウトファイルがビューとして生成される場合、rootが存在し、attachToRootがtrueである必要があります。そうでない場合、InflateExceptionがスローされます。
-
マージタグに設定された属性が無効です!
-
レイアウトファイルを解析する場合、通常の状況では、ルートタグオブジェクトがルートとして使用され、そのxmlサブビューが再帰的にトラバースされます。マージタグを使用する場合、ルートは(パラメーターとして)ルートであり、rInflateメソッドに渡されます。サブビューを作成します。これがマージタグの背後にある秘密です!
PS:その他の分析記事については、一連の記事-> 「Androidの基本原則の分析」列を確認してください。
PS:その他の分析記事については、一連の記事-> 「Androidの基本原則の分析」列を確認してください。
PS:その他の分析記事については、一連の記事-> 「Androidの基本原則の分析」列を確認してください。