Android LayoutInflater原理分析(一)
17372 ワード
転載:http://blog.csdn.net/guolin_blog/article/details/12921889
LayoutInflaterの基本的な使い方
まずLayoutInflaterのインスタンスを取得する必要があります.2つの方法で取得できます.1つ目の書き方:
2つ目の方法:
実は1つ目は2つ目の簡単な書き方で、Androidがパッケージを作ってくれただけです.
LayoutInflaterのインスタンスを取得すると、そのinflate()メソッドを呼び出してレイアウトをロードできます.inflate()は2つのパラメータのメソッドを受信します.最初のパラメータは、ロードするレイアウトid、 です.の2番目のパラメータは、レイアウトの外部に親レイアウトをネストし、必要でなければnullを直接送信することを意味します.
これにより、レイアウトのインスタンスが正常に作成され、指定した場所に追加されて表示されます.
inflate()は3つのパラメータを受信する方法で再ロードされ、構造は以下の通りである.
3番目のパラメータattachToRootはどういう意味ですか?1.rootがnullの場合、attachToRootは機能せず、値を設定しても意味がありません.2.rootがnullではなく、attachToRootがtrueに設定されている場合、ロードされたレイアウトファイルの親レイアウト、すなわちrootが指定されます.3.rootがnullではなく、attachToRootがfalseに設定されている場合、レイアウトファイルの最外層のすべてのlayoutプロパティが設定され、そのviewが親viewに追加されると、これらのlayoutプロパティが自動的に有効になります.4.attachToRootパラメータを設定しない場合、rootがnullでない場合、attachToRootパラメータのデフォルトはtrueです.
例
1、例えば現在、MainActivity対応のレイアウトファイルをactivityという項目があります.main.xml、コードは以下の通りです.
空のLinearLayout
2、レイアウトファイルを定義し、button_と名付けます.Layout.xml、コードは以下の通りです.
Buttonボタン
LayoutInflaterでbutton_layoutこのレイアウトをメインレイアウトファイルのLinearLayoutに追加しますか?
先ほど紹介した使い方により、MainActivityのコードを以下のように修正します.
分析:はLayoutInflaterのインスタンス を取得した. inflate()メソッドを呼び出してbutton_をロードlayoutこのレイアウト LinearLayoutのaddView()メソッドを呼び出してLinearLayoutに追加します.
次の図のようにプログラムを実行できます.
Buttonが画面に表示されました!私たちがLayoutInflaterを借りてbuttonを成功させたことを示しています.LayoutというレイアウトがLinearLayoutに追加されました.LayoutInflaterテクノロジーは、ScrollViewやListViewなど、動的にViewを追加する必要がある場合に広く応用されています.LayoutInflaterの姿はよく見られます.
ソース分析
ソースコードの角度からLayoutInflaterがどのように働いているのかを見てみましょう.
どのinflate()メソッドを使用しているかにかかわらず、最終的にはLayoutInflaterの次のコードに呼び出されます.
ここから見ると LayoutInflaterは、Androidが提供するpull解析方式を使用してレイアウトファイルを解析しています. ここでは23行目を見てcreateView FromTag()というメソッドを呼び出し、ノード名とパラメータを渡します.このメソッド名を見ると、ノード名に基づいてViewオブジェクトを作成するために使用されていると推測できます. 確かに、createView()メソッドの内部でcreateView()メソッドが呼び出され、反射を使用してViewのインスタンスが作成され、返されます. ここではルートレイアウトのインスタンスを作成しただけで、次に31行目にrInflate()メソッドを呼び出して、このルートレイアウトの下のサブ要素をループします.コードは以下の通りです. は21行目においても同様にcreateViewFromTag()メソッドによってViewのインスタンスを作成し、 は、24行目にrInflate()メソッドを再帰的に呼び出して、このViewの下のサブ要素を検索し、再帰が完了するたびにこのViewを親レイアウトに追加する.
これにより,レイアウトファイル全体の解析が完了すると完全なDOM構造が形成され,最終的に最上位のルートレイアウトが返され,inflate()プロセスはすべて終了する.
ボタンサイズの変更
ここではボタンの幅を300 dp、高さを80 dpに変更し、プログラムを再実行して効果を観察します.あれ?どのようにボタンは元の大きさで、何の変化もありません!ボタンがまだ大きくないので、もう少し大きくしますか?まだ役に立たない!
君がButtonのlayoutをwidthとlayout_heightの値をいくらに変更しても、この2つの値は現在完全に機能していないため、何の効果もありません.
普段はlayoutをよく使いますwidthとlayout_heightはViewのサイズを設定し、常に正常に動作します.この2つの属性は確かにViewのサイズを設定するために使用されているようです.
実際にはそうではありませんが、実際にはビューのレイアウト内のサイズを設定するために使用されます.つまり、まずビューが1つのレイアウトに存在する必要があります. layout_widthをmatch_に設定parentは、ビューの幅をレイアウト に満たすことを示す. wrap_に設定されている場合contentは、Viewの幅をコンテンツ に含めることができることを示している.特定の値に設定すると、Viewの幅は対応する値になります.
この2つの属性がlayoutと呼ばれている理由ですwidthとlayout_widthやheightではなくheight.
もう一度見てみましょうlayout.xmlバー、Buttonというコントロールは現在レイアウトに存在しないのでlayout_widthとlayout_heightの2つの属性は何の役にも立たない.
では、ボタンの大きさを変えるにはどうすればいいのでしょうか.解決策にはいろいろありますが、最も簡単な方法はButtonの外にレイアウトをネストすることです.以下に示します.
RelativeLayoutが追加されました.このときのButtonはRelativeLayoutの中に存在します.layout_widthとlayout_height属性も役に立ちます.
もちろん、最外層にあるRelativeLayout、そのlayout_widthとlayout_heightは機能を失う.
次にプログラムを再実行します.結果は下図のようにです.
ここを見ると、心の中に大きな疑問がある友达もいるかもしれません.違うよ!通常Activityでレイアウトファイルを指定する場合、最外層のレイアウトはサイズを指定できますよ、layout_widthとlayout_heightはすべて役に立ちます.確かに、これは主にsetContentView()メソッドでAndroidがレイアウトファイルの最外層にFrameLayoutを自動的にネストするのでlayout_widthとlayout_heightプロパティが効果的です.では確認してみましょう.MainActivityのコードを次のように変更します.
ここでfindView ById()メソッドでactivity_を入手しましたmainレイアウトの最外層のLinearLayoutオブジェクトを呼び出し、そのgetParent()メソッドを呼び出して親レイアウトを取得し、Logで印刷します.次にプログラムを再実行します.結果は下図のようにです.
とても正確です!LinearLayoutの親レイアウトは確かにFrameLayoutですが、このFrameLayoutはシステムによって自動的に追加されました.
ここまで言うと、setContentView()の方法はみんな使いますが、実際にAndroidインタフェースに表示される原理は私たちが見ているものよりずっと複雑です.
いずれのActivityにも表示されるインタフェースは、タイトルバーとコンテンツレイアウトの2つの部分で構成されています.タイトルバーは、多くのインタフェースの上部に表示されている内容です.例えば、私たちの例にはタイトルバーがあり、コードで表示するかどうかを制御することができます. コンテンツレイアウトはFrameLayoutです.このレイアウトのidはcontentと呼ばれています.setContentView()メソッドを呼び出すと、setContentView()ではなくsetContentView()と呼ばれます.
最後にActivityウィンドウの構成図を添付しましょう.より直感的に理解できるようにしましょう.
LayoutInflaterの基本的な使い方
まずLayoutInflaterのインスタンスを取得する必要があります.2つの方法で取得できます.1つ目の書き方:
LayoutInflater layoutInflater = LayoutInflater.from(context);
2つ目の方法:
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
実は1つ目は2つ目の簡単な書き方で、Androidがパッケージを作ってくれただけです.
LayoutInflaterのインスタンスを取得すると、そのinflate()メソッドを呼び出してレイアウトをロードできます.inflate()は2つのパラメータのメソッドを受信します.
layoutInflater.inflate(resourceId, root);
これにより、レイアウトのインスタンスが正常に作成され、指定した場所に追加されて表示されます.
inflate()は3つのパラメータを受信する方法で再ロードされ、構造は以下の通りである.
inflate(int resource, ViewGroup root, boolean attachToRoot)
3番目のパラメータattachToRootはどういう意味ですか?1.rootがnullの場合、attachToRootは機能せず、値を設定しても意味がありません.2.rootがnullではなく、attachToRootがtrueに設定されている場合、ロードされたレイアウトファイルの親レイアウト、すなわちrootが指定されます.3.rootがnullではなく、attachToRootがfalseに設定されている場合、レイアウトファイルの最外層のすべてのlayoutプロパティが設定され、そのviewが親viewに追加されると、これらのlayoutプロパティが自動的に有効になります.4.attachToRootパラメータを設定しない場合、rootがnullでない場合、attachToRootパラメータのデフォルトはtrueです.
例
1、例えば現在、MainActivity対応のレイアウトファイルをactivityという項目があります.main.xml、コードは以下の通りです.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" >
</LinearLayout>
空のLinearLayout
2、レイアウトファイルを定義し、button_と名付けます.Layout.xml、コードは以下の通りです.
<Button xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" >
</Button>
Buttonボタン
LayoutInflaterでbutton_layoutこのレイアウトをメインレイアウトファイルのLinearLayoutに追加しますか?
先ほど紹介した使い方により、MainActivityのコードを以下のように修正します.
public class MainActivity extends Activity {
private LinearLayout mainLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
LayoutInflater layoutInflater = LayoutInflater.from(this);
View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);
mainLayout.addView(buttonLayout);
}
}
分析:
次の図のようにプログラムを実行できます.
Buttonが画面に表示されました!私たちがLayoutInflaterを借りてbuttonを成功させたことを示しています.LayoutというレイアウトがLinearLayoutに追加されました.LayoutInflaterテクノロジーは、ScrollViewやListViewなど、動的にViewを追加する必要がある場合に広く応用されています.LayoutInflaterの姿はよく見られます.
ソース分析
ソースコードの角度からLayoutInflaterがどのように働いているのかを見てみましょう.
どのinflate()メソッドを使用しているかにかかわらず、最終的にはLayoutInflaterの次のコードに呼び出されます.
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
mConstructorArgs[0] = mContext;
View result = root;
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
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);
} else {
View temp = createViewFromTag(name, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
rInflate(parser, temp, attrs);
if (root != null && attachToRoot) {
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
}
return result;
}
}
ここから見ると
private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
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)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs);
viewGroup.addView(view, params);
}
}
parent.onFinishInflate();
}
これにより,レイアウトファイル全体の解析が完了すると完全なDOM構造が形成され,最終的に最上位のルートレイアウトが返され,inflate()プロセスはすべて終了する.
ボタンサイズの変更
ここではボタンの幅を300 dp、高さを80 dpに変更し、プログラムを再実行して効果を観察します.あれ?どのようにボタンは元の大きさで、何の変化もありません!ボタンがまだ大きくないので、もう少し大きくしますか?まだ役に立たない!
<Button xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="300dp" android:layout_height="80dp" android:text="Button" >
</Button>
君がButtonのlayoutをwidthとlayout_heightの値をいくらに変更しても、この2つの値は現在完全に機能していないため、何の効果もありません.
普段はlayoutをよく使いますwidthとlayout_heightはViewのサイズを設定し、常に正常に動作します.この2つの属性は確かにViewのサイズを設定するために使用されているようです.
実際にはそうではありませんが、実際にはビューのレイアウト内のサイズを設定するために使用されます.つまり、まずビューが1つのレイアウトに存在する必要があります.
この2つの属性がlayoutと呼ばれている理由ですwidthとlayout_widthやheightではなくheight.
もう一度見てみましょうlayout.xmlバー、Buttonというコントロールは現在レイアウトに存在しないのでlayout_widthとlayout_heightの2つの属性は何の役にも立たない.
では、ボタンの大きさを変えるにはどうすればいいのでしょうか.解決策にはいろいろありますが、最も簡単な方法はButtonの外にレイアウトをネストすることです.以下に示します.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:layout_width="300dp" android:layout_height="80dp" android:text="Button" >
</Button>
</RelativeLayout>
RelativeLayoutが追加されました.このときのButtonはRelativeLayoutの中に存在します.layout_widthとlayout_height属性も役に立ちます.
もちろん、最外層にあるRelativeLayout、そのlayout_widthとlayout_heightは機能を失う.
次にプログラムを再実行します.結果は下図のようにです.
ここを見ると、心の中に大きな疑問がある友达もいるかもしれません.違うよ!通常Activityでレイアウトファイルを指定する場合、最外層のレイアウトはサイズを指定できますよ、layout_widthとlayout_heightはすべて役に立ちます.確かに、これは主にsetContentView()メソッドでAndroidがレイアウトファイルの最外層にFrameLayoutを自動的にネストするのでlayout_widthとlayout_heightプロパティが効果的です.では確認してみましょう.MainActivityのコードを次のように変更します.
public class MainActivity extends Activity {
private LinearLayout mainLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
ViewParent viewParent = mainLayout.getParent();
Log.d("TAG", "the parent of mainLayout is " + viewParent);
}
}
ここでfindView ById()メソッドでactivity_を入手しましたmainレイアウトの最外層のLinearLayoutオブジェクトを呼び出し、そのgetParent()メソッドを呼び出して親レイアウトを取得し、Logで印刷します.次にプログラムを再実行します.結果は下図のようにです.
とても正確です!LinearLayoutの親レイアウトは確かにFrameLayoutですが、このFrameLayoutはシステムによって自動的に追加されました.
ここまで言うと、setContentView()の方法はみんな使いますが、実際にAndroidインタフェースに表示される原理は私たちが見ているものよりずっと複雑です.
いずれのActivityにも表示されるインタフェースは、タイトルバーとコンテンツレイアウトの2つの部分で構成されています.
最後にActivityウィンドウの構成図を添付しましょう.より直感的に理解できるようにしましょう.