Androidのビュー描画とイベント配信の流れ(下段)

14306 ワード

他人の労働成果を尊重してください.勝手に盗作しないでください.転載は明記してください.ありがとうございます.
転載は出典を明記してください.http://blog.csdn.net/evan_マン/アート/detail/521999517
    本論文では、ActivityのSetContentView方法の下層部はどのように私達のlayout.xmlファイルを処理していますか?そして、WindowManagerServiceからWiewのdispatTouchEvent方法に事件がどのように伝えられているかを簡単に分析してみます.
ActivityでViewを作成する過程は大体次の通りです.
  • Activity対応の呼び出しを行いました.
  • はその後、set ContentViewを実行する.実際に呼び出したPhone Window.set ContentView方法
  • です.
  • は、DecorView、View、またはView Groupオブジェクト
  • を作成する.
  • OResumeメソッドを呼び出しました.Activityのmake Visibleを呼び出す方法があります.
  • WindowManagerを取得し、そのaddView方法を呼び出して、WindowManager Serviceにビューを渡して管理します.
  • View RootとW
  • を作成します.
  • WinowManagerがWmSのリモートインターフェースを呼び出して完了しました.画面
  • にウィンドウを追加しました.
  • フロントビューのset Visibilityメソッドを呼び出します.この方法は最終的にはView RootのperformTravels方法にジャンプします.参考メモ『View—重絵』
  • set ContentViewの方法の内容は以下の通りです.
    [email protected]
    public void setContentView(@LayoutRes int layoutResID) {
            mWindow.setContentView(layoutResID);
            initWindowDecorActionBar(); // ActionBar       
    }
    private Window mWindow = new PhoneWindow(this);
    プロットの下の詳細を分析するなら、次のいくつかの方法から開始します.
        1、comp.android.internal.policy.impl.Phone Window
    のsetContintViewとget Decorviewの2つの方法を参照してください.
        2、
    android.view.WindowManager
    のaddView方法を探究します.
        3、View Rootの作成、表示、イベントの配布
    ビューの描画
    Phone Window.class (comp.android.internal.policy.impl.Phone Window)
    public class PhoneWindow extends Window 
    private DecorView mDecor;  //DecorView PhoneWindow     private final class DecorView extends FrameLayout
    private ViewGroup mContentParent;
    private LayoutInflater mLayoutInflater;
    
    
    public PhoneWindow(Context context) {
            super(context);
            mLayoutInflater = LayoutInflater.from(context);
    }
    set ContentView(int)@Phone Window.class
    @Override
    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); //note1
            }
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
    }
    1、xmlファイルを解析しながらparentをmConteent Parentとする
    set ContentView(View) )@Phone Window.class
    @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();  //note1
            } 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); //noe1
            }
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
    }
    1、DecorWindowとContentPartentを作成する
    2、contentViewをmConteent Partに追加する
    installDecor()@PhoneWindow.class
    private void installDecor() {
            if (mDecor == null) { //  DecorWindow
                mDecor = generateDecor();  
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            }
            if (mContentParent == null) { //  mContentParent                     
                mContentParent = generateLayout(mDecor); //  DecorWindow                parent ViewGroup
                // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
                mDecor.makeOptionalFitsSystemWindows();
                final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                        R.id.decor_content_parent);
                .........
            }
    }
    generateDecor()@PhoneWindow.class
    protected DecorView generateDecor() {
       return new DecorView(getContext(), -1);
    }
    generateLayout()@PhoneWindow.class
    protected ViewGroup generateLayout(DecorView decor) {
            // Apply data from current theme.
            ....//     DecorWindow       WrapContent
            int layoutResource;
            int features = getLocalFeatures();
            ....//  feature       
            View in = mLayoutInflater.inflate(layoutResource, null);  //                 View,     、ActionBar、     View 
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            mContentRoot = (ViewGroup) in;
    
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //note1
            if (contentParent == null) {
                throw new RuntimeException("Window couldn't find content container view");
            }
            ......
            return contentParent;
    }
    1、
    View Group content Parter=(View Group)decor.findView ById(IDuANDROIDu CONTENT)に相当します.
    public static final int IDuANDROIDENT=comp.android.internal.R.id.com nt.mDecor.findViewByIdに相当する.
    comp.android.internal.R.id.com nt
    を選択します
    取得したのは、ユーザがViewをカスタマイズしたレイアウトファイルの親レイアウトです.
    get Decorview()@Phone Window.class
    @Override
    public final View getDecorView() {
            if (mDecor == null) {
                installDecor();
            }
            return mDecor;
    }
    WindowManager.class
    WindowManagerがよく使うのは3つの方法addView、udateView、removeViewがあります.
    WindowManagerのaddViewメソッドの下の階
  • は、Viewに基づいてView Rootを作成し、View Rootの内部にはWクラスが含まれています.
  • Wは、IWindow.Stubインターフェースを実現したView Rootの内部クラスであり、効果はView Rootを呼び出す同名の方法であり、WMSとView Rootの間の通信は明らかにWに通過している.
  • 、ViewRootからWMSへの通信は、IWindowSession=IWindowManager.Stub.asInterface( ServiceManager.get Service(「window」).openSession(imm.get Cient(),imm.getInputContext(); 
  • WでもIWindowSessionでも、彼らの最下層はBinder通信方式によって実現されます.
  • ViewはAndroidビューの表現ですが、Viewは単独では存在しません.Windowという抽象的な概念に依存しなければなりません.一つのPhone WindowはViewに対応しています.一つのviewはView RootとWindowManager.LayoutParamに対応しています.
    View Rootはビュー描画の制御とイベントの処理を行うために、Window Manager.LayoutParaamsはWMSの現在のWindowが画面のどの位置にあるかを知らせるために使用されます.ここでWindow Manager.LayoutParaamsのflagsとtypeパラメータが重要です.
    WindowManager.LayoutParaams.class
    WindowManager.LayoutParams extends ViewGroup.LayoutParams  implements Parcelable{
    public int x;
    //   X  ,           ;     gravity LEFT、RIGHT    x   
    public int y;
    //   Y  ,           ;     gravity BOTTOM、TOP    y   
    public int flags;
    public int type;
    .....
    }
    FlaggasパラメータはWindowの属性を表し、Flagsパラメータの値を変更することによってWindowの表示特性を制御します.
  • FLAG_NOTUFOCUSABLE:入力イベントを受け付けないことを示しています.このフラグは同時にFLAG_NOTKUMODALを起動します.入力イベントは下の階のフォーカスWindow
  • に伝達されます.
  • FLAG_NOTUTOUCH_MODAL:現在Windowエリア以外のクリックイベントは下のWindowに伝達されています.現在Windowエリア内のクリックイベントは自分で処理します.一般的にはこのマークをオンにしないと、下のWindowはイベントを取得できません.
  • FLAG_SHOW_WHEN_uLOCKED:このWindowをスクリーンロックの画面上に表示させることができるようにする.
    TypeパラメータはWindowのタイプを表しています.Windowには三つのタイプがあります.
  • アプリケーションWindow:つまり、私たちの普通のActivityに対応するView
  • です.
  • 子Window:父Windowに依存する必要があります.dialog
  • のように単独では存在できません.
  • システムWindow:Toastとシステムステータスバー
  • など、ステートメント権限が必要です.
    Windowは階層的で、対応するz-ordedが大きいほど前に表示され、階層が大きいほど、階層が小さいWindowをカバーします. 具体的には次のとおりです 
    //  Window 1~99
    FIRST_APPLICATION_WINDOW = 1;
    TYPE_BASE_APPLICATION   = 1;
    TYPE_APPLICATION        = 2;
    TYPE_APPLICATION_STARTING = 3;
    LAST_APPLICATION_WINDOW = 99;
    
    // Window 1000~1999
    FIRST_SUB_WINDOW = 1000;
    TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
    TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
    TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
    TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
    TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
    TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
    LAST_SUB_WINDOW = 1999;
    
    //  Window 2000~2999
    FIRST_SYSTEM_WINDOW     = 2000;
    TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
    TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
    TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
    TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
    TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
    TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
    TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
    TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
    TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
    TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
    TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;  //  AndroidManifest   
    TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
    TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
    TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
    TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
    TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
    TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
    TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
    TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
    TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
    TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
    TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
    TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
    TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
    TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
    TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
    TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
    TYPE_KEYGUARD_SCRIM           = FIRST_SYSTEM_WINDOW+29;
    TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
    TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
    TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
    TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
    LAST_SYSTEM_WINDOW      = 2999;
    イベントの配布
    WindowManagerのaddViewメソッドの下の階はView RootとWクラスを作成します.
    1、WはIWindow.Stubインターフェースを実現したView Rootの内部クラスであり、効果はView Rootを呼び出す同名の方法であり、WMSとView Rootの間の通信は明らかにWを通じている.
    2、ViewRootからWMSへの通信はIWindowSession swindowSession=IWindowManager.Stub.asInterface( 
    ServiceManager.get Service(「window」)
    openSession(imm.get Client()、imm.get Input Contect(); 
    3、Wであろうと、IWindowSessionであろうと、彼らの最下層はBinder通信機構によって実現されます.
    addViewのパラメータはmDecorWindowですので、View Rootの作成はこのmDecorviewに基づいています.View RootのmViewドメインに対応しています.そのため、mDecorWindowは最初にユーザがクリックしたイベントを受信したViewです.下で全体の流れを分析します.
    まずView RootのhandleMessage方法から見ます.
    handle Message()@View Root.class
    public void  handleMessage(Message msg) {
        case DISPATCH_POINTER: {
               MotionEvent event = (MotionEvent) msg.obj;
                try {
                    deliverPointerEvent(event);
               } finally {
                  event.recycle();
                    if (msg.arg1 != 0) {
                      finishInputEvent();
                   }
                   if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
               }
           } break;
    }
    DISPATCHINTERイベントはユーザーのクリックイベントに対応しています.ViewRootのdeliverPointメソッドを呼び出します.
    deliverPointerEvent()@View Root.lass
    private void deliverPointerEvent(MotionEvent event){
        ....
        handled = mView.dispatchTouchEvent(event);
        ....
    }
    View RootのdeliverPoint EventメソッドはmViewのdispatch TouchEventメソッドを呼び出します.
    dispatch TouchEvent()@DecorWindow.class
    public boolean dispatchTouchEvent(MotionEvent ev) {
                final Callback cb = getCallback(); //note1
                return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) //note2
                        : super.dispatchTouchEvent(ev);
    }
    1、Phone WindowのgetCallbackメソッドを呼び出してActivityがAttach方法で導入したCallbackオブジェクトを取得し、Activityがこのインターフェースを実現しました.
    2、cb.dispatch TouchEvent(ev)はActivityのdispatch TouchEventを呼び出す方法です.
    @Window.class
    public void setCallback(Callback callback) {
          mCallback = callback;
    }
    @Window.class
    public final Callback More ...getCallback() {
            return mCallback;
    }
    disp
    atch Touch Event()@Activity.lass
    public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
            if (getWindow().superDispatchTouchEvent(ev)) { //note1
                return true;
            }
            return onTouchEvent(ev); //note2
    }
    1、Phone Windowを呼び出す
    super Displatch TouchEvent()方法
    2、自分のワンタッチイベントを呼び出す方法
    upler Displatch TouchEvent()@Phone Window.class
    public boolean uperDispatchTouchEvent(MotionEvent event) {
          return mDecor.superDispatchTouchEvent(event);  
     }
    //呼び出しmDecorDiscpatch TouchEvent方法
    upler Displatch Touch
    Event()@DecorWindow.class
    public boolean  superDispatchTouchEvent(MotionEvent event) {
             return super.dispatchTouchEvent(event);  
     }
    //親タイプのFraameLayoutを呼び出すdispatch TouchEventメソッド
    上記の簡単な分析により、WMSからのイベント(W転送によるW変換イベントは非同期イベント)をまずView Rootが受信したという結論が分かります.その後、ViewRootは、Handlerでイベントを処理します.ユーザのタッチスクリーン情報などのイベントの多くは、直接的なサブビュー、すなわちDecorWindowオブジェクトに任せることが多いです.DecorWindowのdispatch TouchEvent方法を例にとって、この方法の内部ではgetCallbackのdispatch TouchEventメソッドを呼び出します.dispatch TouchEventメソッド内部ではまずPhone Windowのsuper DisplatTouchEventメソッドを呼び出します.(メソッド内部でDecorWindowのsuper DisplatTouchEventメソッドを呼び出します.さらにDecorWindowの親タイプFrame Layoutのdispatch TouchEvent方法を呼び出します.次は普通のVictEventの配布プロセスです.)イベントがDecorWindowの下のViewで処理されていない場合は、最後に自身のon TouchEventメソッドを呼び出します.