보기 그리 기 2 - onMeasure

사용자 정의 View 를 그 리 는 과정 에서 onMeasure, onLayout, onDraw 세 가지 함수 가 View 의 외관 이미 지 를 실현 하고 onTouch Event 등 함수 가 실현 하 는 보기 재 업로드 행 위 를 추가 하여 완전한 사용자 정의 View 체 계 를 구축 합 니 다.Android 시스템 에서 on 으로 시작 하 는 onXXX 함 수 는 주로 Activity, Service, View 에 나타 나 는데 보통 디자인 모델 안의 템 플 릿 디자인 모델 을 사용 합 니 다.템 플 릿 프로 세 스 를 정의 한 다음 템 플 릿 을 다시 쓰 는 방법 으로 사용자 정의 효 과 를 실현 합 니 다.
역할.
  • onMeasure 는 View 를 그 리 는 크기 를 지정 합 니 다
  • onLayout 는 View 를 그 릴 위 치 를 지정 합 니 다
  • onDraw 구현 그리 기 과정 은 시스템 소스 코드 에서 볼 때
  • View 의 onMeasure 실현 과정
    onMeasure () - 외부 호출 패키지
    |
    setMeasured Dimension () - 측 정 된 점용 크기 를 View 에 설정 합 니 다.
    |
    getDefaultSize () - Min 크기 와 측량 크기 를 비교 하여 선택
    |
    getSuggest Minimum Width () - Min 크기 받 기
    각각의 실현 코드 는 다음 과 같다.
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //       widthMeasureSpec heightMeasureSpeac         
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }

    구체 적 인 실현 방법 은 setMeasuredDimension() 에 봉인 되 어 있다.
        protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
            //               ....
            mMeasuredWidth = measuredWidth;
            mMeasuredHeight = measuredHeight;
    
            mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
        }
        public static int getDefaultSize(int size, int measureSpec) {
            //size    
            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;
        }

    getDefaultSize () 방법 은 MeasureSpec 의 specSize 를 되 돌려 줍 니 다. 이 specSize 는 View 의 측정 크기 입 니 다. View 의 최종 크기 는 layout() 에서 확 정 된 것 이지 만 specSize 의 크기 는 거의 모든 시간 layout() 에서 확 정 된 최종 크기 와 같 기 때 문 입 니 다.
        protected int getSuggestedMinimumWidth() {
            //mMinWidth    xml    android:minSize  ,     View.SetMinSize  
            return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
        }

    getSuggested MinimumWidth () 방법, android:minWidth 설정 이 있 으 면 이 값 으로 폭 을 설정 하지만 Background 를 설정 한 경우 도 있 습 니 다. 이 경우 Background 와 minWidth 의 크기 를 비교 해 야 합 니 다.위 는 width Measure Spec 과 height Measure Spec 을 통 해 공간 크기 를 차지 하 는 과정 입 니 다. 본 소 원 width Measure Spec 과 height Measure Spec 는 어디에서 왔 습 니까?
    MeasureSpec 이 뭐 예요?
    측정 규격, MeasureSpec 는 32 비트 int 수 표시, 역할
  • 부모 레이아웃 의 하위 레이아웃 View 에 대한 측정 요구
  • 측정 모드 와 측정 데이터 포함
  • 는 너비, 높이 1, 3 이 모두 이해 하기 쉽다 는 뜻 으로 이해 하기 어 려 운 것 은 두 번 째 점 이다. MeasureSpec 의 소스 코드
  • 를 살 펴 보 자.
    public static class MeasureSpec {
            private static final int MODE_SHIFT = 30;
            /**     */
            private static final int MODE_MASK = 0x3 << MODE_SHIFT;
            /**       */
            public static final int UNSPECIFIED = 0 << MODE_SHIFT;
            /**      */
            public static final int EXACTLY = 1 << MODE_SHIFT;
            /**      */
            public static final int AT_MOST = 2 << MODE_SHIFT;
    
            /** *        */
            public static int getMode(int measureSpec) {
                return (measureSpec & MODE_MASK);
            }
    
            /** *        */
            public static int getSize(int measureSpec) {
                return (measureSpec & ~MODE_MASK);
            }
    
            /** *     */
            public static int makeMeasureSpec(int size, int mode) {
                if (sUseBrokenMakeMeasureSpec) {
                    return size + mode;
                } else {
                    return (size & ~MODE_MASK) | (mode & MODE_MASK);
                }
            }
    
            static int adjust(int measureSpec, int delta) {
                return makeMeasureSpec(getSize(measureSpec + delta),
                        getMode(measureSpec));
            }
        }

    이곳 의 소스 코드 는 이해 하기 쉬 운 삭 제 를 했다.MODE 로 표 시 를 정 의 했 습 니 다.MASK=3<<30;측정 모드 가 져 오기
            public static int getMode(int measureSpec) {
                return (measureSpec & MODE_MASK);
            }

    측정 데이터 가 져 오기
            public static int getSize(int measureSpec) {
                return (measureSpec & ~MODE_MASK);
            }

    이 int 형식의 데 이 터 는 32 비트 이 고 앞의 2 위 는 측정 모드 를 나타 내 며 뒤의 30 위 는 측정 데 이 터 를 나타 낸다.
  • 00 표시 MeasureSpec.UNSPECIFIED 는 부모 레이아웃 이 하위 레이아웃 에 대해 어떠한 제한 도 하지 않 는 다 는 것 을 나타 낸다. 하위 컨트롤 이 크 고 싶 은 만큼 큰 모델 은 일반적으로 깊이 연구 하지 않 고 일반 시스템 은 ListView 와 ScrollView 등 컨트롤 에 사용 된다.
  • 01 은 MeasureSpec.EXACTLY View 의 크기 를 정확하게 제어 하 는 것 이 getSize () 가 되 돌아 오 는 값 임 을 나타 낸다
  • 10 표시 MeasureSpec.AT_MOST 는 하위 레이아웃 에서 자신 이 가리 키 지만 getSize () 의 참고 치
  • 를 초과 해 서 는 안 된다 는 뜻 이다.
    ViewGroup 에서 MeasureSpec 을 얻 는 과정measureChildWithMargins 방법 으로 볼 때
        protected void measureChildWithMargins(View child,
                int parentWidthMeasureSpec, int widthUsed,
                int parentHeightMeasureSpec, int heightUsed) {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    
            final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                    mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                            + widthUsed, lp.width);
            final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                    mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                            + heightUsed, lp.height);
    
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }

    5 개의 매개 변 수 를 포함 합 니 다: 자 View, 아버지 WidthMeasureSpec, 아버지 HeightMeasureSpec, 이미 사용 한 너비, 이미 사용 한 높이 의 실행 과정: 1. 먼저 LayoutParams 2. View 를 얻 는 WidthMeasureSpec 3. View 를 얻 는 HeightMeasureSpec 4. 측량 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        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 = 0;
                    resultMode = MeasureSpec.UNSPECIFIED;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = 0;
                    resultMode = MeasureSpec.UNSPECIFIED;
                }
                break;
            }
            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
        }

    실행 과정 1. 부모 specMode 와 specSize 가 져 오기 2. 수평 (수직) 방향 최대 사용 가능 한 공간 size 가 져 오기 3. specMode 와 childDimension (view 의 공간 크기) 을 통 해 하위 View 의 MeasureSpec 를 확인 합 니 다. 하위 View 컨트롤 의 점용 크기 는 하위 View 와 그의 ViewGroup 이 공동으로 결정 한 것 임 을 알 고 있 습 니 다. 구체 적 인 관 계 는 다음 표를 참고 하 십시오.
    parentSpecMode & childViewSize
    EXACTLY
    AT_MOST
    UNSPECIFIED
    확 정 된 값: 100 dp
    EXACTILY & childSize
    AT_MOST& childSize
    AT_MOST& childSize
    match_parent
    EXACTILY & parentLeftSize
    AT_MOST& parentLeftSize
    UNSPECIFIED & 0
    wrap_content
    AT_MOST& parentLeftSize
    AT_MOST& parentLeftSize
    UNSPECIFIED & 0
    시 계 를 통 해 제거 할 수 있 습 니 다. 하위 View 가 구체 적 인 값 이 라면 부모 View Group 의 측정 모드 에 관 계 없 이 그 는 EXACTILY + View 입 니 다.
    사용자 정의 View 우리 가 뭘 더 해 야 하 는 지
    parentSpecMode & childViewSize
    EXACTLY
    AT_MOST
    UNSPECIFIED
    확 정 된 값: 100 dp
    EXACTILY & childSize
    AT_MOST& childSize
    AT_MOST& childSize
    match_parent
    EXACTILY & parentLeftSize
    AT_MOST& parentLeftSize
    UNSPECIFIED & 0
    wrap_content
    AT_MOST& parentLeftSize
    AT_MOST& parentLeftSize
    UNSPECIFIED & 0
    원본 코드 에서 얻 은 표 에 굵 은 기울 임 꼴 두 가 지 를 추가 하 는 것 은 논리 적 으로 문제 가 있 습 니 다. 예 를 들 어 하나의 컨트롤 이 그의 높이 를 android:height=wrap_content 로 지정 하면 아버지 View Group 의 크기 와 일치 하 는 것 이 아니 라 그 자신 이 높이 를 설정 해 야 합 니 다.AT_MOST& parentLeftSize wrap 로 설정content 와 matchparent 는 다 르 지 않 습 니 다. 실제로 시스템 사용자 정의 컨트롤 TextView ImageViewonMeasure 방법 도 바 뀌 었 습 니 다. wrap_content 모드 에서 하위 View 가 자신의 크기 를 가리 키 도록 합 니 다.다시 쓰기 onMeasure() 구현 코드:
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //      ,      
        super.onMeasure(widthMeasureSpec , heightMeasureSpec);  
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);  
        int widthSpceSize = MeasureSpec.getSize(widthMeasureSpec);  
        int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);  
        int heightSpceSize=MeasureSpec.getSize(heightMeasureSpec);  
    
    //     At_Most          if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){ 
            setMeasuredDimension(mWidth, mHeight);  
        }else if(widthSpecMode==MeasureSpec.AT_MOST){  
            setMeasuredDimension(mWidth, heightSpceSize);  
        }else if(heightSpecMode==MeasureSpec.AT_MOST){  
            setMeasuredDimension(widthSpceSize, mHeight);  
        }  
     }  

    위의 코드 에서 wrap콘 텐 츠 를 설정 할 때 mWidth mHeight 에 기본 높이 를 설정 하면 됩 니 다. 구체 적 인 값 은 구체 적 으로 분석 해 야 합 니 다.
    논리 적 으로 아직도 잘못된 점 이 존재 한다
    parentSpecMode & childViewSize
    EXACTLY
    AT_MOST
    UNSPECIFIED
    확 정 된 값: 100 dp
    EXACTILY & childSize
    AT_MOST& childSize
    AT_MOST& childSize
    match_parent
    EXACTILY & parentLeftSize
    AT_MOST& parentLeftSize
    UNSPECIFIED & 0
    wrap_content
    AT_MOST& parentLeftSize
    AT_MOST& parentLeftSize
    UNSPECIFIED & 0
    분석 자 View 가 math_parent 아버지 View Group 이 AT 라면모스 트 의 경 우 는 존재 하 는가?
  • View Group 은 확실한 값 입 니 다. 그러면 첫 번 째 가로줄 의 정보 에 따 르 면 그 는 틀림없이 EXACTILY 유형 입 니 다. 오류
  • View Group 은 wrapcontent, 그럼 자 View 는 matchparent, 하위 View 의 크기 는 부모 View Group 의 크기 에 달 려 있 지만 부모 View Group 의 크기 는 그 가 포함 하 는 내용 에 따라 확정 되 고 서로 의존 하 며 운영 체제 의 잠 금 에 해당 하기 때문에 이러한 상황 은 더 이상 존재 하지 않 습 니 다.
  • View Group 은 matchcontent, 그러면 View Group 의 부모 레이아웃 도 match 일 것 입 니 다.content 의 (wrap content 와 확 정 된 값 이 라면 위의 1, 2 분석 을 참고 할 수 있 습 니 다) 이 를 통 해 View Group 의 부모 레이아웃 을 유추 하 는 부모 레이아웃 도 match 일 것 입 니 다.content, 한 번 유추....................................................................content 유형, 핸드폰 화면 은 객관 적 으로 존재 하고 크기 가 있 는 사물 이기 때문에 View Group 은 match콘 텐 츠 라 는 유형 도 존재 하지 않 습 니 다.한 마디 로 View 를 match 로 만 들 수 있 는 상황 은 없다.parent 타 입 이 고 View 의 부모 레이아웃 은 AT 입 니 다.가장

  • 마지막.
    measure 과정 은 View 3 대 절차 중 가장 복잡 한 것 으로 measure 가 완 성 된 후에 getWidthMeasure() getHeightMeasure() 방법 을 통 해 정확 한 너비 와 높이 를 얻 을 수 있다.그러나 일부 특수 한 상황 에서 시스템 은 여러 번 measure 를 해 야 최종 너비 와 높이 를 확정 할 수 있 고 상황 에 따라 onMeasure 방법 에서 얻 은 측정 너비 가 정확 하지 않 을 수 있다.좋 은 습관 은 onLayout() 에서 View 의 측정 너비 와 최종 너비 와 높이 를 얻 는 것 이다.Activity 가 시작 되 자마자 View 의 너비 와 높이 를 가 져 오 는 것 이 필요 합 니 다.라 이 프 사이클 방법 onCreate()/onStart()/onResume() 에서 생각 할 수 있 지만 measure 와 라 이 프 사이클 방법 이 일치 하지 않 습 니 다. 특정한 라 이 프 사이클 방법 에서 얻 고 measure 가 아직 실행 되 지 않 았 을 때 받 은 값 은 0 일 가능성 이 높 습 니 다. onWindowFocusChanged() View 를 통 해 그 려 진 후에 초점 이 바 뀔 것 입 니 다. 또한 onResume 과 onPause 를 자주 진행 하면 onWindowFocusChanged()코드 도 실행 합 니 다:
        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            if (hasFocus) {
                int width = view.getMeasuredWidth();
                int height = view.getMeasuredHeight();
            }
        }

    view 에서 스 레 드 post 메 시 지 를 끝 에 그립 니 다. view 그리 기 가 완료 되면 이 runnable 을 실행 합 니 다.
        @Override
        protected void onStart() {
            super.onStart();
            view.post(new Runnable() {
                @Override
                public void run() {
                    int width = view.getMeasuredWidth();
                    int height = view.getMeasuredHeight();
                }
            });
        }
    

    참고: - 임 옥 강

    좋은 웹페이지 즐겨찾기