Android View 그리 기 메커니즘 에 대한 상세 한 설명

16239 단어 AndroidView그리 기
보기 그리 기 메커니즘 1,
View 트 리 의 그래 픽 프로 세 스
Activity 가 초점 을 받 았 을 때 레이아웃 을 그 려 달라 고 요청 합 니 다.이 요청 은 Android framework 에서 처리 합 니 다.그 리 는 것 은 루트 노드 부터 레이아웃 트 리 를 measure 와 draw 합 니 다.전체 View 트 리 의 그래 픽 프로 세 스 는 ViewRoot.java 류 의 performTraversals()함수 에서 펼 쳐 집 니 다.이 함수 가 하 는 작업 은 보기 크기(measure)를 다시 계산 해 야 하 는 지,보기 의 위치(layot)를 다시 설정 해 야 하 는 지,다시 그 려 야 하 는 지(draw)로 간단하게 요약 할 수 있 습 니 다.프로 세 스 도 는 다음 과 같 습 니 다.
这里写图片描述
보기 프로 세 스 함수 호출 체인 그리 기
这里写图片描述
설명 이 필요 한 것 은 사용자 가 자발적으로 request 를 호출 하면 measure 와 layot 과정 만 출발 하고 draw 과정 을 실행 하지 않 습 니 다.
개념
1.measure 와 layot
전체적으로 Measure 와 Layout 두 단계 의 실행:
这里写图片描述
트 리 의 옮 겨 다 니 는 것 은 질서 가 있 습 니 다.부모 보기에 서 하위 보기 까지 모든 ViewGroup 은 모든 하위 보 기 를 측정 하고 맨 밑 에 있 는 View 는 자신 을 측정 합 니 다.
2.구체 적 분석
measure 프로 세 스 는 measure(int,int)방법 으로 시작 하여 위 에서 아래로 질서 있 게 View 를 측정 하고 measure 프로 세 스 의 마지막 에 각 보기 마다 자신의 크기 와 측정 규격 을 저장 합 니 다.
layot 프로 세 스 는 layot(int,int,int,int)방법 으로 시작 되 며 위 에서 아래로 옮 겨 다 닙 니 다.이 과정 에서 모든 부모 보 기 는 measure 과정 에서 얻 은 크기 에 따라 자신의 하위 보 기 를 배치 합 니 다.
measure 과정 은 하나의 View 와 모든 하위 노드 의 mMeasured Width 와 mMeasured Height 변수 에 값 을 부여 합 니 다.이 값 은 getMeasured Width()와 getMeasured Height()방법 으로 얻 을 수 있 습 니 다.그리고 이 두 값 은 부모 보기 의 제약 범위 안에 있어 야 모든 부모 보기 가 모든 하위 보기 의 측정 을 받 을 수 있 습 니 다.하위 보기 가 Measure 가 얻 은 크기 가 만 족 스 럽 지 않 을 경우 부모 보기 가 개입 하여 측정 규칙 을 설정 하여 두 번 째 measure 를 진행 합 니 다.예 를 들 어 부모 보 기 는 주어진 dimension 에 따라 모든 하위 보 기 를 측정 할 수 있 습 니 다.최종 하위 보기 의 제약 되 지 않 은 크기 가 너무 크 거나 너무 작 을 때 부모 보 기 는 정확 한 크기 로 다시 하위 보 기 를 measure 할 수 있 습 니 다.
3.measure 프로 세 스 전달 사이즈 의 두 가지 유형
  • ViewGroup.LayoutParams(View 자체 레이아웃 매개 변수)
  • MeasureSpecs 류(부모 보기 의 하위 보기 측정 요구)
  • ViewGroup.LayoutParams
    이 종 류 는 보기 의 높이 와 너비 등 인 자 를 지정 하 는 데 자주 사용 된다.각 보기 의 height 와 width 에 대해 다음 과 같은 선택 이 있 습 니 다.
  • 구체 값
  • MATCH_PARENT 는 하위 보기 가 부모 보기 만큼 크 기 를 원 함(padding 값 은 포함 되 지 않 음)
  • WRAP_CONTENT 는 보기 가 내용 크기(padding 값 포함)
  • 를 감 쌀 수 있 음 을 표시 합 니 다.
    ViewGroup 의 하위 클래스 는 ViewGroup.LayoutParams 의 하위 클래스 가 있 습 니 다.예 를 들 어 RelativeLayout 가 가지 고 있 는 ViewGroup.LayoutParams 의 하위 클래스 RelativeLayoutParams.
    때때로 우 리 는 view.getLayoutParams()방법 을 사용 하여 보기 LayoutParams 를 가 져 와 서 강전 환 을 해 야 하지만,구체 적 인 유형 을 모 르 기 때문에 강전 오류 가 발생 할 수 있 습 니 다.사실 이 방법 은 부모 보기 형식의 LayoutParams 를 얻 었 습 니 다.예 를 들 어 View 의 부모 컨트롤 이 RelativeLayout 이면 얻 은 LayoutParams 유형 은 RelativeLayoutParams 입 니 다.
    MeasureSpecs
    측정 규격 은 측정 요구 와 사이즈 에 대한 정 보 를 포함 하고 세 가지 모델 이 있 습 니 다.
    UNSPECIFIED
    부모 보 기 는 하위 보기 에 제약 이 없습니다.원 하 는 임의의 크기 에 도달 할 수 있 습 니 다.예 를 들 어 ListView,ScrollView 는 일반적으로 사용자 정의 View 에서 사용 할 수 없습니다.
    EXACTLY
    부모 보 기 는 하위 보기 로 정확 한 사 이 즈 를 지정 합 니 다.또한 하위 보기 가 아무리 기대 하 더 라 도 이 지정 한 크기 의 경계 내 에서 해당 하 는 속성 은 match 입 니 다.parent 또는 구체 적 인 값,예 를 들 어 100 dp,부모 컨트롤 은MeasureSpec.getSize(measureSpec)을 통 해 하위 컨트롤 의 사 이 즈 를 직접 얻 을 수 있 습 니 다.
    AT_MOST
    부모 보 기 는 하위 보기 의 최대 사 이 즈 를 지정 합 니 다.하위 보 기 는 자신의 모든 하위 보기 가 이 사이즈 범위 내 에 적응 할 수 있 도록 확보 해 야 합 니 다.대응 하 는 속성 은 wrap 입 니 다.콘 텐 츠,이러한 모드 에서 부모 컨트롤 은 하위 View 의 사 이 즈 를 확정 할 수 없 으 며,하위 컨트롤 은 자신 이 필요 에 따라 자신의 사 이 즈 를 계산 할 수 밖 에 없습니다.이러한 모델 은 바로 우리 가 사용자 정의 보 기 를 통 해 측정 논 리 를 실현 해 야 하 는 상황 입 니 다.
    3.measure 핵심 방법
    measure(int widthMeasureSpec, int heightMeasureSpec)
    이 방법 은View.java클래스 에서 final 형식 으로 복사 할 수 없 지만 measure 호출 체인 은 최종 적 으로 View/View Group 대상onMeasure()방법 을 되 돌려 줍 니 다.따라서 보 기 를 사용자 정의 할 때 복사onMeasure()방법 만 있 으 면 됩 니 다.
    onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    이 방법 은 보기 에서 측정 논 리 를 실현 하 는 방법 을 사용자 정의 하 는 것 입 니 다.이 방법의 매개 변 수 는 부모 보기 가 하위 보기 의 width 와 height 에 대한 측정 요구 입 니 다.사용자 정의 보기에 서 해 야 할 일 은 이 widthMeasureSpec 과 height MeasureSpec 에 따라 보기 의 width 와 height 를 계산 하 는 것 입 니 다.패턴 처리 방식 이 다 릅 니 다.
    setMeasuredDimension()
    측정 단계 의 최종 방법 은onMeasure(int widthMeasureSpec, int heightMeasureSpec)방법 에서 호출 하여 계 산 된 사 이 즈 를 이 방법 에 전달 하면 측정 단계 가 곧 끝난다.이 방법 도 반드시 호출 해 야 하 는 방법 이다.그렇지 않 으 면 이상 을 보고 할 것 이다.사용자 정의 보 기 를 사용 할 때 시스템 의 복잡 한 Measure 과정 에 관심 을 가 질 필요 가 없습니다.Measure Spec 에 따라 계 산 된 사 이 즈 를 호출setMeasuredDimension()하면 됩 니 다.ViewPager Indicator 의 onMeasure 방법 을 참고 하 십시오.
    다음은 View Group 의measureChildren(int widthMeasureSpec, int heightMeasureSpec)방법 으로 복합 View 의 Measure 절 차 를 분석 하 겠 습 니 다.
    Measure Child 방법 호출 프로 세 스 맵:
    这里写图片描述
    소스 코드 분석
    
    /**
       *       View   measure   ,          View       MeasureSpec        padding
       *         GONE      View,         getChildMeasureSpec       
       *
       * @param widthMeasureSpec    View   width     
       * @param heightMeasureSpec    View   height     
       */
      protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
          final View child = children[i];
          if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
          }
        }
      }
    
      protected void measureChild(View child, int parentWidthMeasureSpec,
          int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();//   Child   LayoutParams
    
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,//    ChildView   widthMeasureSpec
            mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,//    ChildView   heightMeasureSpec
            mPaddingTop + mPaddingBottom, lp.height);
    
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
    
      /** 
       *      measureChildren        ,     ChildView        MeasureSpec。
       *      ChildView   MeasureSpec   LayoutParams                。
       *
       * @param spec    View      
       * @param padding    View         paddingand,       margins
       *
       * @param childDimension       (height   width)    
       * @return      MeasureSpec 
       */
      public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    
          .........
    
        //                         MeasureSpec
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 
      }
    
      /**
       *
       *      View      ,       、      
       *    View            onMeasure(int, int)  ,      。
       *   ,   onMeasure(int, int)            
       *
       * @param widthMeasureSpec       ,        Measure   
       * @param heightMeasureSpec       ,          Measure   
       *
       */
      public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
       ...
    
       onMeasure(widthMeasureSpec, heightMeasureSpec);
    
       ...
      }
    
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
      }
    4.layot 관련 개념 및 핵심 방법
    우선 하위 보기 의 구체 적 인 위 치 는 부모 보기 에 비해 명확 하 다.View 의 onLayout 방법 은 비어 있 고 ViewGroup 의 onLayout 는 abstract 이기 때문에 사용자 정의 View 가 ViewGroup 을 계승 하려 면 onLayout 함 수 를 실현 해 야 합 니 다.
    layot 과정 에서 하위 보 기 는getMeasuredWidth()getMeasuredHeight()방법 으로 measure 과정 에서 얻 은 mMeasured Width 와 mMeasured Height 를 자신의 width 와 height 로 호출 합 니 다.그리고 각 하위 보기 의layout(l, t, r, b)함 수 를 호출 하여 각 하위 보기 가 부모 보기 의 위 치 를 확인 합 니 다.
    LinearLayout 의 onLayout 소스 코드 분석
    
    @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mOrientation == VERTICAL) {
          layoutVertical(l, t, r, b);
        } else {
          layoutHorizontal(l, t, r, b);
        }
      }
    
      /**
       *        View,            
       */
      void layoutVertical(int left, int top, int right, int bottom) {
      for (int i = 0; i < count; i++) {
            final View child = getVirtualChildAt(i);
            if (child == null) {
              childTop += measureNullChild(i);
            } else if (child.getVisibility() != GONE) {//         View     GONE      
              final int childWidth = child.getMeasuredWidth();//measure       Width
              final int childHeight = child.getMeasuredHeight();//measure       height
    
              ...   childLeft、childTop   
    
              setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                  childWidth, childHeight);
            }
          }
      }
    
      private void setChildFrame(View child, int left, int top, int width, int height) {    
        child.layout(left, top, left + width, top + height);
      }  
    
      View.java
      public void layout(int l, int t, int r, int b) {
        ...
        setFrame(l, t, r, b)
      }
    
      /**
       *     View             
       */
       protected boolean setFrame(int left, int top, int right, int bottom) {
        ...
       }
    5.프로 세 스 관련 개념 및 핵심 방법 그리 기
    먼저 draw 과정 과 관련 된 함 수 를 살 펴 보 겠 습 니 다.
    View.draw(Canvas canvas):
    ViewGroup 은 이 방법 을 복사 하지 않 았 기 때문에 모든 보 기 는 최종 적 으로 View 의 draw 방법 으로 그립 니 다.사용자 정의 보기에 서도 이 방법 을 복사 하 는 것 이 아니 라 복사onDraw(Canvas)방법 으로 그 려 야 합 니 다.사용자 정의 보기 가 이 방법 을 복사 하려 면super.draw(canvas)를 호출 하여 시스템 그리 기 를 완성 한 다음 사용자 정의 그리 기 를 하 십시오.
    View.onDraw():
    View 의onDraw(Canvas)기본 값 은 비어 있 습 니 다.그리 기 과정 에서 복사 해 야 할 방법 을 사용자 정의 하고 자신의 내용 을 그립 니 다.
    dispatchDraw()
    하위 보기 그리 기 시작 합 니 다.View 에서 기본 값 은 비어 있 습 니 다.ViewGroup 은 하위 보 기 를 그 리 려 고 복사dispatchDraw()했 습 니 다.이 방법 은 상관 하지 않 아 도 됩 니 다.사용자 정의 View Group 은dispatchDraw()을 복사 해 서 는 안 됩 니 다.
    흐름 도 그리 기
    这里写图片描述
    View.draw(Canvas)소스 코드 분석
    
     /**
       * Manually render this view (and all of its children) to the given Canvas.
       * The view must have already done a full layout before this function is
       * called. When implementing a view, implement
       * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
       * If you do need to override this method, call the superclass version.
       *
       * @param canvas The Canvas to which the View is rendered. 
       *
       *       Canvas      View(       View)。              layout。      view    ,
       *        onDraw(Canvas)   ,    draw(canvas)   。            ,           。
       */
      public void draw(Canvas canvas) {
    
        / * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *   1. Draw the background if need
         *   2. If necessary, save the canvas' layers to prepare for fading
         *   3. Draw view's content
         *   4. Draw children (dispatchDraw)
         *   5. If necessary, draw the fading edges and restore layers
         *   6. Draw decorations (scrollbars for instance)
         */
    
      // Step 1, draw the background, if needed
        if (!dirtyOpaque) {
          drawBackground(canvas);
        }
    
         // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        if (!verticalEdges && !horizontalEdges) {
          // Step 3, draw the content
          if (!dirtyOpaque) onDraw(canvas);
    
          // Step 4, draw the children
          dispatchDraw(canvas);
    
          // Step 6, draw decorations (scrollbars)
          onDrawScrollBars(canvas);
    
          if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
          }
    
          // we're done...
          return;
        }
    
        // Step 2, save the canvas' layers
        ...
    
        // Step 3, draw the content
        if (!dirtyOpaque) 
          onDraw(canvas);
    
        // Step 4, draw the children
        dispatchDraw(canvas);
    
        // Step 5, draw the fade effect and restore layers
    
        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);
      }
    위의 처리 과정 에서 우 리 는 최적화 된 작은 기 교 를 얻 을 수 있다.레이 어 를 그 릴 필요 가 없 을 때 두 번 째 단계 와 다섯 번 째 단 계 는 건 너 갈 수 있다.따라서 그림 을 그 릴 때 절약 할 수 있 는 layer 는 절약 할 수 있 고 그리 기 효율 을 높 일 수 있 습 니 다.
    ViewGroup.dispatchDraw()소스 코드 분석
    
    dispatchDraw(Canvas canvas){
    
    ...
    
     if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {//   ChildView    
      final boolean buildCache = !isHardwareAccelerated();
          for (int i = 0; i < childrenCount; i++) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {//    Visible      ,               
              final LayoutParams params = child.getLayoutParams();
              attachLayoutAnimationParameters(child, params, i, childrenCount);//          
              bindLayoutAnimation(child);//  Child     
              if (cache) {
                child.setDrawingCacheEnabled(true);
                if (buildCache) {
                  child.buildDrawingCache(true);
                }
              }
            }
          }
    
      final LayoutAnimationController controller = mLayoutAnimationController;
          if (controller.willOverlap()) {
            mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
          }
    
      controller.start();//    View    
    }
    
     //    ChildView
     for (int i = 0; i < childrenCount; i++) {
          int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
          final View child = (preorderedList == null)
              ? children[childIndex] : preorderedList.get(childIndex);
          if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
            more |= drawChild(canvas, child, drawingTime);
          }
        }
    
    ...
    
    }
    
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }
    
    /**
       * This method is called by ViewGroup.drawChild() to have each child view draw itself.
       * This draw() method is an implementation detail and is not intended to be overridden or
       * to be called from anywhere else other than ViewGroup.drawChild().
       */
      boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        ...
      }
    drawChild(canvas, this, drawingTime)
    View 의child.draw(canvas, this,drawingTime)방법 을 직접 호출 했 습 니 다.문서 에서 도 설명 되 었 습 니 다.ViewGroup.drawChild()방법 을 제외 하고 다른 곳 에서 복사 하거나 호출 해 서 는 안 됩 니 다.이것 은 View Group 에 속 합 니 다.View.draw(Canvas)방법 은 우리 가 사용자 정의 컨트롤 에서 복사 할 수 있 는 방법 으로 구체 적 으로 상기view.draw(Canvas)에 대한 설명 을 참고 할 수 있 습 니 다.매개 변수 에서 볼 수 있 듯 이child.draw(canvas, this, drawingTime)부모 보기 와 관련 된 논 리 를 처리 한 것 이 분명 하지만 View 의 최종 그리 기 또는View.draw(Canvas)방법 입 니 다.
    invalidate()
    View 트 리,즉 draw 프로 세 스 를 다시 그 려 달라 고 요청 합 니 다.보기 의 크기 가 변 하지 않 으 면layout()프로 세 스 를 호출 하지 않 고invalidate()방법 을 호출 한 View 만 그립 니 다.
    requestLayout()
    레이아웃 이 변 할 때,예 를 들 어 방향 변화,사이즈 변화 등 은 이 방법 을 사용 합 니 다.사용자 정의 보기에 서 사이즈 크기 를 재 측정 하려 면 수 동 으로 이 방법 을 사용 해 야 합 니 다.measure()layout()과정 을 촉발 하지만 draw 를 하지 않 습 니 다.
    읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

    좋은 웹페이지 즐겨찾기