【Androidソース解析】一、setContentView初探

8554 ワード

最近CSDNである大神のいくつかのソースコードの解析の文章を見て、自分でもう一度振り返って整理します.
一、ActivityのsetContentViewから
Activityは、3つのリロードのsetContentViewメソッドを提供します.
public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
}
public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
}
public void setContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().setContentView(view, params);
        initWindowDecorActionBar();
}

ActivityのsetContentView内部では、getWindowのsetContentViewメソッドが先に呼び出され、ActivityのinitWindowDecorActionBarメソッドが呼び出されていることがわかります.
二、Window類
getWindowメソッドはActivityのWindowクラスのメンバー変数mWindowを返します.ここではWindowクラスについて簡単に説明します.
  • Windowクラスは抽象クラスであり、その唯一の実装クラスはPhoneWindowである.
  • PhoneWindowには内部クラスDecorViewがあり、DecorViewはActivityのルートViewである.
  • DecorViewはFramLayoutから継承されています.

  • 三、PhoneWindowのsetContentView方法
    WindowクラスのsetContentViewメソッドは抽象的であり,PhoneWindowクラスのsetContentViewメソッドを直接見る.
    1、setContentView(int layoutResID)
    最初のメソッドが入力するパラメータは、レイアウトのリソースIDです.
    public void setContentView(int layoutResID) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                        getContext());
                transitionTo(newScene);
            } else {
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    setContentViewメソッドでは、まずメンバー変数mContentParentがnullであるか否かを判断し、最初の呼び出しであればmContentParentがnull、PhoneWindowのinstallDecorメソッドを呼び出し、mContentParentがnullでない場合はFEATURE_を設定するか否かを判断するCONTENT_TRANSITIONSのWindowプロパティ(デフォルトfalse)は、このプロパティが設定されていない場合、mContentParent内のすべてのサブViewを削除します.
    1.1.PhoneWindowクラスのinstallDecorメソッド
    ではinstallDecorメソッドでは何をしていますか?
    private void installDecor() {
            if (mDecor == null) {
                mDecor = generateDecor();
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            }
            if (mContentParent == null) {
                //         ,           ,   id content FrameLayout   mContentParent
                mContentParent = generateLayout(mDecor);
                //......
                //        
            }
        }
    

    InstallDecorメソッドでは、mContentParentを先に処理するのではなく、mDecorメンバー変数がnullであるかどうかを先に判断し、mDecorがnullである場合、generateDecorメソッドを呼び出してmDecorに値を割り当て、generateDecorのコードは簡単です.
    protected DecorView generateDecor() {
            return new DecorView(getContext(), -1);
        }
    

    つまり、DecorViewの構築方法でnewがDecorViewオブジェクトを返します.したがって、PhoneWindowのmDecorはDecorViewクラスのメンバー変数、つまりすべてのコンテンツのルートViewです.
    1.2、generateLayout方法
    mDecorはnullではありません.mContentParentがnullの場合、generateLayoutメソッドを呼び出してmContentParentを作成します.
    protected ViewGroup generateLayout(DecorView decor) {
            // Apply data from current theme.
    
            TypedArray a = getWindowStyle();
    
            //......
            //    style         
    
            // Inflate the window decor.
    
            int layoutResource;
            int features = getLocalFeatures();
            //......
            //      features              ,  layoutResource 
    
            //               DecorView   ,    contentParent 
            View in = mLayoutInflater.inflate(layoutResource, null);
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            mContentRoot = (ViewGroup) in;
    
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
            if (contentParent == null) {
                throw new RuntimeException("Window couldn't find content container view");
            }
    
            //......
            //        ,    contentParent
            return contentParent;
        }
    

    generateLayoutメソッドではまず、アプリケーショントピックstyleに基づいて値を設定して設定します.私たちが設定したandroid:themeプロパティは、ここのgetWindowStyleメソッドで取得されますが、コードでrequestWindowFeature()で設定したプロパティはgetLocalFeatureメソッドで取得されます.これも、requestWindowFeature()コードがsetContentView()の前で実行される理由です.そして設定したfeatures値に基づいて異なるウィンドウを選択してレイアウトファイルを修飾し、レイアウトファイルのlayoutResource値を得る.LayoutInflaterはレイアウトのリソースファイルをViewに解析した後、DecorViewに追加する.このViewはPhoneWindowのmContentRootメンバー変量であり、mContentParentはレイアウトファイル中のIDが@android:id/contentのFramLayoutである.setContentViewメソッドに戻り、WindowにFEATURE_が設定されていない場合CONTENT_TRANSITIONSの場合は、LayoutInflaterでレイアウトファイルをmContentParentにロードします.
    2、setContentView(Viewview)およびsetContentView(Viewview,ViewGroup.LayoutParams params)メソッド
    1つのパラメータの方法も2つのパラメータを呼び出す方法で、paramsパラメータが直接MATCH_に設定されているだけです.PARENT.
    @Override
        public void setContentView(View view) {
            setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                view.setLayoutParams(params);
                final Scene newScene = new Scene(mContentParent, view);
                transitionTo(newScene);
            } else {
                mContentParent.addView(view, params);
            }
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    パラメータがlayoutResIDの方法とは異なる点は、ViewGroupのaddViewメソッドを直接呼び出してレイアウトをmContentParentにロードすることである.Viewのロードが完了すると、両方のメソッドは最後にCallbackのonContentChangedメソッドを呼び出し、対応するActivityビューの内容が変化したことを通知します.getCallbackメソッドはWindowのmCallbackメンバー変数を返し、このメンバー変数はsetCallbackメソッドによって付与され、Activityはこのインタフェースを実現し、attachメソッドではmWindowを通過することは間違いない.設定はsetCallback(this)で行い、ActivityのonContentChangedメソッドは空のメソッドであり、Activity setContentViewまたはaddContentViewの場合に呼び出されます.
    3、ActivityのinitWindowDecorActionBar
    private void initWindowDecorActionBar() {
            Window window = getWindow();
    
            // Initializing the window decor can change window feature flags.
            // Make sure that we have the correct set before performing the test below.
            window.getDecorView();
    
            if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
                return;
            }
    
            mActionBar = new WindowDecorActionBar(this);
            mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
    
            mWindow.setDefaultIcon(mActivityInfo.getIconResource());
            mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
        }
    

    WindowのsetContentViewメソッドの実行が完了した後のinitWindowDecorActioonBarメソッドは、Actionbarを作成し、デフォルトの表示を設定するなどします.Activity転送setContentViewメソッド自体にレイアウトは表示されません.Activityの開始は実際にActivity Threadのmainメソッドです.ActivityがActivity Threadのmainメソッドを転送した後、Activity ThreadクラスperformLaunchActivityを呼び出して起動するActivityコンポーネントを作成し、Activityコンポーネントを作成する過程で、Activityコンポーネントのウィンドウオブジェクトとビューオブジェクトも作成されます.次にActivityコンポーネントの作成が完了したら、Activity ThreadクラスのhandleResumeActivityを呼び出してアクティブにします.handlerResumeActivityでActivityを呼び出すmakeVisibleメソッドには、setContentViewで作成したmDecorビューファミリーが表示されます.
    void makeVisible() {
            if (!mWindowAdded) {
                ViewManager wm = getWindowManager();
                wm.addView(mDecor, getWindow().getAttributes());
                mWindowAdded = true;
            }
            mDecor.setVisibility(View.VISIBLE);
        }
    

    参考:AndroidアプリケーションsetContentViewとLayoutInflaterロード解析メカニズムソースコード分析