AndroidUI 그리기 프로세스

Android 소스 코드 읽기 UI 그리기 프로세스

환경

  • 자바 11
  • 안드로이드 11

강좌 API의 불일치로 인해 소스 코드의 일부 주요 메소드를 입력할 수 없으므로 주의 깊게 읽고 스크린샷을 사용하여 아이디어를 이해하세요.

창에 뷰가 추가됨

소스 코드를 입력해 보면 각 액티비티의 기본 생성 코드에 setContentView 메소드가 있음을 알 수 있는데, 이 메소드는 입력 경로의 현재 레이아웃과 후속 작업을 로드하는 데 사용됩니다.

기본 본질은 대리자를 호출하여 리소스를 설정하는 것입니다.

  @Override
    public void setContentView(@LayoutRes int layoutResID) {
    
    
        initViewTreeOwners();
        getDelegate().setContentView(layoutResID);
    }

계속해서 이 대리자 클래스의 인터페이스 구현 방법을 살펴보세요.

이미지-20230711161927489

    @Override
    public void setContentView(int resId) {
    
    
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

ensureSubDecor()이 장식 방법을 입력하십시오.아래 그림은 코드의 일부를 보여줍니다.

     if (!mSubDecorInstalled) 
     mSubDecor = createSubDecor();

createSubDecor()실제 설치 가치를 기준으로 해당 장식을 제작할지 여부를 결정합니다.

이미지-20230711162250175

이 방법에는 많은 양의 시스템 리소스 통합이 필요하며 일부 시스템 테마 데코레이터는 다음과 같이 생성해야 하는 해당 창에 배치됩니다.

  • 콘텐츠
  • 테마컨텍스트
  • subDecor 및 기타 시스템 리소스

계속 아래를 내려다보면 mWindow.setContentView(subDecor);값을 할당하는 할당 작업이 표시됩니다.

추신: WINDOW 개체의 주요 구현은 전화 창에 있으므로 이 기본 메서드를 찾기 위해 전화 창으로 이동합니다.

이 메소드에는 두 가지 주요 구현이 있는데 여기서는 주로layoutResId가 매개변수인 구현 내용을 살펴봅니다.

   @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);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
    
    
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

이미지-20230711164741590

generateLayout여기 에서 메서드를 클릭합니다 . 이 메서드는 주로 레이아웃에 필요한 테마 속성과 스타일 속성을 조합하는 데 사용됩니다. 여기를 참조하세요.

  mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

리소스를 로드하는 방법은 다양한 기능에 따라 레이아웃에 다른 값을 할당한 다음 레이아웃 정보를 mdecor에 추가하는 방식입니다.

그런 다음 generateLayout()은 viewGroup인 contentParent를 반환합니다.

흐름도

이미지-20230711172049149

요약하다

이미지-20230711172822341

  1. 액티비티에 들어간 후 시스템에서 요구하는 테마 매개변수를 설정하기 위해 최상위 레이아웃 decorView가 먼저 생성됩니다.
  2. 기본 레이아웃에 decorView를 추가하고 viewGroup을 반환합니다. 다양한 테마는 다양한 viewGroup을 로드하지만 모두 컨테이너 OnDraw라는 ID를 갖습니다.
  3. 마지막으로 setContentView에 의해 레이아웃에 추가됨

그리기 과정 보기

이미지-20230711163004442

위 그림에 따르면 먼저 ActivityThread클래스 H를 확인하고 찾을 수 있습니다. 이 클래스는 핸들을 상속하고 handleMessage내부적으로 메서드를 구현하며 내부에 호출 링크가 있습니다.

  1. handlerMessage는 선택된 활동의 시작을 식별하기 위해 handlerLaunchActivity를 호출합니다.
  2. handlerLaunchActivity 调사용 handlerResumeActivity
  3. handlerResumeActivity는 PerformResumeActivity를 호출하고 마지막으로 이 메서드에서 다시 호출합니다.

이미지-20230711175042268

performTraversal()이 호출 링크인 viewRootImpl 메소드에서 뷰를 그리는 세 단계가 이 스레드에서 완료된다는 점에 유의해야 합니다 .

이미지-20230711175532161

이미지-20230711175608056

이미지-20230711175632714

측정

이미지-20230711175815720

​ 최하위 소스코드의 측정을 보면 실제로 측정을 실행한다는 것은 최하위의 setMeasureDimensionRow를 연속적으로 호출하여 두 멤버 변수에 값을 할당하고 측정 컨트롤의 너비와 높이를 저장하는 것임을 알 수 있다.

​ 뷰를 그리기 전에 안드로이드의 최하위 레이어는 먼저 measure해당 뷰의 측정 모드와 크기를 사용하게 됩니다. 안드로이드는 32비트 int 저장 값인 Measurespec이라는 클래스를 제공합니다. 처음 두 자리는 모드를 나타냅니다. , 마지막 30자리는 크기를 나타냅니다.

  • SpecMode(처음 2자리) + SpecSize(마지막 30자리)

핵심 방법

makeMeasureSpec

이미지-20230711180346695

값 모드를 사용하려면 크기의 처음 두 자리만 사용하면 되고, MeasureSpec을 구성하려면 마지막 30자리만 사용하면 됩니다.

마찬가지로 MeasureSpec도 해당 값을 갖고 getsize해당 getmode값을 얻습니다.

getRootMeasureSpec

매개변수

  1. 하위 컨테이너 높이/너비
  2. 최상위 컨테이너 높이/너비

설정된 레이아웃 속성에 따라 해당 모드를 설정합니다.

이미지-20230711181416911

이미지-20230711181510179

PerformMeasure를 도와 필요한 측정 사양을 얻으세요.

이미지-20230711181120529

데코 뷰는 프레임 레이아웃을 상속하고 해당 onMeasure 메소드를 봅니다.

재귀를 통해 상위 컨테이너와 하위 컨트롤의 측정 방법에 대한 다양한 규칙을 결정하고 설정합니다.

이미지-20230711182103316

측정 비교 차트 보기

이미지-20230711182200787

자신의 프로세스를 완벽하게 측정

이미지-20230711183000873

이미지-20230711182532080

ps: 뷰를 사용자 정의할 때 onmeasureDiemsion 메서드를 재정의해야 합니다.

뷰가 자체적으로 설정하고 측정하기 전에 getdefaultsize가 호출되어 다양한 모드에 따른 크기를 얻습니다.흥미롭게도 at 및 정확하게 할당은 상위 컨테이너의 나머지 크기이므로 사용자 정의 뷰에서 일련의 메서드를 다시 작성해야 합니다. 그렇지 않으면 문제가 발생할 수 있습니다.

이미지-20230711183506916

사용자 정의 컨테이너와 viwe의 차이점은 다음과 같습니다.

  1. 사용자 지정 컨테이너는 먼저 하위 컨트롤을 측정한 다음 자체적으로 측정해야 합니다.
  2. 뷰 자체를 직접 측정할 수 있음

공들여 나열한 것

이미지-20230711183856133

핵심 방법

view.setFrame()

이미지-20230711184313298

컨트롤의 상하좌우 값을 설정한 후 컨트롤을 배치해야 하는 위치를 결정합니다.

view.onlayout()

이때 컨테이너인 경우 이 메서드를 호출하여 자식 컨트롤의 위치를 ​​확인해야 합니다.

그리다

이미지-20230711185425080

핵심 방법

뷰.그리기()
        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         *      7. If necessary, draw the default focus highlight
         */

이미지-20230711185902270

viewGroup.dispatchDraw()

이미지-20230711190035041

요약하다

드로잉 과정

뷰그룹

  1. 배경 그리기
  2. 자신을 그려라
  3. 자식 컨트롤 그리기
  4. 전경 스크롤 막대 등을 그립니다.

보다

  1. 배경 그리기
  2. 자신만의 온드로우를 호출하세요
  3. 전경 스크롤 막대 등을 그립니다.

Supongo que te gusta

Origin blog.csdn.net/doomwatcher/article/details/131667276
Recomendado
Clasificación