自定義View——背景知識

  1. Activity
    • 作用:負責(zé)生命周期管理與事件處理,每個Activity組合了一個Window,實際視圖控制是交由Winodw管理ui排版。是為了滿足多窗口管理和傻瓜式視圖管理的需要而誕生的。
    • 起源:在Activity中的onCreate()方法中會調(diào)用setContentView()加載自定義視圖,實際調(diào)用的是WindowsetContentView方法
    • 相關(guān)源碼:
      android/app/Activity.java
      public void setContentView(@LayoutRes int layoutResID) {
          //調(diào)用mWindow.setContentView()
          getWindow().setContentView(layoutResID);
          initWindowDecorActionBar();
      }
      
      android/app/Activity.java
      final void attach(... ...){
         ... ...
          //實例化mWindow
         mWindow = new PhoneWindow(this, window, activityConfigCallback);
          //實現(xiàn)window的接口,鍵盤觸摸 ui策略等
         mWindow.setWindowControllerCallback(this);
         mWindow.setCallback(this);
         mWindow.setOnWindowDismissedCallback(this);
         ... ...
      }
      
  2. Window
    • 作用:唯一實現(xiàn)類PhoneWindow,創(chuàng)建了頂層視圖DecorViewDecorView作為父布局用來加載ActivitysetContentView()方法傳入的layoutRes。是為了管理ui排版,視圖控制而誕生的。
    • 相關(guān)源碼:
      com/android/internal/policy/PhoneWindow.java
      @Override
      public void setContentView(int layoutResID) {
          if (mContentParent == null) {
              //創(chuàng)建DecorView,并實例化DecorView布局內(nèi)的ViewGroup控件mContentParent
              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 {
              //將mContentParent作為父控件,加載layoutResID
              mLayoutInflater.inflate(layoutResID, mContentParent);
          }
         ... ...
      }
      
      com/android/internal/policy/PhoneWindow.java
      private void installDecor() {
          ... ...
          if (mDecor == null) {
              //創(chuàng)建DecorView
              mDecor = generateDecor(-1);
             ... ...
          }
          ... ...
          if (mContentParent == null) {
              //實例化mContentParent
             mContentParent = generateLayout(mDecor);
              ... ...
          }
      }
      
      com/android/internal/policy/PhoneWindow.java
      protected DecorView generateDecor(int featureId) {
          ... ...
          //創(chuàng)建DecorView
          return new DecorView(context, featureId, this, getAttributes());
      }
      
      com/android/internal/policy/PhoneWindow.java
      public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
      protected ViewGroup generateLayout(DecorView decor) {
          ... ...
          int layoutResource;
          if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
              layoutResource = R.layout.screen_swipe_dismiss;
              ... ...
          } 
          ... ...
          else {
             layoutResource = R.layout.screen_simple;
          }
          ... ...
          //加載DecorView布局
          mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
          //從DecorView中獲取id為content的控件
         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
          ... ...
          //將contentParent返回
          return contentParent;
      }
      
      android/view/Window.java
      public <T extends View> T findViewById(@IdRes int id) {
          //返回DecorView中給定id的視圖控件
          return getDecorView().findViewById(id);
      }
      
      frameworks/base/core/res/res/layout/screen_simple.xml
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:fitsSystemWindows="true"
         android:orientation="vertical">
         <ViewStub android:id="@+id/action_mode_bar_stub"
             android:inflatedId="@+id/action_mode_bar"
             android:layout="@layout/action_mode_bar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:theme="?attr/actionBarTheme" />
      
         <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
      </LinearLayout>
      
  3. DecorView
    • 作用:繼承自FrameLayout,是Android視圖樹的根視圖,View層的事件都會先經(jīng)過DecorView,再分發(fā)到下面的View
    • 相關(guān)源碼:
      com/android/internal/policy/DecorView.java
      void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
          mDecorCaptionView = createDecorCaptionView(inflater);
          //加載PhoneWindow生成的layoutResource
          final View root = inflater.inflate(layoutResource, null);
          if (mDecorCaptionView != null) {
              if (mDecorCaptionView.getParent() == null) {
                  addView(mDecorCaptionView,
                          new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
              }
              mDecorCaptionView.addView(root,
                      new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
          } else {
             //添加到DecorView中
              addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
          }
          mContentRoot = (ViewGroup) root;
      }
      
  4. WindowManager
    • 作用:在Activity生命周期onResume之后,綁定DecorViewViewRootImpl。
    • 相關(guān)源碼:
      android/app/ActivityThread.java
      public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
              String reason) {
             ... ...
          //執(zhí)行activity的onResume方法
          final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
          ... ...
             final Activity a = r.activity;
             if (r.window == null && !a.mFinished && willBeVisible) {
              //獲取activity的window實例
             r.window = r.activity.getWindow();
              //獲取window中實例化的DecorView
             View decor = r.window.getDecorView();
             decor.setVisibility(View.INVISIBLE);
              //獲取windowManager,獲取的是WindowManager的子類,WindowManagerImpl
             ViewManager wm = a.getWindowManager();
             WindowManager.LayoutParams l = r.window.getAttributes();
             a.mDecor = decor;
             if (a.mVisibleFromClient) {
                 if (!a.mWindowAdded) {
                     a.mWindowAdded = true;
                      //windowManager調(diào)用addView添加DecorView
                     wm.addView(decor, l);
                 }
                  ... ...
             }
         }
          ... ...
      }
      
      android/view/WindowManagerImpl.java
      public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
          mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
      }
      
      android/view/WindowManagerGlobal.java
      public void addView(View view, ViewGroup.LayoutParams params,
              Display display, Window parentWindow) {
          ViewRootImpl root;
          //實例化ViewRootImpl
          root = new ViewRootImpl(view.getContext(), display);
          //保存decorView對象
          mViews.add(view);
          //保存viewRootImpl對象
          mRoots.add(root);
          //保存decorView的layout params
          mParams.add(wparams);
          try {
              //綁定ViewRootImpl與DecorView
              root.setView(view, wparams, panelParentView);
          }... ...
      }
      
  5. ViewRoot(實際實現(xiàn)類是android.view.ViewRootImpl)
    • 作用
      1. 連接DecorViewWindowManager,也可以說是WindowDecorView的紐帶
      2. 通過DecorView完成View的三大流程繪制
      3. 用責(zé)任鏈模式,向DecorView分發(fā)用戶的InputEvent事件
    • 注意:在Activity.onResume()之后,ViewRootImpl.setView中將DecorView傳入,并在之后執(zhí)行requestLayout()通過DecorView遞歸執(zhí)行View的三大流程,所以View的三大流程都是在Activity.onResume之后。
    • 相關(guān)源碼:
      android/view/ViewRootImpl.java
      final IWindowSession mWindowSession;
      
      public ViewRootImpl(Context context, Display display) {
          //WindowManagerGlobal獲取IWindowSession
          mWindowSession = WindowManagerGlobal.getWindowSession();
      }
      
      public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
          synchronized (this) {
              if (mView == null) {
                  //這個mView就是DecorView,view的三大繪制流程均是通過mView來遞歸的
                  mView = view;
                  ... ...
                  //view的三大繪制流程
                  requestLayout();
                  //創(chuàng)建InputChannel
                  mInputChannel = new InputChannel();
                  //wms根據(jù)當(dāng)前的window創(chuàng)建了SocketPair用于跨進程通信,并對傳入的mInputChannel進行了注冊
                  //此后ViewRootImpl中的mInputChannel就指向了正確的InputChannel
                  //client端與server端就能進行雙向通信了
                  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                 getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                             mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                             mTempInsets);
                  //創(chuàng)建WindowInputEventReceiver,處理事件分發(fā)
                  mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());
                  //組裝InputStage責(zé)任鏈
                  mSyntheticInputStage = new SyntheticInputStage();
                  InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                  InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                              "aq:native-post-ime:" + counterSuffix);
                  InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                  InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                              "aq:ime:" + counterSuffix);
                  InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                  InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                              "aq:native-pre-ime:" + counterSuffix);
                  mFirstInputStage = nativePreImeStage;
                  mFirstPostImeInputStage = earlyPostImeStage;
              }
          }
      }
      
      • View的三大繪制流程入口
      android/view/ViewRootImpl.java
      public void requestLayout() {
          if (!mHandlingLayoutInLayoutRequest) {
              checkThread();
              mLayoutRequested = true;
              //這里執(zhí)行view的繪制流程
              scheduleTraversals();
          }
      }
      
      final class TraversalRunnable implements Runnable {
         @Override
         public void run() {
             doTraversal();
         }
      }
      
      final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
      
      void scheduleTraversals() {
         if (!mTraversalScheduled) {
              ... ...
             mChoreographer.postCallback(
                 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
              ... ...
         }
      }
      
      void doTraversal() {
         if (mTraversalScheduled) {
             ... ...
             performTraversals();
              ... ...
         }
      }
      
      private void performTraversals() {
          ... ...
          //measure流程
          performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
          //layout流程
          performLayout(lp, mWidth, mHeight);
          //draw流程
          performDraw();
      }
      
      private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
         try {
              //執(zhí)行DecorView的mesaure流程
             mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
      }
      
      private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                  int desiredWindowHeight) {
          final View host = mView;
          try {
              //執(zhí)行DecorView的layout流程
             host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
          }
      }
      
      private void performDraw() {
          ... ...
          try {
             boolean canUseAsync = draw(fullRedrawNeeded);
          }
          ... ...
      }
      
      private boolean draw(boolean fullRedrawNeeded) {
          ... ...
          if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
              scalingRequired, dirty, surfaceInsets)) {
             return false;
         }
          ... ...
      }
      
      private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
              boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
          ... ...
          //執(zhí)行DecorView的draw流程
          mView.draw(canvas);
          ... ...
      }
      
  6. 綜上
    1. 創(chuàng)建DecorView的流程:
      • Activityattach時,會創(chuàng)建PhoneWindow實例mWindow;
      • ActivityonCreate中調(diào)用setContentView方法時,會間接調(diào)用mWindow.setContentView;
      • PhoneWindowsetContentView中會創(chuàng)建DecorView的實例,并將Activity傳入的布局加載到DecorViewidcontentmContentParent中;
      • DecorView是android視圖樹的根節(jié)點,最底層的View,在PhoneWindow創(chuàng)建布局時generateLayout會根據(jù)不同類型的主題創(chuàng)建布局,其中會有一個idcontentViewGroup,Activity中傳入的布局文件就是以content為父布局;
    2. 建立DecorViewWindowManager的聯(lián)系并最終繪制顯示的流程:
      • ActivityThread在調(diào)用handleResumeActivity,會執(zhí)行ActivityonResume方法,隨后,會創(chuàng)建ViewRootImpl的實例,獲取ActivityWindow實例,并獲取Window中的DecorView實例,使用WindowManager,將ViewRootImplDecorView綁定;
      • ViewRootImpl獲取到DecorView的實例后,會持有該引用,并分步調(diào)用mViewmeasure,layout,draw方法。
    3. 當(dāng)InputManager監(jiān)控到硬件層面的輸入事件時,會通知ViewRootImpl對輸入事件進行底層分發(fā)(具體細節(jié)查看View的事件分發(fā)機制)
      • 創(chuàng)建InputChannel,并通過BinderSystemServer進程中完成InputChannel的注冊。
      • 創(chuàng)建WindowInputEventReceiver來處理事件分發(fā)。
      • 組裝InputStage責(zé)任鏈,負責(zé)不同InputEvent事件的處理。
  7. 其他要點
    • View ---getLeft() getTop() getRight() getBottom()返回的是View 相對與ViewGroup的左上右下距離
    • MotionEvent---getRawX() getRawY()返回的是觸摸點相對于屏幕的x/y距離 getX() getY()返回的是觸摸點相對于自身控件的x/y距離
  8. 系列文章
    1. View的背景知識
    2. View的測量流程
    3. View的布局流程
    4. View的繪制背景知識
    5. View的繪制流程
    6. View的三大繪制流程總結(jié)
    7. View的事件分發(fā)機制
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容