Android inflate解析

9572 ワード

inflateについては、みんなよく知らないと信じています.その役割はlayoutです.xmlレイアウトファイルはViewオブジェクトになります.特にListView、GridViewのAdapterでは、BaseAdapterを継承する際に書き直さなければならないいくつかのメソッドのうちgetView()メソッドがあり、このメソッドでは基本的に出現し、ListView、GridViewの各Itemのレイアウトとしてinflateメソッドを使用してレイアウトをロードします.このブログでは、Androidのinflateの過程を分析します.
inflateを使用すると、一般的には2つのクラスのメソッドを呼び出す:1 Viewの静的メソッドViewを使用する.inflate():
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root)

②LayoutInflaterのinflate()メソッドを使用し、LayoutInflaterクラスにはいくつかのリロードメソッドがあります.
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)
public View inflate(XmlPullParser parser, @Nullable ViewGroup root)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

実際、これらのメソッドの呼び出しプロセスを見てみると、これらのメソッドはいずれを呼び出してもinflate(XmlPullParser parser,@Nullable ViewGroup root,boolean attachToRoot)というメソッドが最終的に呼び出されることがわかります.
ソースの表示:
Viewのinflate()メソッド:
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
	LayoutInflater factory = LayoutInflater.from(context);
	return factory.inflate(resource, root);
}
LayoutInflaterのinflate(@LayoutRes int resource,@Nullable ViewGroup root)メソッドを呼び出します.呼び出し手順は次のとおりです.
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
	return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
	final Resources res = getContext().getResources();
	// ...   Log
	final XmlResourceParser parser = res.getLayout(resource);
	try {
		//     
		return inflate(parser, root, attachToRoot);
	} finally {
		parser.close();
	}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot){
	//   xml  ,  View  
}
別の方法:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
	return inflate(parser, root, root != null);
}
も同様に上記の方法を呼び出した.
前述したすべてのinflate()メソッドが最終的に呼び出されるLayoutInflateの1つですが、このメソッドは静的ではないので、LayoutInflateオブジェクト呼び出しを通過する必要があります.では、新しい問題があります.どのようにLayoutInflaterオブジェクトを取得しますか?Javaでオブジェクトを作成する一般的な形式と同じように直接new LayoutInflater()を作成することができますか?LayoutInflaterの構造方法を見るとprotected修飾を使用していることがわかります.したがって、私たちのコードではnewを直接使用してオブジェクトを作成することはできません.LayoutInflateオブジェクトをコードで取得するには、一般的に3つの方法があります.
public static LayoutInflater from(Context context)
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
//  Activity 
public LayoutInflater getLayoutInflater()
ActivityでgetLayoutInflater()メソッドを呼び出し、Windowに継承されたPhoneWindowのgetLayoutInflater()メソッドを呼び出し、PhoneWindowのLayoutInflaterオブジェクトの初期化はPhoneWindowの構築メソッドで、LayoutInflateを呼び出す.form(context)メソッド:
public PhoneWindow(Context context) {
	super(context);
	mLayoutInflater = LayoutInflater.from(context);
}
LayoutInflaterのfromメソッドでgetSystemService()メソッドを使用してLayoutInflateオブジェクトを取得します.
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オブジェクトは最終的にgetSystemService()メソッドによって取得される.
ソースコードの観点からinflateプロセスを分析する:
// parser:XML       ,  pull      xml  
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;
		//     ,    root  ,       result    
		View result = root;

		try {
			//        
			int type;
			while ((type = parser.next()) != XmlPullParser.START_TAG &&
					type != XmlPullParser.END_DOCUMENT) {
				// Empty
			}
			if (type != XmlPullParser.START_TAG) {
				throw new InflateException(parser.getPositionDescription()
						+ ": No start tag found!");
			}
			//          
			final String name = parser.getName();
			if (TAG_MERGE.equals(name)) {
				//      merge  root    attachToRoot false,    
				// (  merge                         ,        ,           merge  ,                        )
				if (root == null || !attachToRoot) {
					throw new InflateException(" can be used only with a valid "
							+ "ViewGroup root and attachToRoot=true");
				}

				//    root     
				rInflate(parser, root, inflaterContext, attrs, false);
			} else {
				// Temp is the root view that was found in the xml
				//   createViewFromTag()            View  ,        。        createView()  ,createView()         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);
					}
					
					// root  null ,  root      
					params = root.generateLayoutParams(attrs);
					if (!attachToRoot) {
						// root  null  attachToRoot false , root        temp
						temp.setLayoutParams(params);
					}
				}
				// ...     
				//   rInflateChildren()           ,   rInflateChildren()     rInflate()  。
				//  SDK    23        rInflate()  
				rInflateChildren(parser, temp, attrs, true);

				// ...     

				//   root  null    attachToRoot true ,  temp  root ,    temp       
				if (root != null && attachToRoot) {
					root.addView(temp, params);
				}

				//   root null   attachToRoot false,    temp   result      
				if (root == null || !attachToRoot) {
					result = temp;
				}
			}
		} 
		// ...     

		Trace.traceEnd(Trace.TRACE_TAG_VIEW);

		return result;
	}
}
コードには比較的詳細なコメントがありますが、ここではあまり説明しないで、くどくどしないでください.ソースの表示を続行します.
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
	rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}

void rInflate(XmlPullParser parser, View parent, Context context,
            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_TAG.equals(name)) {
			parseViewTag(parser, parent, attrs);
		} else if (TAG_INCLUDE.equals(name)) {
			if (parser.getDepth() == 0) {
				throw new InflateException(" cannot be the root element");
			}
			parseInclude(parser, context, parent, attrs);
		} else if (TAG_MERGE.equals(name)) {
			throw new InflateException(" must be the root element");
		} else {
			//   createViewFromTag()    View  
			final View view = createViewFromTag(parent, name, context, attrs);
			//    
			final ViewGroup viewGroup = (ViewGroup) parent;
			//       
			final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
			//     rInflateChildren()  ,     inflate
			rInflateChildren(parser, view, attrs, true);
			//    createViewFromTag()    View         
			viewGroup.addView(view, params);
		}
	}

	//       inflate,     ,  onFinishInflate()  ,                     
	if (finishInflate) {
		parent.onFinishInflate();
	}
}
これにより、レイアウトファイル全体の解析が完了し、完全なDOM構造が形成され、最終的に最上位のルートレイアウトが返され、inflate()プロセスがすべて終了します.
inflate()のrootパラメータとattachToRootパラメータの異なる値の設定をまとめます.
1.rootがnullの場合、attachToRootはtrueでもfalseでも、返されるresultはtempであり、attachToRootは意味がない(いずれもresult=temp文を歩く).2.rootがnullではなく、attachToRootがtrueに設定されている場合、ロードされたレイアウトファイルの親レイアウト、すなわちroot(root.addView(temp,params)という文を実行し、tempをrootに追加します).3.rootがnullではなく、attachToRootがfalseに設定されている場合、レイアウトファイルの最外層のすべてのlayoutプロパティが設定され、そのviewが親viewに追加されると、これらのlayoutプロパティが自動的に有効になります(temp.setLayoutParams(params)という文を実行し、最外層のプロパティをtempに設定します).4.attachToRootパラメータを設定しない場合、attachToRoot=(root!=null);rootはnull、attachToRootはfalse、rootはnull、attachToRootはtrueです.
実はこのまとめは覚えておく必要はありません.inflate(XmlPullParser parser,@Nullable View Group root,boolean attachToRoot)という方法のソースコードを見ることで簡単に手に入れることができます.
次のブログ「Android inflateインスタンス解析」では、この2つのパラメータが異なることで示す異なる効果を簡単なインスタンスで比較します.
以上の解析により,inflate法を用いてxmlファイルをViewオブジェクトに変更する際に,最外層の属性をどのように伝達すれば有効になるかが分かった.しかし、ActivityでsetContentView(layoutResID)メソッドを呼び出してActivityのレイアウトファイルを設定すると、最外層のプロパティが有効になります.setContentView(resId)メソッドの呼び出し手順を見てみましょう.
ActivityのonCreate()メソッドでsetContentView(layoutResID)メソッドを呼び出すのは実際にgetWindow()である.setContentView(layoutResID);つまりPhoneWindowのsetContentView(layoutResID)メソッドであり、PhoneWindowのこのメソッドではmLayoutInflaterが呼び出される.inflate(layoutResID, mContentParent);このコードは、mLayoutInflaterがLayoutInflaterオブジェクト、mContentParentが実はFrameLayoutオブジェクトです(『AndroidカスタムViewのActivityページの構成』というブログで説明しています).つまり、setContentView(layoutResID)メソッドを呼び出してカスタムレイアウトファイルをViewツリーに追加すると、最外層にFrameLayoutがネストされるので、Activityのレイアウトを書くときに最外層のプロパティが有効になります.