Android LayoutInflater原理分析(一)

17372 ワード

転載:http://blog.csdn.net/guolin_blog/article/details/12921889
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); 
  • 最初のパラメータは、ロードするレイアウトid、
  • です.
  • の2番目のパラメータは、レイアウトの外部に親レイアウトをネストし、必要でなければnullを直接送信することを意味します.

  • これにより、レイアウトのインスタンスが正常に作成され、指定した場所に追加されて表示されます.
    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);  
        }  
    }  

    分析:
  • はLayoutInflaterのインスタンス
  • を取得した.
  • inflate()メソッドを呼び出してbutton_をロードlayoutこのレイアウト
  • LinearLayoutのaddView()メソッドを呼び出してLinearLayoutに追加します.

  • 次の図のようにプログラムを実行できます.
    Android LayoutInflater原理分析(一)_第1张图片
    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;  
        }  
    }  

    ここから見ると
  • LayoutInflaterは、Androidが提供するpull解析方式を使用してレイアウトファイルを解析しています.
  • ここでは23行目を見てcreateView FromTag()というメソッドを呼び出し、ノード名とパラメータを渡します.このメソッド名を見ると、ノード名に基づいてViewオブジェクトを作成するために使用されていると推測できます.
  • 確かに、createView()メソッドの内部でcreateView()メソッドが呼び出され、反射を使用してViewのインスタンスが作成され、返されます.
  • ここではルートレイアウトのインスタンスを作成しただけで、次に31行目にrInflate()メソッドを呼び出して、このルートレイアウトの下のサブ要素をループします.コードは以下の通りです.
  • 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();  
    }  
  • は21行目においても同様にcreateViewFromTag()メソッドによってViewのインスタンスを作成し、
  • は、24行目にrInflate()メソッドを再帰的に呼び出して、このViewの下のサブ要素を検索し、再帰が完了するたびにこのViewを親レイアウトに追加する.

  • これにより,レイアウトファイル全体の解析が完了すると完全な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つのレイアウトに存在する必要があります.
  • 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 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は機能を失う.
    次にプログラムを再実行します.結果は下図のようにAndroid LayoutInflater原理分析(一)_第2张图片です.
    ここを見ると、心の中に大きな疑問がある友达もいるかもしれません.違うよ!通常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つの部分で構成されています.
  • タイトルバーは、多くのインタフェースの上部に表示されている内容です.例えば、私たちの例にはタイトルバーがあり、コードで表示するかどうかを制御することができます.
  • コンテンツレイアウトはFrameLayoutです.このレイアウトのidはcontentと呼ばれています.setContentView()メソッドを呼び出すと、setContentView()ではなくsetContentView()と呼ばれます.

  • 最後にActivityウィンドウの構成図を添付しましょう.より直感的に理解できるようにしましょう.Android LayoutInflater原理分析(一)_第3张图片