View 의 작업 절차 - measure

15219 단어
앞 에 쓰다
오늘 아버지 와 병원 에 가 셨 습 니 다. 아버 지 는 요즘 허리 가 아 프 십 니 다. 다행히 별일 없 으 셔 서 여러분 도 자신의 몸 을 많이 아 껴 야 합 니 다. 몸 에 문제 가 생기 면 늦 습 니 다.자, 여담 은 여기까지 하 겠 습 니 다. '개발 예술 탐색' 에 있 는 많은 것들 을 제 가 이미 봤 습 니 다. 하지만 제 가 그 당시 에 수준 이 제한 되 어 있 었 고 잘 모 르 는 것 도 많 았 기 때문에 보고 잊 어 버 리 는 것 은 정상 적 인 일 이 었 습 니 다.그러나 업무 경험 이 늘 어 나 면서 예전 에 대학 에서, 등 일부 과목 을 다 녔 는데 모두 연결 되 어 좋 았 고 자신의 부족 함 을 느 낄 수 있 었 다.이번 주 글 은 주로 독서 노트 라 고 할 수 있 습 니 다. 제4 장과 View 와 관련 된 필 기 를 읽 어 보 세 요. View Group 에 대해 서 는 시간 이 좀 걸 릴 것 같 습 니 다. 조금 만 더 가 라 앉 히 고 여러분 과 함께 탐색 해 보 겠 습 니 다.그리고 정말 입 니 다. 이 글 은 저도 억지로 쓴 것 입 니 다. 소스 코드 를 보면 서 제 의문 이 점점 많아 지고 현 단계 에서 해결 할 수 없 는 것 이 많 습 니 다. 그러나 문 제 는 항상 하나씩 해결 되 기 때 문 입 니 다. 먼저 View 의 measure 절 차 를 거 쳐 다른 것 을 다시 이야기 하 겠 습 니 다.
ViewRoot & DecorView
ViewRoot 는 ViewRootImpl 류 에 대응 하고 ViewRootImpl 류 에 다음 과 같은 설명 이 있 습 니 다.
/** 
 * The top of a view hierarchy, implementing the needed 
 * protocol between View and the WindowManager.
 */ 

View 층 맨 위 에 View 와 Window Manager 간 에 필요 한 협 의 를 실현 합 니 다.이런 종 류 는 매우 중요 하지만 저 는 지금 그 에 대한 이해 가 깊 지 않 습 니 다. (하 나 는 소스 코드 를 읽 을 수 없 기 때 문 입 니 다. 하 나 는 소스 코드 를 체계적으로 읽 어 본 적 이 없 기 때 문 입 니 다) 그래서 책 에 있 는 말 을 가지 고 보면 Window Manager 와 DecorView 를 연결 하 는 유대 이 고 View 의 세 가지 절 차 는 모두 ViewRoot 를 통 해 이 루어 집 니 다.Activity Thread 에서 (똑 같이 재 미 있 는 점 은 Activity Thread 는 하나의 클래스 입 니 다. 스 레 드 가 아 닙 니 다. 물론 어느 날 이 블록 을 읽 고 여러분 과 공유 하 게 된다 면) Activity 대상 이 생 성 되면 DecorView 를 Window 에 추가 하고 ViewRootImpl 대상 을 만 들 며 ViewRootImpl 대상 과 DecorView 를 연결 합 니 다.
다음은 결론 을 내리 고 가능 한 한 빨리 본론 으로 들 어 갑 니 다. View 의 그리 기 절 차 는 ViewRoot 의 performTraversals 에서 시작 되 었 습 니 다. measure, layot, draw 세 가지 과정 을 거 쳐 야 최종 적 으로 하나의 View 를 그 릴 수 있 습 니 다.
MeasureSpec
이전에 Android 사용자 정의 View 에서 알 아야 할 것들 중 에 저 는 MeasureSpec 를 간략하게 소 개 했 습 니 다. 물론 더 이상 귀 찮 게 하지 못 하 겠 습 니 다. 계속 소개 하 세 요. 익숙 한 것 은 이 부분 을 뛰 어 넘 을 수 있 습 니 다.
우선 우 리 는 그의 주석 을 직접 보고 주석 이 이 물건 을 어떻게 해석 하 는 지 보 자.
/**
     * A MeasureSpec encapsulates the layout requirements passed from parent to child.
     * Each MeasureSpec represents a requirement for either the width or the height.
     * A MeasureSpec is comprised of a size and a mode. There are three possible
     * modes:
     * 
*
UNSPECIFIED
*
* The parent has not imposed any constraint on the child. It can be whatever size * it wants. *
* *
EXACTLY
*
* The parent has determined an exact size for the child. The child is going to be * given those bounds regardless of how big it wants to be. *
* *
AT_MOST
*
* The child can be as large as it wants up to the specified size. *
*
* * MeasureSpecs are implemented as ints to reduce object allocation. This class * is provided to pack and unpack the tuple into the int. */

맨 위 에 있 는 말 은 아마도 Measure Spec 이 부모 (컨트롤) 에서 하위 (컨트롤) 에 게 전달 하 는 레이아웃 요 구 를 패키지 한 것 을 의미 합 니 다.모든 Measure Spec 은 너비 가 아니면 높 은 요 구 를 대표 한다.하나의 MeasureSpec 은 하나의 사이즈 와 하나의 (측정) 모델 을 포함 하 는데 이 모델 들 은 다음 과 같다.
  • UNSPECIFIED 부모 (용기) 는 하위 (컨트롤) 에 대해 아무런 제한 이 없 으 며, 하위 컨트롤 은 원 하 는 만큼 크다.
  • EXACTLY 부모 용 기 는 하위 컨트롤 의 정확 한 사 이 즈 를 결정 했다. 하위 컨트롤 은 자신 이 어떻게 되 고 싶 든 지 간 에 제 시 된 제약 크기 가 될 것 이다.
  • AT_MOST 서브 컨트롤 은 그 가 원 하 는 규격 사이즈 만큼 클 수 있다.

  • 마지막 말 은 왜 이렇게 실현 되 는 지 설명 했다. 당분간 우리 가 이런 것 에 관심 을 가 질 필요 가 없다.주석 을 다 읽 고 나 니 기본적으로 Measure Spec 에 대해 기본 적 인 인식 을 가지 게 되 었 다.그러나 이 주석 을 보면 의심 이 생 길 것 이다. 주석 에서 MeasureSpec 은 아버지 가 자식 에 게 전달 한 것 이 라 고 말 하면 가장 꼭대기 층 인 DecorView 의 MeasureSpec 는 어떻게 왔 을 까?에서 ViewRootImpl 의 measureHierarchy 방법 에서 만 들 었 다 고 하 는데 주요 코드 는 다음 과 같다.
    childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth,lp.width);
    childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight,lp.width);
    performMeasure(childWidthMeasureSpec,childHeightmeasureSpec);
    

    그 중에서 desired Window Width 와 desired Window Height 는 화면의 크기 입 니 다. getRoot Measure Spec () 소스 코드 를 추적 해 보 세 요.
        private static int getRootMeasureSpec(int windowSize, int rootDimension) {
            int measureSpec;
            switch (rootDimension) {
    
            case ViewGroup.LayoutParams.MATCH_PARENT:
                // Window can't resize. Force root view to be windowSize.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
                break;
            case ViewGroup.LayoutParams.WRAP_CONTENT:
                // Window can resize. Set max size for root view.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
                break;
            default:
                // Window wants to be an exact size. Force root view to be that size.
                measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
                break;
            }
            return measureSpec;
        }
    

    이 방법 중 matchparent、wrap_content 와 나머지 상황 의 처리:
  • LayoutParams.MATCH_PARENT: 정확 한 모드 로 측정 하고 크기 는 창 크기
  • LayoutParams.WRAP_CONTENT: 크기 가 일정 하지 않 고 최대 창 크기
  • 기타: 우리 가 평소에 xml 과 코드 를 쓴 경험 을 결합 하면 여기 서 다른 것 은 우리 가 크기 (예 를 들 어 100 dp) 를 정 했 고 크기 는 지 정 된 크기 이다.

  • View 의 MeasureSpec 획득
    위의 MeasureSpec 주석 에서 아버지 에서 아들 로 전 달 된 것 이 라 고 말 했 습 니 다. 그러면 View 는 키 레이아웃 이 분명 합 니 다. View Group 에서 child 가 MeasureSpec 를 어떻게 얻 었 는 지 찾 아 보 겠 습 니 다.
        /**
         * Ask all of the children of this view to measure themselves, taking into
         * account both the MeasureSpec requirements for this view and its padding.
         * We skip children that are in the GONE state The heavy lifting is done in
         * getChildMeasureSpec.
         *
         * @param widthMeasureSpec The width requirements for this view
         * @param heightMeasureSpec The height requirements for this view
         */
        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);
                }
            }
        }
    

    낡은 규칙, 먼저 이 찌꺼기 로 주석 을 번역 하 게 해 주세요...........................................................................GONE 상 태 를 건 너 뛰 는 (이 상 태 를 측정 하지 않 는 View) 더 힘 든 일 은 getChild Measure Spec 에서 이 루어 집 니 다.
    분명히 getChild Measure Spec 방법 을 봐 야 겠 어 요.
        /**
         * Does the hard part of measureChildren: figuring out the MeasureSpec to
         * pass to a particular child. This method figures out the right MeasureSpec
         * for one dimension (height or width) of one child view.
         *
         * The goal is to combine information from our MeasureSpec with the
         * LayoutParams of the child to get the best possible results. For example,
         * if the this view knows its size (because its MeasureSpec has a mode of
         * EXACTLY), and the child has indicated in its LayoutParams that it wants
         * to be the same size as the parent, the parent should ask the child to
         * layout given an exact size.
         *
         * @param spec The requirements for this view
         * @param padding The padding of this view for the current dimension and
         *        margins, if applicable
         * @param childDimension How big the child wants to be in the current
         *        dimension
         * @return a MeasureSpec integer for the child
         */
        public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
            int specMode = MeasureSpec.getMode(spec);
            int specSize = MeasureSpec.getSize(spec);
    
            int size = Math.max(0, specSize - padding);
    
            int resultSize = 0;
            int resultMode = 0;
    
            switch (specMode) {
            // Parent has imposed an exact size on us
            case MeasureSpec.EXACTLY:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size. So be it.
                    resultSize = size;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // Parent has imposed a maximum size on us
            case MeasureSpec.AT_MOST:
                if (childDimension >= 0) {
                    // Child wants a specific size... so be it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size, but our size is not fixed.
                    // Constrain child to not be bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = MeasureSpec.AT_MOST;
                }
                break;
    
            // Parent asked to see how big we want to be
            case MeasureSpec.UNSPECIFIED:
                if (childDimension >= 0) {
                    // Child wants a specific size... let him have it
                    resultSize = childDimension;
                    resultMode = MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    // Child wants to be our size... find out how big it should
                    // be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                }
                break;
            }
            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
        }
    

    우리 계속 번역 하 자... 어 려 운 일 은 우리 가 하 자. Measure Spec 이 child 에 게 전달 되 는 것 을 계산 해 보 자.이 방법 은 키 view 의 한 사이즈 (높이 또는 너비) 로 정확 한 MeasureSpec 를 계산 합 니 다.
    그의 목적 은 Measure Spec 과 Layout Params 의 정 보 를 조합 하여 child 에 게 가장 좋 은 결 과 를 얻 게 하 는 것 이다.예 를 들 어 이 view 가 그의 사 이 즈 를 알 고 있다 면 (측정 모드 가 EXACTLY 이기 때문에) 이 child 는 그의 Layout Params 를 지 적 했 습 니 다. 그 는 부모 용기 와 같은 크기 (match parent) 를 가지 고 싶 습 니 다. 이때 부모 용 기 는 child 에 게 정확 한 크기 로 구 조 를 요구 해 야 합 니 다.
    위의 주석 을 통 해 알 수 있 듯 이 하위 View 의 MeasureSpec 는 자신의 LayoutParams 에 의 해 결정 되 는 것 이 아니 라 부모 용기 와 자신의 LayoutParams 에 의 해 결정 된다.다음은 코드 를 보고 어떻게 작 동 하 는 지 보 겠 습 니 다.
  • 먼저 View Group 의 너비 / 높이 의 MeasureSpec 에서 해당 하 는 specMode 를 얻 은 다음 mdoe 에 따라 서로 다른 작업 을 수행 합 니 다
  • EXACTLY: 하위 View 가 정확 한 값 의 크기 를 지정 하면 측정 모드 는 EXACTLY 이 고 사 이 즈 는 들 어 오 는 child Dimension 입 니 다. 이 두 값 은 하나의 Measure Spec 으로 포장 되 어 돌아 갑 니 다.childDimension 이 MATCH 와 같다 면PARENT, 위 에서 가 져 온 자신의 크기 와 EXACTLY 모드 를 MeasureSpec 로 포장 하고 되 돌려 줍 니 다.childDimension 이 WRAP 와 같다 면CONTENT, 그러면 자신의 사 이 즈 를 아이 에 게 부여 하 는 사 이 즈 를 서브 뷰 의 사이즈 가 부모 용기 보다 크 지 않 게 하면 됩 니 다. 이 사이즈 와 ATMOST 모드 로 포장 하고 되 돌려 줍 니 다.

  • 다음 ATMOST 와 UNSPECIFIED 측정 모델 과 EXACTLY 의 방식 차이 가 많 지 않 아서 일일이 보지 않 습 니 다. 에서 표 하 나 를 정 리 했 습 니 다.
    |columns:childLayoutParams/rows:parentSpecMode|EXACTILY|AT_MOST|UNSPECIFIED | ------------ |:-----------------: | ------: | |dp/px|EXACTLY/childSize|EXACTLY/childSize|EXACTLY/childSize| |match_parent|EXACTLY/parentSize|AT_MOST/parentSize|UNSPECIFIED/0| |wrap_content|AT_MOST/parentSize|AT_MOST/parentSize|UNSPECIFIED/0|
    마지막 으로 에서 와 같은 말 을 강조해 야 한다. 상기 표 는 경험 총화 가 아니 라 코드 를 하나의 표 로 구 현 했 을 뿐이다.
    View 의 measure 프로 세 스
    마침내 몇 개의 비교적 중요 한 구 덩이 를 메 웠 으 니 View 의 measure 절 차 를 이야기 할 수 있 습 니 다.View 의 측정 은 measure () 방법 으로 이 루어 집 니 다. measure () 방법 에서 onMeasure () 방법 을 호출 하여 View 의 onMeasure () 방법의 실현 을 살 펴 보 겠 습 니 다.
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }
    

    이 방법 에 대해 주의해 야 할 점 이 있 습 니 다. 예전 에 안 드 로 이 드 사용자 정의 View 에서 알 아야 할 것들 이 있 습 니 다. 그리고 이 방법 에 대한 주석 에는 주의 할 점 이 적 혀 있 습 니 다. 관심 있 는 것 은 가서 보 세 요. 여 기 는 군말 하지 않 겠 습 니 다.이상 의 방법 은 set Measured Dimension 방법 으로 View 너비 와 높이 를 설정 하 였 으 므 로 우 리 는 그 가 어떻게 너비 와 높이 를 얻 었 는 지 살 펴 보면 된다. 더 이상 말 하지 않 고 소스 코드 를 본다.
        public static int getDefaultSize(int size, int measureSpec) {
            int result = size;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);
    
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            }
            return result;
        }
    

    알 수 있 듯 이 여 기 는 간단 한 판단 과 할당 작업 만 했 을 뿐 측정 모델 이 AT 라면MOST 와 EXACTLY: View 가 측정 한 크기 를 되 돌려 줍 니 다. UNSPECIFIED 모드 라면 들 어 오 는 size 값 을 되 돌려 줍 니 다.다음은 들 어 오 는 이 size 값 을 어떻게 얻 었 는 지 보 겠 습 니 다.
        protected int getSuggestedMinimumWidth() {
            return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
        }
    

    상기 코드 를 통 해 알 수 있 듯 이 view 에 배경 이 있 으 면 최소 너비 와 background 의 너비 에서 큰 것 을 되 돌려 줍 니 다. 배경 이 없 으 면 최소 너비 로 돌아 갑 니 다. 이 최소 폭 은 xml 또는 set Minimum Width 를 통 해 설정 할 수 있 습 니 다. 기본 값 은 0 입 니 다.
    다음은 하나의 문 제 를 통 해 오늘 알 게 된 것 을 회상 합 니 다: View 에서 wrap 을 사용 합 니 다.content 왜 효과 와 matchparent 효과 가 같다 고요?우선 이 문 제 는 View Group 에 가 봐 야 합 니 다. View 의 Measure Spec 은 View Group 에서 전 달 된 것 이기 때 문 입 니 다. 앞에서 우리 가 그린 표 는 코드 를 처리 하 는 구 현 입 니 다. 우 리 는 직접 표를 찾 을 수 있 습 니 다 ~
    체크 표 는 matchparent 와 wrap콘 텐 츠 라 는 두 가지 상황 에서 View 에 전달 되 는 size 는 모두 parentSize (UNSPECIFIED 를 무시 한 경우) 입 니 다. 그러면 View 가 이 두 가지 상황 에 대해 특별한 처 리 를 했 는 지 살 펴 보면 됩 니 다.위의 getDefault Size 방법 에서 AT 를 볼 수 있 습 니 다.MOST 와 EXACTLY 두 가지 모드 의 반환 값 이 같 기 때문에 View 에 계 승 된 컨트롤 을 사용자 정의 할 때 onMeasure () 방법 을 다시 써 서 wrap 을 처리 해 야 합 니 다.콘 텐 츠 의 경우.
    여기까지 읽 으 면 View 와 View Group 이라는 두 물건 이 전혀 분리 되 지 않 는 다 는 것 을 알 게 될 것 입 니 다. 사실 도 마찬가지 입 니 다. 하지만 여기 서 저 는 제 필 기 를 계속 하지 않 겠 습 니 다. 저도 잘 모 르 는 일이 있 기 때문에 나중에 구 덩이 를 메 우 겠 습 니 다. (flag 를 만 들 었 습 니 다)
    후기
    또 하나의 View Group 의 measure 프로 세 스 가 있 습 니 다. 여 기 는 계속 되 지 않 았 습 니 다. 남 겨 두 었 다가 다시 이 구 덩이 를 메 우 고 싶 습 니 다.'아, 그 랬 구나' 라 는 상 태 였 던 기억 이 납 니 다. 지금 읽 으 면 코드 의 절차 에 의문 이 생 길 수 있 습 니 다. 이런 의혹 들 은 제 수준 으로 는 해결 하기 어 려 웠 습 니 다.그러나 공 부 는 끊임없이 보완 되 고 새로운 문제 가 발생 하 는 과정 이다. 우리 중 고등학교 에서 배 운 물리 처럼 우 리 는 상대 적 으로 정확 한 지식 만 배 울 때 가 많다. 그러나 앞으로 계속 공부 하 는 과정 에서 우 리 는 예전 의 잘못 과 한 계 를 가 진 이 해 를 계속 바로 잡 을 수 있다.

    좋은 웹페이지 즐겨찾기