TextView가 설치된 AutoSize

9521 단어 Androidtech
이 글은 FOLIO Advent Calendar 열흘째 되는 글이다.
며칠 전 앱에서 TextView의 AutoSize를 사용할 기회가 생겼는데, 어떻게 설치하는지 신경 쓰여서 특별한 달력의 계절도 있어서 실제로 보기로 했어요!
(실제로 보니까 설치가 복잡하지 않아서 내용이 너무 짧아요.)

AutoSize 소개


먼저 TextView의 AutoSize를 간단히 확인하고 싶습니다.
TextView의 AutoSize는 TextView의 너비, 높이, 문자열 길이에 따라 자동으로 TextSize를 조정하는 기능입니다.
AutoSize에 필요한 설정은 다음 네 가지 요소입니다.
autoSizePreseSizes라는 것도 있는데 미리 정해진 사이즈로 TextSize를 조정하는 경우도 있지만, 이번에는 결정된 범위 내에서 지정된 절차에 따라 사이즈를 변경하는 다음과 같은 설정 방법을 주로 처리한다.android:autoSizeMinTextSize자동 조정 시 최소 TextSizeandroid:autoSizeMaxTextSize자동 조정 시 최대 TextSizeandroid:autoSizeStepGranularity여기에 설정된 TextSize를 사용하여 각각 확대 또는 축소android:autoSizeTextType설정uniform시 자동 조정, 설정none시 자동 조정 안 함
실제로 설정하면 ↓ 같은 동작이 됩니다.
GIF에 오를 수 있다면 올라가고 싶어요.
이 네 가지 요소만 설정하면 TextView의 AutoSize 자체는 간단하게 설정할 수 있습니다.

AutoSize 내부 설치


다음은 주제입니다. AutoSize의 내부 설치가 어떤지 보고 싶습니다.
TextView반부터 거슬러 올라가면 AppCompatTextViewAutoSizeHelper반이 있습니다.
이 분류에 대한 주석
Utility class which encapsulates the logic for the TextView auto-size text feature
이런 기록이 있다.
이 반에서 오토시즈 관련 처리가 이뤄졌기 때문에 이 반 내용을 보면 어떻게 시행됐는지 알 수 있다.

setupAutoSizeText()

AppCompatTextViewAutoSizeHelper 자세히 보니 setupAutoSizeText() 설치 방법이 있어 여기서부터 살펴보자.
    private boolean setupAutoSizeText() {
        if (supportsAutoSizeText()
                && mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) {
            // Calculate the sizes set based on minimum size, maximum size and step size if we do
            // not have a predefined set of sizes or if the current sizes array is empty.
            if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
                // Calculate sizes to choose from based on the current auto-size configuration.
                final int autoSizeValuesLength = ((int) Math.floor((mAutoSizeMaxTextSizeInPx
                        - mAutoSizeMinTextSizeInPx) / mAutoSizeStepGranularityInPx)) + 1;
                final int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
                for (int i = 0; i < autoSizeValuesLength; i++) {
                    autoSizeTextSizesInPx[i] = Math.round(
                            mAutoSizeMinTextSizeInPx + (i * mAutoSizeStepGranularityInPx));
                }
                mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
            }
            mNeedsAutoSizeText = true;
        } else {
            mNeedsAutoSizeText = false;
        }

        return mNeedsAutoSizeText;
    }
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java;l=572?q=AutoSizeHelper&hl=ja
먼저 설정된autoSizeMaxTextautoSizeMinTextSize 사이의 차이autoSizeStepGranularity를 나누고, 먼저 설정할 수 있는 TextSize의 개수를 계산한다.
그리고 autoSizeMinTextSize를 초기값으로 순서대로 autoSizeStepGranularity를 더하면 범위를 설정할 수 있는 TextSize를 모두 그룹에 저장합니다.
그런 다음 계산된 값을 cleanupAutoSizePresetSizes에 전달하고 값이 Positive인지, 중복된 값이 있는지 확인합니다.
그런 다음 계산된 값을 mAutoSizeTextSizesInPx에 저장합니다.
따라서 AutoSize 설치로 범위를 쉽게 설정할 수 있는 TextSize를 모두 계산한 후 저장하는 작업이 수행되었습니다.

autoSizeText()


다음은 본명autoSizeText 방법을 보겠습니다.
   void autoSizeText() {
        if (!isAutoSizeEnabled()) {
            return;
        }

        if (mNeedsAutoSizeText) {
            if (mTextView.getMeasuredHeight() <= 0 || mTextView.getMeasuredWidth() <= 0) {
                return;
            }

            final boolean horizontallyScrolling = mImpl.isHorizontallyScrollable(mTextView);
            final int availableWidth = horizontallyScrolling
                    ? VERY_WIDE
                    : mTextView.getMeasuredWidth() - mTextView.getTotalPaddingLeft()
                            - mTextView.getTotalPaddingRight();
            final int availableHeight = mTextView.getHeight() - mTextView.getCompoundPaddingBottom()
                    - mTextView.getCompoundPaddingTop();

            if (availableWidth <= 0 || availableHeight <= 0) {
                return;
            }

            synchronized (TEMP_RECTF) {
                TEMP_RECTF.setEmpty();
                TEMP_RECTF.right = availableWidth;
                TEMP_RECTF.bottom = availableHeight;
                final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
                if (optimalTextSize != mTextView.getTextSize()) {
                    setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize);
                }
            }
        }
        // Always try to auto-size if enabled. Functions that do not want to trigger auto-sizing
        // after the next layout pass should set this to false.
        mNeedsAutoSizeText = true;
    }
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java;l=602?q=AutoSizeHelper&hl=ja
여기서 먼저 AutoSize가 설정한 TextView의 width와 Height를 획득했다.얻은 TextView의 크기는 findLargestTextSizeWhichFits 방법으로 전달됩니다.
   private int findLargestTextSizeWhichFits(RectF availableSpace) {
        final int sizesCount = mAutoSizeTextSizesInPx.length;
        if (sizesCount == 0) {
            throw new IllegalStateException("No available text sizes to choose from.");
        }

        int bestSizeIndex = 0;
        int lowIndex = bestSizeIndex + 1;
        int highIndex = sizesCount - 1;
        int sizeToTryIndex;
        while (lowIndex <= highIndex) {
            sizeToTryIndex = (lowIndex + highIndex) / 2;
            if (suggestedSizeFitsInSpace(mAutoSizeTextSizesInPx[sizeToTryIndex], availableSpace)) {
                bestSizeIndex = lowIndex;
                lowIndex = sizeToTryIndex + 1;
            } else {
                highIndex = sizeToTryIndex - 1;
                bestSizeIndex = highIndex;
            }
        }

        return mAutoSizeTextSizesInPx[bestSizeIndex];
    }
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java;l=696?q=AutoSizeHelper&hl=ja
그리고 findLargestTextSizeWhichFits에서는 TextView에 수용되는 가장 큰 TextSize를 계산합니다.
내용으로 준비lowIndexhighIndex, lowIndexには를 초기값으로 하고 1 대입highIndex에서 계산한 setupAutoSizeText의length-1(어쨌든 최대 TextSize의 Index)을 대체한다.
그리고 플러스mAutoSizeTextSizesInPxlowIndex를 2로 나눈 인덱스의 사이즈를 건네주고highIndex 크기가 큰지 작은지 판정한다.
크기가 크면(TextView 초과) -> suggestedSizeFitsInSpace 현재 테스트된 사이즈의 Index-1로 업데이트되며highIndex 이 값을 유지합니다.
크기가 작으면(TextView에 수납) -> bestSizeIndex 현재 크기bestSizeIndex를 유지하고, lowIndex를 현재 시도하는 사이즈의 Index+1으로 업데이트합니다.lowIndex가 성립되었을 때 이 조작을 반복합니다.
예.
mAutoSizeTextSizesInPx = [1, 2, 3, 4]
mAutoSizeTextSizesInPx.length = 4
の場合、
lowIndex = 1
highIndex = 3
でスタート。

(1 + 3) / 2 = 2

TextViewに収まる場合
lowIndex = 2 + 1 = 3
highIndex = 3
=> 収まる場合TextSizeは4(mAutoSizeTextSizesInPx[3])
=> 収まらない場合TextSizeは3(mAutoSizeTextSizesInPx[2])

TextViewに収まらない場合
highIndex = 2 - 1 = 1
lowIndex = 1
=> 収まる場合TextSizeは2(mAutoSizeTextSizesInPx[1])
=> 収まらない場合TextSizeは1(mAutoSizeTextSizesInPx[0])
TextSize를 이렇게 판정하여 AutoSize를 실현하였다.

AutoSize 실행 시기


AutoSize가 TextSize를 어떻게 조정하는지 알았기 때문에 마지막으로 AutoSize가 어느 시기에 실행되었는지 확인하고 싶습니다.lowIndex <= highIndex에서 참고원을 거슬러 올라가 마침내autoSizeText()에 도착했다.
TextView에서 설정한 문자열이 변경될 때마다 AutoSize가 실행됩니다.

총결산

AppCompatTextView#onTextChanged()에서 물론 이번에 본 것 외에도 많은 방법과 처리 방법이 있었지만 이번에는 기본적인 부분만 봤는데 의외로 간단하게 이루어져서 놀랐어요.
그리고 AutoSize 실행 시기를 조사할 때 Buton도 autoSize를 사용할 수 있다는 것을 알아차렸다.(Buton에서 AutoSize를 사용하는 경우는 거의 없는 것 같습니다.)
설치 계기를 살펴보려고 했는데 그룹 린터Layout이 가로로 늘어선 TextView가 AutoSize를 할 수 있는지 없는지가 계기였지만, 이번 설치를 보니 맞춤 제작이 가능할 것 같았다.
가로로 늘어선 TextView가 AutoSize, 높이AppCompatTextViewAutoSizeHelper를 지정할 수 있도록 도전해 보고 싶습니다. 다음에 시도해 보세요!

좋은 웹페이지 즐겨찾기