Android 용 TextView 자동 맞춤


231

배경

여러 번 TextView의 글꼴을 주어진 경계에 자동 맞춤해야합니다.

문제

안타깝게도이 문제 (예 : here , herehere ) 에 대해 이야기하는 스레드 및 게시물 (및 제안 된 솔루션)이 많이 있지만 실제로는 잘 작동하지 않습니다.

그래서 나는 실제 거래를 찾을 때까지 각각을 테스트하기로 결정했습니다.

그러한 textView의 요구 사항은 다음과 같아야한다고 생각합니다.

  1. 글꼴, 서체, 스타일 및 문자 집합을 사용할 수 있어야합니다.

  2. 너비와 높이를 모두 처리해야합니다

  3. 제한으로 인해 텍스트를 맞출 수 없으면 잘리지 않습니다 (예 : 너무 긴 텍스트, 사용 가능한 작은 크기). 그러나 원하는 경우 가로 / 세로 스크롤 막대를 요청할 수 있습니다.

  4. 여러 줄 또는 한 줄을 허용해야합니다. 여러 줄의 경우 최대 및 최소 줄을 허용하십시오.

  5. 계산 속도가 느려서는 안됩니다. 최상의 크기를 찾기 위해 루프를 사용하십니까? 적어도 최적화하고 매번 샘플링을 1 씩 늘리지 마십시오.

  6. 여러 줄의 경우 크기 조정 또는 더 많은 줄 사용을 선호하거나 "\ n"문자를 사용하여 줄을 직접 선택할 수 있어야합니다.

내가 시도한 것

나는 많은 샘플 (링크의 샘플을 포함하여)을 시도했으며, 사례를 처리하기 위해 샘플을 수정하려고 시도했지만 실제로 이야기하지는 않았습니다.

TextView가 자동으로 맞는지 시각적으로 확인할 수있는 샘플 프로젝트를 만들었습니다.

현재 샘플 프로젝트는 텍스트 (영어 알파벳과 숫자)와 textView의 크기 만 무작위로 지정하고 한 줄로 유지하도록하지만, 시도한 샘플에서는 제대로 작동하지 않습니다.

코드는 다음과 같습니다 ( 여기 에서도 사용 가능 ).

파일 res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  android:layout_height="match_parent" tools:context=".MainActivity">
  <Button android:id="@+id/button1" android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true" android:text="Button" />
  <FrameLayout android:layout_width="match_parent"
    android:layout_height="wrap_content" android:layout_above="@+id/button1"
    android:layout_alignParentLeft="true" android:background="#ffff0000"
    android:layout_alignParentRight="true" android:id="@+id/container"
    android:layout_alignParentTop="true" />

</RelativeLayout>

파일 src/.../MainActivity.java

public class MainActivity extends Activity
  {
  private final Random        _random            =new Random();
  private static final String ALLOWED_CHARACTERS ="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";

  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewGroup container=(ViewGroup)findViewById(R.id.container);
    findViewById(R.id.button1).setOnClickListener(new OnClickListener()
      {
        @Override
        public void onClick(final View v)
          {
          container.removeAllViews();
          final int maxWidth=container.getWidth();
          final int maxHeight=container.getHeight();
          final FontFitTextView fontFitTextView=new FontFitTextView(MainActivity.this);
          final int width=_random.nextInt(maxWidth)+1;
          final int height=_random.nextInt(maxHeight)+1;
          fontFitTextView.setLayoutParams(new LayoutParams(width,height));
          fontFitTextView.setSingleLine();
          fontFitTextView.setBackgroundColor(0xff00ff00);
          final String text=getRandomText();
          fontFitTextView.setText(text);
          container.addView(fontFitTextView);
          Log.d("DEBUG","width:"+width+" height:"+height+" text:"+text);
          }
      });
    }

  private String getRandomText()
    {
    final int textLength=_random.nextInt(20)+1;
    final StringBuilder builder=new StringBuilder();
    for(int i=0;i<textLength;++i)
      builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length())));
    return builder.toString();
    }
  }

질문

실제로 작동하는 일반적인 문제에 대한 해결책을 아는 사람이 있습니까?

심지어 내가 작성한 것보다 훨씬 적은 기능을 가진 솔루션조차, 예를 들어 일정한 수의 텍스트 줄을 가지고 크기에 따라 글꼴을 조정하지만 이상한 결함이 없으며 텍스트도 얻지 못하는 솔루션 사용 가능한 공간과 비교하여 크거나 작습니다.


GitHub 프로젝트

이것이 중요한 TextView이기 때문에 모든 사람들이 쉽게 라이브러리를 사용하고 여기에 기여할 수 있도록 라이브러리를 게시하기로 결정했습니다 .



@ Thrakbad 그것은 내가 언급 한 링크 중 하나입니다. 또한 테스트를 통과하지 못합니다.
안드로이드 개발자

아 죄송합니다, 어쨌든 마지막 예를 놓쳤습니다
Thrakbad

예, 제발 믿어주세요. 많은 샘플을 시험해 보았으며 찾은 문제를 해결하기 위해 샘플을 수정하려고했지만 성공하지 못했습니다. 효과가 있다고 생각되면 테스트 해보십시오. 이를 위해 샘플 코드를 게시했습니다.
안드로이드 개발자

1
@rule 이것은 이미 읽은 게시물 중 하나이며 모든 코드 샘플을 테스트했습니다. 또한 당신은 이중 게시 된 것 같아요.
안드로이드 개발자

답변:


147

MartinH의 간단한 수정 덕분에 이곳은 ,이 코드는 처리 소요 android:drawableLeft, android:drawableRight, android:drawableTopandroid:drawableBottom태그를.


여기에 내 대답은 당신이 행복해야한다 경계에 맞도록 자동 스케일 텍스트 뷰의 텍스트를

테스트 사례를 수정했습니다.

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewGroup container = (ViewGroup) findViewById(R.id.container);
    findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(final View v) {
            container.removeAllViews();
            final int maxWidth = container.getWidth();
            final int maxHeight = container.getHeight();
            final AutoResizeTextView fontFitTextView = new AutoResizeTextView(MainActivity.this);
            final int width = _random.nextInt(maxWidth) + 1;
            final int height = _random.nextInt(maxHeight) + 1;
            fontFitTextView.setLayoutParams(new FrameLayout.LayoutParams(
                    width, height));
            int maxLines = _random.nextInt(4) + 1;
            fontFitTextView.setMaxLines(maxLines);
            fontFitTextView.setTextSize(500);// max size
            fontFitTextView.enableSizeCache(false);
            fontFitTextView.setBackgroundColor(0xff00ff00);
            final String text = getRandomText();
            fontFitTextView.setText(text);
            container.addView(fontFitTextView);
            Log.d("DEBUG", "width:" + width + " height:" + height
                    + " text:" + text + " maxLines:" + maxLines);
        }
    });
}

안드로이드 개발자의 요청 에 따라 여기에 코드를 게시하고 있습니다 .

최종 효과 :

여기에 이미지 설명을 입력하십시오

샘플 레이아웃 파일 :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp" >

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:ellipsize="none"
    android:maxLines="2"
    android:text="Auto Resized Text, max 2 lines"
    android:textSize="100sp" /> <!-- maximum size -->

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:ellipsize="none"
    android:gravity="center"
    android:maxLines="1"
    android:text="Auto Resized Text, max 1 line"
    android:textSize="100sp" /> <!-- maximum size -->

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Auto Resized Text"
    android:textSize="500sp" /> <!-- maximum size -->

</LinearLayout>

그리고 자바 코드 :

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Build;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.widget.TextView;

public class AutoResizeTextView extends TextView {
    private interface SizeTester {
        /**
         *
         * @param suggestedSize
         *            Size of text to be tested
         * @param availableSpace
         *            available space in which text must fit
         * @return an integer < 0 if after applying {@code suggestedSize} to
         *         text, it takes less space than {@code availableSpace}, > 0
         *         otherwise
         */
        public int onTestSize(int suggestedSize, RectF availableSpace);
    }

    private RectF mTextRect = new RectF();

    private RectF mAvailableSpaceRect;

    private SparseIntArray mTextCachedSizes;

    private TextPaint mPaint;

    private float mMaxTextSize;

    private float mSpacingMult = 1.0f;

    private float mSpacingAdd = 0.0f;

    private float mMinTextSize = 20;

    private int mWidthLimit;

    private static final int NO_LINE_LIMIT = -1;
    private int mMaxLines;

    private boolean mEnableSizeCache = true;
    private boolean mInitializedDimens;

    public AutoResizeTextView(Context context) {
        super(context);
        initialize();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initialize();
    }

    private void initialize() {
        mPaint = new TextPaint(getPaint());
        mMaxTextSize = getTextSize();
        mAvailableSpaceRect = new RectF();
        mTextCachedSizes = new SparseIntArray();
        if (mMaxLines == 0) {
            // no value was assigned during construction
            mMaxLines = NO_LINE_LIMIT;
        }
    }

    @Override
    public void setTextSize(float size) {
        mMaxTextSize = size;
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    @Override
    public void setMaxLines(int maxlines) {
        super.setMaxLines(maxlines);
        mMaxLines = maxlines;
        adjustTextSize();
    }

    public int getMaxLines() {
        return mMaxLines;
    }

    @Override
    public void setSingleLine() {
        super.setSingleLine();
        mMaxLines = 1;
        adjustTextSize();
    }

    @Override
    public void setSingleLine(boolean singleLine) {
        super.setSingleLine(singleLine);
        if (singleLine) {
            mMaxLines = 1;
        } else {
            mMaxLines = NO_LINE_LIMIT;
        }
        adjustTextSize();
    }

    @Override
    public void setLines(int lines) {
        super.setLines(lines);
        mMaxLines = lines;
        adjustTextSize();
    }

    @Override
    public void setTextSize(int unit, float size) {
        Context c = getContext();
        Resources r;

        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();
        mMaxTextSize = TypedValue.applyDimension(unit, size,
                r.getDisplayMetrics());
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the lower text size limit and invalidate the view
     *
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        adjustTextSize();
    }

    private void adjustTextSize() {
        if (!mInitializedDimens) {
            return;
        }
        int startSize = (int) mMinTextSize;
        int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom()
                - getCompoundPaddingTop();
        mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft()
                - getCompoundPaddingRight();
        mAvailableSpaceRect.right = mWidthLimit;
        mAvailableSpaceRect.bottom = heightLimit;
        super.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                efficientTextSizeSearch(startSize, (int) mMaxTextSize,
                        mSizeTester, mAvailableSpaceRect));
    }

    private final SizeTester mSizeTester = new SizeTester() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public int onTestSize(int suggestedSize, RectF availableSPace) {
            mPaint.setTextSize(suggestedSize);
            String text = getText().toString();
            boolean singleline = getMaxLines() == 1;
            if (singleline) {
                mTextRect.bottom = mPaint.getFontSpacing();
                mTextRect.right = mPaint.measureText(text);
            } else {
                StaticLayout layout = new StaticLayout(text, mPaint,
                        mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
                        mSpacingAdd, true);

                // Return early if we have more lines
                if (getMaxLines() != NO_LINE_LIMIT
                        && layout.getLineCount() > getMaxLines()) {
                    return 1;
                }
                mTextRect.bottom = layout.getHeight();
                int maxWidth = -1;
                for (int i = 0; i < layout.getLineCount(); i++) {
                    if (maxWidth < layout.getLineWidth(i)) {
                        maxWidth = (int) layout.getLineWidth(i);
                    }
                }
                mTextRect.right = maxWidth;
            }

            mTextRect.offsetTo(0, 0);
            if (availableSPace.contains(mTextRect)) {

                // May be too small, don't worry we will find the best match
                return -1;
            } else {
                // too big
                return 1;
            }
        }
    };

    /**
     * Enables or disables size caching, enabling it will improve performance
     * where you are animating a value inside TextView. This stores the font
     * size against getText().length() Be careful though while enabling it as 0
     * takes more space than 1 on some fonts and so on.
     *
     * @param enable
     *            Enable font size caching
     */
    public void enableSizeCache(boolean enable) {
        mEnableSizeCache = enable;
        mTextCachedSizes.clear();
        adjustTextSize(getText().toString());
    }

    private int efficientTextSizeSearch(int start, int end,
            SizeTester sizeTester, RectF availableSpace) {
        if (!mEnableSizeCache) {
            return binarySearch(start, end, sizeTester, availableSpace);
        }
        int key = getText().toString().length();
        int size = mTextCachedSizes.get(key);
        if (size != 0) {
            return size;
        }
        size = binarySearch(start, end, sizeTester, availableSpace);
        mTextCachedSizes.put(key, size);
        return size;
    }

    private static int binarySearch(int start, int end, SizeTester sizeTester,
            RectF availableSpace) {
        int lastBest = start;
        int lo = start;
        int hi = end - 1;
        int mid = 0;
        while (lo <= hi) {
            mid = (lo + hi) >>> 1;
            int midValCmp = sizeTester.onTestSize(mid, availableSpace);
            if (midValCmp < 0) {
                lastBest = lo;
                lo = mid + 1;
            } else if (midValCmp > 0) {
                hi = mid - 1;
                lastBest = hi;
            } else {
                return mid;
            }
        }
        // Make sure to return the last best.
        // This is what should always be returned.
        return lastBest;

    }

    @Override
    protected void onTextChanged(final CharSequence text, final int start,
            final int before, final int after) {
        super.onTextChanged(text, start, before, after);
        adjustTextSize();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldwidth,
            int oldheight) {
        mInitializedDimens = true;
        mTextCachedSizes.clear();
        super.onSizeChanged(width, height, oldwidth, oldheight);
        if (width != oldwidth || height != oldheight) {
            adjustTextSize();
        }
    }
}

경고:

Android 3.1 (Honeycomb) 의이 해결 된 버그 에 주의하십시오 .


좋아, 나는 코드를 테스트했는데 괜찮아 보인다. 그러나 API 16 용 getMaxLines ()를 사용했지만 setMaxLines를 재정의하고 값을 저장하여 maxLines를 저장하고 가져올 수 있습니다. 또한 때로는 텍스트가 가장 작은 크기에 비해 너무 길 수 있으므로 setEllipsize를 사용하려고 시도했지만 그 효과도 있습니다! 승자가 있다고 생각합니다. 올바른 코드로 표시 할 수 있도록 여기에 코드를 게시하십시오.
안드로이드 개발자

getMaxLines ()를 제거하지 않고 직접 사용했습니다. 여전히 API16에서만 작동합니다 ... 모든 사람이 사용할 수 있도록 변경하십시오. 매개 변수를 필드에 저장하고 getMaxLines 대신 필드에 액세스하도록 setMaxLines를 재정의하는 것이 좋습니다.
안드로이드 개발자

지금 확인하고 최대 라인에 대한 지원을 추가했습니다.
M-WaJeEh

괜찮아 보인다. Lint가 오류 / 경고를하지 않도록 대신 onTestSize () 메소드에 "@TargetApi (Build.VERSION_CODES.JELLY_BEAN)"을 추가하여 대신 CTOR에서 초기화해야합니다. 이진 검색을 정적으로 설정하는 것도 좋은 일이며, 가장 큰 CTOR에서 초기화를 수행하고 다른 CTOR에서 호출 할 수 있습니다. 그러나 실제로 가장 좋은 대답이며 V가 필요합니다. 이것은 마침내이 오래된 질문에 대한 좋은 대답입니다. 잘 했어!
안드로이드 개발자

@ M-WaJeEh이 글타래의 새로운 글을 확인하세요. "MartinH"라는 사용자는 자신의 코드를 수정했다고 말합니다.
Android 개발자

14

M-WaJeEh의 답변을 수정하여 측면의 복합 드로어 블을 고려했습니다.

getCompoundPaddingXXXX()방법은 반환 padding of the view + drawable space. 예를 들어 TextView.getCompoundPaddingLeft ()를 참조하십시오.

문제 : 텍스트에 사용 가능한 TextView 공간의 너비와 높이 측정이 수정됩니다. 드로어 블 크기를 고려하지 않으면 무시되고 텍스트가 드로어 블과 겹칩니다.


업데이트 된 세그먼트 adjustTextSize(String):

private void adjustTextSize(final String text) {
  if (!mInitialized) {
    return;
  }
  int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom() - getCompoundPaddingTop();
  mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();

  mAvailableSpaceRect.right = mWidthLimit;
  mAvailableSpaceRect.bottom = heightLimit;

  int maxTextSplits = text.split(" ").length;
  AutoResizeTextView.super.setMaxLines(Math.min(maxTextSplits, mMaxLines));

  super.setTextSize(
      TypedValue.COMPLEX_UNIT_PX,
      binarySearch((int) mMinTextSize, (int) mMaxTextSize,
                   mSizeTester, mAvailableSpaceRect));
}

M-WaJeEh의 소스를 업데이트했습니다. 왜 새로운 답변으로 넣었습니까? 나는 당신이 선의를 가지고 있다는 것을 알고 있지만, 그것을 답으로하는 것이 좋은 것입니까? 나는 그가 당신의 답변을 볼 수 있는지 잘 모르겠습니다 ...
안드로이드 개발자

1
방금 해당 업데이트를 제공 할 수 있도록 가입했습니다. 내 평판은 1에 불과하므로 내 게시물이 아닌 게시물에 댓글을 달지 못합니다 (최소 15 명).
MartinH

걱정할 필요는 없습니다. 아마도 지금은 그에게 연락조차 할 수 없으므로 M-WajeEh에게 제 답변을 전하고 싶습니다 : /
MartinH

무엇을했는지 더 잘 설명해 주시겠습니까? 버그를 고치셨습니까? 버그를 보여주기 위해 스크린 샷을 보여줄 수 있습니까?
Android 개발자

1
안녕하세요 @MartinH, SO에 오신 것을 환영합니다. 나는 이것을 조사하고 당신의 이름을 언급하고 테스트 후 당신의 게시물에 대한 링크를 게시하여 내 답변을 업데이 트할 것 입니다. 며칠 만주세요. 요즘 어딘가에 갇혀 있습니다.
M-WaJeEh

7

Ok 지난주에 테스트 에 정확하게 맞게 코드를 크게 다시 작성했습니다 . 이제이 1 : 1을 복사하면 즉시 작동 setSingleLine()합니다. 조정 MIN_TEXT_SIZE하고 MAX_TEXT_SIZE극단적 인 가치를 추구하는 경우를 잊지 마십시오 .

수렴 알고리즘은 다음과 같습니다.

for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

    // Go to the mean value...
    testSize = (upperTextSize + lowerTextSize) / 2;

    // ... inflate the dummy TextView by setting a scaled textSize and the text...
    mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
    mTestView.setText(text);

    // ... call measure to find the current values that the text WANTS to occupy
    mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
    int tempHeight = mTestView.getMeasuredHeight();

    // ... decide whether those values are appropriate.
    if (tempHeight >= targetFieldHeight) {
        upperTextSize = testSize; // Font is too big, decrease upperSize
    }
    else {
        lowerTextSize = testSize; // Font is too small, increase lowerSize
    }
}

그리고 전체 학급은 여기에서 찾을 수 있습니다.

결과는 매우 유연합니다. 이것은 xml에서 선언 된 것과 동일하게 작동합니다.

<com.example.myProject.AutoFitText
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="4"
    android:text="@string/LoremIpsum" />

... 테스트에서와 같이 프로그래밍 방식으로 구축되었습니다.

나는 당신이 이것을 지금 사용할 수 있기를 바랍니다. setText(CharSequence text)그런데 지금 전화 해서 사용할 수 있습니다 . 수업은 엄청나게 드문 예외를 처리하며 견고해야합니다. 알고리즘이 아직 지원 하지 않는 유일한 것은 :

  • setMaxLines(x)어디로 전화x >= 2

그러나 원하는 경우이를 작성할 수 있도록 광범위한 의견을 추가했습니다.


참고 :

이것을 한 줄로 제한하지 않고 정상적으로 사용하면 앞에서 언급 한 것처럼 단어가 깨질 수 있습니다. 이것은 안드로이드 기능 이며 오류가 아닙니다AutoFitText . Android는 항상 TextView에 비해 너무 긴 단어를 중단하며 실제로는 매우 편리합니다. 203 행에서 시작하는 내 의견과 코드를 보시오. .

결론 : 공백 문자도 지원하도록 테스트를 다시 작성하는 것이 좋습니다.

final Random _random = new Random();
final String ALLOWED_CHARACTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
final int textLength = _random.nextInt(80) + 20;
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < textLength; ++i) {
    if (i % 7 == 0 && i != 0) {
        builder.append(" ");
    }
    builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length())));
}
((AutoFitText) findViewById(R.id.textViewMessage)).setText(builder.toString());

이것은 매우 아름답고 (보다 현실적인) 결과를 낳을 것입니다.
이 문제를 시작하기위한 주석도 있습니다.

행운과 안부


몇 가지 문제가 있지만 전반적으로 잘 작동합니다. 문제 : 멀티 라인을 지원하지 않으며 (작성 한대로) 단어를 쓰려고 할 때 완전히 피할 수 있다고 생각하는 API16 함수 (addOnGlobalLayoutListener)를 포함하십시오 (또는 적어도 OnPreDrawListener를 대신 사용하십시오). 이진 검색을 사용하면 전혀 맞지 않는 크기를 테스트 할 수 있습니다 (그리고 아무 이유없이 더 많은 메모리를 사용하면 너무 큰 경우 예외가 발생할 수 있음). 텍스트 자체와 같이 변경되는 경우 처리를 지원하지 않습니다.
안드로이드 개발자

btw, mTestView 변수를 사용하지 않고 현재 뷰에서 직접 테스트를 수행 할 필요가 없으므로 모든 특별한 것을 고려할 필요는 없습니다. 또한 MAX_TEXT_SIZE 대신 targetFieldHeight 사용할 수 있다고 생각합니다.
안드로이드 개발자

나는 순수한 이진 검색 (특히 그중 2 개) 대신 측정 된 크기에 대한 대상의 비율로 더 나은 추측을 얻을 수 있다고 생각합니다. 다른 방법 (뉴턴과 내가 기억하지 못하는 다른 것들)도 있지만 여기에 적합하다고 생각하지 않습니다. 최대 크기를 테스트하는 대신 최소 크기에서 시작할 수 있기 때문에 지금 제안한 것이 좋습니다. 사실 더 나은 추측을 계속하기 때문에 최대 크기가 필요하지 않습니다.
안드로이드 개발자

또한 조건이 간단 할 수있을 때 2 개의 이진 검색을 한 줄로 병합 할 수 있다고 생각합니다. if (getMeasuredWidth ()> = targetFieldWidth || getMeasuredHeight ()> = targetFieldHeight)
android 개발자

1
내가 찾은 또 다른 문제는 텍스트 중력입니다. 세로로 중앙에 놓을 수 없다고 생각합니다.
안드로이드 개발자

4

내 요구 사항은

  • ScalableTextView를 클릭하십시오
  • listActivity를 열고 다양한 길이의 문자열 항목을 표시하십시오.
  • 목록에서 텍스트를 선택하십시오.
  • 다른 활동에서 텍스트를 ScalableTextView로 다시 설정하십시오.

나는 경계에 맞는 AutoView TextView Text (주석 포함) 링크와 DialogTitle.java 링크를 언급했다.

제공된 솔루션이 훌륭하고 간단하지만 텍스트 상자의 크기를 동적으로 변경하지는 않습니다. 때 좋은 작품 목록보기에서 선택한 텍스트의 길이가에서 기존 텍스트의 아이폰에보다 크기가 크다 ScalableTextView . 에서 기존 텍스트보다 길이가 작은 텍스트를 선택하면 텍스트 ScalableTextView크기가 커지지 않고 텍스트를 더 작은 크기로 표시합니다.

텍스트 길이에 따라 텍스트 크기를 다시 조정하기 위해 ScalableTextView.java를 수정했습니다. 여기, 내 것이요ScalableTextView.java

public class ScalableTextView extends TextView
{
float defaultTextSize = 0.0f;

public ScalableTextView(Context context, AttributeSet attrs, int defStyle)
{
    super(context, attrs, defStyle);
    setSingleLine();
    setEllipsize(TruncateAt.END);
    defaultTextSize = getTextSize();
}

public ScalableTextView(Context context, AttributeSet attrs)
{
    super(context, attrs);
    setSingleLine();
    setEllipsize(TruncateAt.END);
    defaultTextSize = getTextSize();
}

public ScalableTextView(Context context)
{
    super(context);
    setSingleLine();
    setEllipsize(TruncateAt.END);
    defaultTextSize = getTextSize();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    final Layout layout = getLayout();
    if (layout != null)
    {
        final int lineCount = layout.getLineCount();
        if (lineCount > 0)
        {
            int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
            while (ellipsisCount > 0)
            {
                final float textSize = getTextSize();

                // textSize is already expressed in pixels
                setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));

                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                ellipsisCount = layout.getEllipsisCount(lineCount - 1);
            }
        }
    }
}
}

행복한 코딩 ....


각 반복마다 하나씩 감소시키는 대신 이진 검색을 사용하면 안됩니까? 또한이 코드는 TextView에 여러 줄을 허용하는 대신 한 줄의 텍스트가 있어야합니다.
안드로이드 개발자

이것은 나를 위해 일한 유일한 솔루션이었습니다. 내 문제는 TExtView가 4.1에서만보기에 맞지 않는 것과 관련이있다
FOliveira

부모와 100 % 일치하지 않는 것 같습니다. 스크린 샷 참조 : s14.postimg.org/93c2xgs75/Screenshot_2.png
user25

4

이 속성이 더 낮은 Android 버전에서 단계별로 작동하는 방법을 설명합니다.

1- 프로젝트 gradle 파일에서 안드로이드 지원 라이브러리 26.xx를 가져옵니다. IDE에 지원 라이브러리가 없으면 자동으로 다운로드됩니다.

dependencies {
    compile 'com.android.support:support-v4:26.1.0'
    compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support:support-v13:26.1.0' }

allprojects {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
    } }

2- 레이아웃 XML 파일을 열고이 태그처럼 TextView를 리 팩터하십시오. 이 시나리오는 다음과 같습니다. 시스템에서 글꼴 크기가 너무 크면 텍스트 줄 바꿈이 아닌 사용 가능한 너비에 텍스트를 맞 춥니 다.

<android.support.v7.widget.AppCompatTextView
            android:id="@+id/textViewAutoSize"
            android:layout_width="match_parent"
            android:layout_height="25dp"
            android:ellipsize="none"
            android:text="Auto size text with compatible lower android versions."
            android:textSize="12sp"
            app:autoSizeMaxTextSize="14sp"
            app:autoSizeMinTextSize="4sp"
            app:autoSizeStepGranularity="0.5sp"
            app:autoSizeTextType="uniform" />

자동 맞춤 TextView 구현은 제대로 작동하지 않습니다. 글꼴 크기를 조정하는 대신 텍스트가 다음 줄로 줄 바꿈되는 경우가 있습니다 ... youtu.be/fjUdJ2aVqE4?t=14 처럼 비디오에도 표시됩니다 . 은 "텍스트 뷰"의 "W"는 다음 라인으로 어떻게되는지주의 ... 어쨌든, 여기에 대한 새 요청을 생성 : issuetracker.google.com/issues/68787108를 . 출연을 고려하십시오.
안드로이드 개발자

@androiddeveloper 당신이 맞아요. 이 태그 구현이 한 줄로 작동한다고 설명하지 않았습니다. 줄 바꿈이 필요한 경우 android : layout_height = "wrap_content"속성을 변경해야합니다. 그러나 android.support.v7.widget.AppCompatTextView 및 gradle 구현 보장 이이 속성을 작동합니다.
Sinan Ergin

나는 당신이 이해한다고 생각하지 않습니다. 다른 단어를 풀 수있는 방법이 없다면 부분 단어를 다음 줄로 줄이기를 원하지 않습니다. 비디오에서 글꼴이 작아 질 수 있다는 것을 쉽게 알 수 있습니다. 그게 문제입니다. 문제를 해결하는 방법을 알고 있습니까? 단어 줄 바꿈이없고 글꼴 크기 만 변경된다는 의미입니까?
안드로이드 개발자

@androiddeveloper 많은 시나리오가 있습니다. 내 시나리오는 : 자동 줄 바꿈이 아닌 고정 텍스트를 자동 맞춤합니다. 시스템 글꼴 크기를 늘리는 동안 텍스트보기에서 텍스트 줄 바꿈이 표시되지 않아야합니다. 자동 줄 바꿈이 중요하지 않은 경우 고정 높이를 설정하지 않아도됩니다. 이 속성은 자동 줄 바꿈 만 사용하지 않고 텍스트 너비를 TextView로 고정하여 자동으로 맞출 수 있습니다.
Sinan Ergin

내 테스트에 따르면 글꼴 크기가 올바르게 변경되지 않고 줄 바꿈 문제가 발생할 수 있습니다.
안드로이드 개발자

3

Android 3 (Honeycomb) 및 Android 4.0 (Ice Cream Sandwich)의 경고, 버그

안드로이드 버전 : 3.1-4.04에는 버그가 있으며 TextView 내부의 setTextSize ()는 처음으로 만 작동합니다 (처음 호출).

버그는 이슈 22493 : Android 4.0의 TextView 높이 버그이슈 17343 : 버튼의 높이 및 텍스트가 HoneyComb에서 텍스트 크기를 늘리거나 줄인 후 원래 상태로 돌아 오지 않음에 설명되어 있습니다.

해결 방법은 크기를 변경하기 전에 TextView에 지정된 텍스트에 줄 바꿈 문자를 추가하는 것입니다.

final String DOUBLE_BYTE_SPACE = "\u3000";
textView.append(DOUBLE_BYTE_SPACE);

다음과 같이 코드에서 사용합니다.

final String DOUBLE_BYTE_SPACE = "\u3000";
AutoResizeTextView textView = (AutoResizeTextView) view.findViewById(R.id.aTextView);
String fixString = "";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1
   && android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {  
    fixString = DOUBLE_BYTE_SPACE;
}
textView.setText(fixString + "The text" + fixString);

텍스트의 왼쪽과 오른쪽에이 "\ u3000"문자를 추가하여 중앙에 유지합니다. 왼쪽에 정렬 한 경우 오른쪽에만 추가하십시오. 물론 AutoResizeTextView 위젯에 내장 될 수도 있지만 수정 코드를 외부에 유지하고 싶었습니다.


어떤 라인 (전 / 후)과 어떤 기능을 정확하게 보여줄 수 있습니까?
안드로이드 개발자

알았어 고마워. 나는 이것이 모든 것을 고치기를 희망하며 이것을 읽는 사람이 도움이 될 것입니다. 현재 확인할 수 없습니다. 아마 다음에 내가 그것을 사용할 것입니다.
Android 개발자

이 코드는 뷰의 코드 외부에서 호출됩니다. 대신 뷰의 코드 내에서 이것을 처리하는 좋은 대체 방법을 알고 있습니까?
안드로이드 개발자

당신은 textView.setText를 (우선) 및 텍스트 뷰 클래스의 코드 안에 넣을 수
Malachiasz

"getText ()"가 추가 문자가있는 텍스트를 반환한다는 의미도 아닙니까?
Android 개발자

3

이 문제에 대한 공식적인 해결책이 있습니다. Android O에 도입 된 자동 크기 조정 TextView는 지원 라이브러리 26에서 사용할 수 있으며 Android 4.0까지 하위 호환됩니다.

https://developer.android.com/preview/features/autosizing-textview.html

왜이 정보가 포함 된 https://stackoverflow.com/a/42940171/47680 이 관리자에 의해 삭제 된 이유를 모르겠습니다 .


1
예, 나도 들어 봤는데 얼마나 좋은가요? 그것을 사용하는 샘플이 있습니까? 그들의 동영상에서도 버그를 발견했습니다. 한 단어의 편지가 줄 바꿈 후 줄에 도착했습니다. youtu.be/1N9KveJ-FU8?t=781 ( "W"참고)
android developer

3

2018 년 6 월부터 Android는 공식적으로 Android 4.0 (API 레벨 14) 이상 에서이 기능 을 지원 합니다 .
Android 8.0 (API 레벨 26) 이상 :

setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, int autoSizeMaxTextSize, 
        int autoSizeStepGranularity, int unit);

Android 8.0 이전의 Android 버전 (API 레벨 26) :

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(TextView textView,
int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit)

자세한 답변을 확인하십시오 .


1

텍스트보기를 이미지로 변환하고 경계 내에서 이미지 크기를 조정합니다.

뷰를 이미지로 변환하는 방법에 대한 예는 다음과 같습니다 . 뷰를 Android에 표시하지 않고 비트 맵으로 변환?

문제는 텍스트를 선택할 수 없지만 트릭을 수행해야한다는 것입니다. 나는 그것을 시도하지 않았으므로 그것이 어떻게 보일지 잘 모르겠습니다 (확장 때문에).


좋은 생각이지만 텍스트를 픽셀 화하여 textView와 함께 사용할 수있는 기능을 제거합니다.
안드로이드 개발자

0

다음은 사용자 정의 글꼴에 대한 기능이 추가 된 눈사태 TextView입니다.

용법:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="match_parent" >

                <de.meinprospekt.androidhd.view.AutoFitText
                android:layout_width="wrap_content"
                android:layout_height="10dp"
                android:text="Small Text"
                android:textColor="#FFFFFF"
                android:textSize="100sp"
                foo:customFont="fonts/Roboto-Light.ttf" />

</FrameLayout>

xmlns : foo = "http://schemas.android.com/apk/res-auto"를 추가하는 것을 잊지 마십시오. 글꼴은 자산 소방서에 있어야합니다

import java.util.ArrayList;
import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.TextView;
import de.meinprospekt.androidhd.R;
import de.meinprospekt.androidhd.adapter.BrochuresHorizontalAdapter;
import de.meinprospekt.androidhd.util.LOG;

/**
 * https://stackoverflow.com/a/16174468/2075875 This class builds a new android Widget named AutoFitText which can be used instead of a TextView to
 * have the text font size in it automatically fit to match the screen width. Credits go largely to Dunni, gjpc, gregm and speedplane from
 * Stackoverflow, method has been (style-) optimized and rewritten to match android coding standards and our MBC. This version upgrades the original
 * "AutoFitTextView" to now also be adaptable to height and to accept the different TextView types (Button, TextClock etc.)
 * 
 * @author pheuschk
 * @createDate: 18.04.2013
 * 
 * combined with: https://stackoverflow.com/a/7197867/2075875
 */
@SuppressWarnings("unused")
public class AutoFitText extends TextView {

    private static final String TAG = AutoFitText.class.getSimpleName();

    /** Global min and max for text size. Remember: values are in pixels! */
    private final int MIN_TEXT_SIZE = 10;
    private final int MAX_TEXT_SIZE = 400;

    /** Flag for singleLine */
    private boolean mSingleLine = false;

    /**
     * A dummy {@link TextView} to test the text size without actually showing anything to the user
     */
    private TextView mTestView;

    /**
     * A dummy {@link Paint} to test the text size without actually showing anything to the user
     */
    private Paint mTestPaint;

    /**
     * Scaling factor for fonts. It's a method of calculating independently (!) from the actual density of the screen that is used so users have the
     * same experience on different devices. We will use DisplayMetrics in the Constructor to get the value of the factor and then calculate SP from
     * pixel values
     */
    private float mScaledDensityFactor;

    /**
     * Defines how close we want to be to the factual size of the Text-field. Lower values mean higher precision but also exponentially higher
     * computing cost (more loop runs)
     */
    private final float mThreshold = 0.5f;

    /**
     * Constructor for call without attributes --> invoke constructor with AttributeSet null
     * 
     * @param context
     */
    public AutoFitText(Context context) {
        this(context, null);
    }

    public AutoFitText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public AutoFitText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        //TextViewPlus part https://stackoverflow.com/a/7197867/2075875
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoFitText);
        String customFont = a.getString(R.styleable.AutoFitText_customFont);
        setCustomFont(context, customFont);
        a.recycle();

        // AutoFitText part
        mScaledDensityFactor = context.getResources().getDisplayMetrics().scaledDensity;
        mTestView = new TextView(context);

        mTestPaint = new Paint();
        mTestPaint.set(this.getPaint());

        this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                // make an initial call to onSizeChanged to make sure that refitText is triggered
                onSizeChanged(AutoFitText.this.getWidth(), AutoFitText.this.getHeight(), 0, 0);
                // Remove the LayoutListener immediately so we don't run into an infinite loop
                //AutoFitText.this.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                removeOnGlobalLayoutListener(AutoFitText.this, this);
            }
        });
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
        tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
        } catch (Exception e) {
            LOG.e(TAG, "Could not get typeface: "+e.getMessage());
            return false;
        }

        setTypeface(tf);  
        return true;
    }

    @SuppressLint("NewApi")
    public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
        if (Build.VERSION.SDK_INT < 16) {
            v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
        } else {
            v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
        }
    }

    /**
     * Main method of this widget. Resizes the font so the specified text fits in the text box assuming the text box has the specified width. This is
     * done via a dummy text view that is refit until it matches the real target width and height up to a certain threshold factor
     * 
     * @param targetFieldWidth The width that the TextView currently has and wants filled
     * @param targetFieldHeight The width that the TextView currently has and wants filled
     */
    private void refitText(String text, int targetFieldWidth, int targetFieldHeight) {

        // Variables need to be visible outside the loops for later use. Remember size is in pixels
        float lowerTextSize = MIN_TEXT_SIZE;
        float upperTextSize = MAX_TEXT_SIZE;

        // Force the text to wrap. In principle this is not necessary since the dummy TextView
        // already does this for us but in rare cases adding this line can prevent flickering
        this.setMaxWidth(targetFieldWidth);

        // Padding should not be an issue since we never define it programmatically in this app
        // but just to to be sure we cut it off here
        targetFieldWidth = targetFieldWidth - this.getPaddingLeft() - this.getPaddingRight();
        targetFieldHeight = targetFieldHeight - this.getPaddingTop() - this.getPaddingBottom();

        // Initialize the dummy with some params (that are largely ignored anyway, but this is
        // mandatory to not get a NullPointerException)
        mTestView.setLayoutParams(new LayoutParams(targetFieldWidth, targetFieldHeight));

        // maxWidth is crucial! Otherwise the text would never line wrap but blow up the width
        mTestView.setMaxWidth(targetFieldWidth);

        if (mSingleLine) {
            // the user requested a single line. This is very easy to do since we primarily need to
            // respect the width, don't have to break, don't have to measure...

            /*************************** Converging algorithm 1 ***********************************/
            for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

                // Go to the mean value...
                testSize = (upperTextSize + lowerTextSize) / 2;

                mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
                mTestView.setText(text);
                mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

                if (mTestView.getMeasuredWidth() >= targetFieldWidth) {
                    upperTextSize = testSize; // Font is too big, decrease upperSize
                } else {
                    lowerTextSize = testSize; // Font is too small, increase lowerSize
                }
            }
            /**************************************************************************************/

            // In rare cases with very little letters and width > height we have vertical overlap!
            mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

            if (mTestView.getMeasuredHeight() > targetFieldHeight) {
                upperTextSize = lowerTextSize;
                lowerTextSize = MIN_TEXT_SIZE;

                /*************************** Converging algorithm 1.5 *****************************/
                for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

                    // Go to the mean value...
                    testSize = (upperTextSize + lowerTextSize) / 2;

                    mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
                    mTestView.setText(text);
                    mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

                    if (mTestView.getMeasuredHeight() >= targetFieldHeight) {
                        upperTextSize = testSize; // Font is too big, decrease upperSize
                    } else {
                        lowerTextSize = testSize; // Font is too small, increase lowerSize
                    }
                }
                /**********************************************************************************/
            }
        } else {

            /*********************** Converging algorithm 2 ***************************************/
            // Upper and lower size converge over time. As soon as they're close enough the loop
            // stops
            // TODO probe the algorithm for cost (ATM possibly O(n^2)) and optimize if possible
            for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) {

                // Go to the mean value...
                testSize = (upperTextSize + lowerTextSize) / 2;

                // ... inflate the dummy TextView by setting a scaled textSize and the text...
                mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor);
                mTestView.setText(text);

                // ... call measure to find the current values that the text WANTS to occupy
                mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
                int tempHeight = mTestView.getMeasuredHeight();
                // int tempWidth = mTestView.getMeasuredWidth();

                // LOG.debug("Measured: " + tempWidth + "x" + tempHeight);
                // LOG.debug("TextSize: " + testSize / mScaledDensityFactor);

                // ... decide whether those values are appropriate.
                if (tempHeight >= targetFieldHeight) {
                    upperTextSize = testSize; // Font is too big, decrease upperSize
                } else {
                    lowerTextSize = testSize; // Font is too small, increase lowerSize
                }
            }
            /**************************************************************************************/

            // It is possible that a single word is wider than the box. The Android system would
            // wrap this for us. But if you want to decide fo yourself where exactly to break or to
            // add a hyphen or something than you're going to want to implement something like this:
            mTestPaint.setTextSize(lowerTextSize);
            List<String> words = new ArrayList<String>();

            for (String s : text.split(" ")) {
                Log.i("tag", "Word: " + s);
                words.add(s);
            }
            for (String word : words) {
                if (mTestPaint.measureText(word) >= targetFieldWidth) {
                    List<String> pieces = new ArrayList<String>();
                    // pieces = breakWord(word, mTestPaint.measureText(word), targetFieldWidth);

                    // Add code to handle the pieces here...
                }
            }
        }

        /**
         * We are now at most the value of threshold away from the actual size. To rather undershoot than overshoot use the lower value. To match
         * different screens convert to SP first. See {@link http://developer.android.com/guide/topics/resources/more-resources.html#Dimension} for
         * more details
         */
        this.setTextSize(TypedValue.COMPLEX_UNIT_SP, lowerTextSize / mScaledDensityFactor);
        return;
    }

    /**
     * This method receives a call upon a change in text content of the TextView. Unfortunately it is also called - among others - upon text size
     * change which means that we MUST NEVER CALL {@link #refitText(String)} from this method! Doing so would result in an endless loop that would
     * ultimately result in a stack overflow and termination of the application
     * 
     * So for the time being this method does absolutely nothing. If you want to notify the view of a changed text call {@link #setText(CharSequence)}
     */
    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        // Super implementation is also intentionally empty so for now we do absolutely nothing here
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        if (width != oldWidth && height != oldHeight) {
            refitText(this.getText().toString(), width, height);
        }
    }

    /**
     * This method is guaranteed to be called by {@link TextView#setText(CharSequence)} immediately. Therefore we can safely add our modifications
     * here and then have the parent class resume its work. So if text has changed you should always call {@link TextView#setText(CharSequence)} or
     * {@link TextView#setText(CharSequence, BufferType)} if you know whether the {@link BufferType} is normal, editable or spannable. Note: the
     * method will default to {@link BufferType#NORMAL} if you don't pass an argument.
     */
    @Override
    public void setText(CharSequence text, BufferType type) {

        int targetFieldWidth = this.getWidth();
        int targetFieldHeight = this.getHeight();

        if (targetFieldWidth <= 0 || targetFieldHeight <= 0 || text.equals("")) {
            // Log.v("tag", "Some values are empty, AutoFitText was not able to construct properly");
        } else {
            refitText(text.toString(), targetFieldWidth, targetFieldHeight);
        }
        super.setText(text, type);
    }

    /**
     * TODO add sensibility for {@link #setMaxLines(int)} invocations
     */
    @Override
    public void setMaxLines(int maxLines) {
        // TODO Implement support for this. This could be relatively easy. The idea would probably
        // be to manipulate the targetHeight in the refitText-method and then have the algorithm do
        // its job business as usual. Nonetheless, remember the height will have to be lowered
        // dynamically as the font size shrinks so it won't be a walk in the park still
        if (maxLines == 1) {
            this.setSingleLine(true);
        } else {
            throw new UnsupportedOperationException("MaxLines != 1 are not implemented in AutoFitText yet, use TextView instead");
        }
    }

    @Override
    public void setSingleLine(boolean singleLine) {
        // save the requested value in an instance variable to be able to decide later
        mSingleLine = singleLine;
        super.setSingleLine(singleLine);
    }
}

알려진 버그 : 안드로이드 4.03와 작동하지 않음 - 글꼴이다 (원래 avalancha도 작동하지 않습니다) 아래 그 버그에 대한 해결 방법입니다 보이지 않거나 아주 작은 : https://stackoverflow.com/a/21851239/2075875


0

이 시도

TextWatcher changeText = new TextWatcher() {
     @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                tv3.setText(et.getText().toString());
                tv3.post(new Runnable() {           
                    @Override
                    public void run() {
                    while(tv3.getLineCount() >= 3){                     
                            tv3.setTextSize((tv3.getTextSize())-1);                     
                        }
                    }
                });
            }

            @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override public void afterTextChanged(Editable s) { }
        };

텍스트 크기를 변경할 때마다 블록으로 끝난 후에 만 ​​드로잉이 발생합니다 (이벤트 큐에 추가됨). 즉, 텍스트가 변경 될 때마다 루프가 최대 한 번만 작동하며 행 수가 3 행을 초과하지 않는다고 보장 할 수 없습니다.
안드로이드 개발자

0

더 쉬운 것을 찾고 있다면 :

 public MyTextView extends TextView{

    public void resize(String text, float textViewWidth, float textViewHeight) {
       Paint p = new Paint();
       Rect bounds = new Rect();
       p.setTextSize(1);
       p.getTextBounds(text, 0, text.length(), bounds);
       float widthDifference = (textViewWidth)/bounds.width();
       float heightDifference = (textViewHeight);
       textSize = Math.min(widthDifference, heightDifference);
       setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}

매우 짧아 보이지만 준비한 테스트를 통과 할 수 있습니까? 또한이 함수가 호출되면 얻지 못합니다.
안드로이드 개발자

아니요, 아직 제안입니다. 테스트를 최대한 빨리 통과 할 수 있도록 개선하겠습니다.
Sander

글쎄, 제안이라면 Github에 직접 게시 할 수 있습니까?
안드로이드 개발자

0

@Malachiasz가 설명 한 문제에 대한 빠른 수정

자동 크기 조정 클래스에서 이에 대한 사용자 지정 지원을 추가하여 문제를 해결했습니다.

public void setTextCompat(final CharSequence text) {
    setTextCompat(text, BufferType.NORMAL);
}

public void setTextCompat(final CharSequence text, BufferType type) {
    // Quick fix for Android Honeycomb and Ice Cream Sandwich which sets the text only on the first call
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 &&
        Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
        super.setText(DOUBLE_BYTE_WORDJOINER + text + DOUBLE_BYTE_WORDJOINER, type);
    } else {
        super.setText(text, type);
    }
}

@Override
public CharSequence getText() {
    String originalText = super.getText().toString();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 &&
        Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
        // We try to remove the word joiners we added using compat method - if none found - this will do nothing.
        return originalText.replaceAll(DOUBLE_BYTE_WORDJOINER, "");
    } else {
        return originalText;
    }
}

yourView.setTextCompat(newTextValue)대신에 전화yourView.setText(newTextValue)


setText를 재정의하는 대신 setTextCompat의 새 함수를 작성해야하는 이유는 무엇입니까? 또한 어떤 문제가 있습니까?
안드로이드 개발자

setText ()는 TextView의 최종 메서드이므로 재정의 할 수 없습니다. 다른 옵션은 사용자 정의 TextView 외부 에서이 작업을 수행하는 것이지만이 솔루션은 TextView 내부에서 사용하는 것입니다.
Ionut Negru

정확히. "setText"중 일부는 비공개이고, 일부는 공개이며, 일부는 공개되지 않습니다. 공용 void setText (CharSequence text, BufferType type)을 재정의하면 대부분 처리 할 수 ​​있습니다. 비록 그 목적을 모르는 최종 함수가 있습니다 : public final void setText (char [] text, int start, int len). 아마도 코드를 더 깊이 살펴 보았을 것입니다.
안드로이드 개발자

setText ()의 모든 변형은 실제로 결국 setText (CharSequence text) 메소드를 호출합니다. 동일한 동작을 보장하려면 해당 메소드를 대체해야합니다. 그렇지 않으면 사용자 정의 setText () 메소드를 추가하는 것이 훨씬 좋습니다.
Ionut Negru

예, 그러나 대부분의 경우 괜찮습니다.
Android 개발자

0

추가 시도 LayoutParamsMaxWidthMaxHeight받는 사람을 TextView. 레이아웃이 부모 컨테이너를 따르도록하고 오버플로는하지 않습니다.

textview.setLayoutParams(new LayoutParams(LinearLayout.MATCH_PARENT,LinearLayout.WRAP_CONTENT));

int GeneralApproxWidthOfContainer = 400;
int GeneralApproxHeightOfContainer = 600;
textview.setMaxWidth(400);
textview.setMaxHeight(600);` 

당신이 무슨 말을하는지 잘 모르겠습니다. textView 샘플에 이것을 추가하여 때로는 작은 문제가 발생하지 않도록 제안합니까? 그렇다면 왜 github 웹 사이트에 게시하지 않았습니까?
안드로이드 개발자

0

Android O부터 xml로 텍스트 크기를 자동으로 조정할 수 있습니다.

https://developer.android.com/preview/features/autosizing-textview.html

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:autoSizeTextType="uniform"
    app:autoSizeMinTextSize="12sp"
    app:autoSizeMaxTextSize="100sp"
    app:autoSizeStepGranularity="2sp"
  />

Android O를 사용하면 TextView의 특성과 경계에 따라 레이아웃을 채우도록 텍스트 크기가 자동으로 확장되거나 축소되도록 TextView에 지시 할 수 있습니다. 이 설정을 사용하면 동적 내용이있는 다른 화면에서 텍스트 크기를보다 쉽게 ​​최적화 할 수 있습니다.

Support Library 26.0 Beta는 Android O 이전의 Android 버전을 실행하는 기기에서 TextView 기능 자동 크기 조정을 완벽하게 지원합니다. 라이브러리는 Android 4.0 (API 레벨 14) 이상을 지원합니다. android.support.v4.widget 패키지에는 이전 버전과 호환되는 방식으로 기능에 액세스하기위한 TextViewCompat 클래스가 포함되어 있습니다.


안타깝게도 내 테스트 및 Google IO의 비디오에서도 글꼴 크기를 조정하는 대신 부분 단어를 잘못 감싸는 등의 문제가 있음을 알았습니다. 나는 이것에 대해 여기에보고했다 : issuetracker.google.com/issues/38468964 , 이것이 여전히 그것을 사용하지 않는 이유입니다.
안드로이드 개발자

0

Android 공식 Autosizing TextView를 시도한 후 Android 버전이 Android 8.0 (API 레벨 26) 이전 android.support.v7.widget.AppCompatTextView인지 확인해야합니다. 지원 라이브러리 버전이 26.0.0 이상인지 확인하십시오. 예:

<android.support.v7.widget.AppCompatTextView
    android:layout_width="130dp"
    android:layout_height="32dp"
    android:maxLines="1"
    app:autoSizeMaxTextSize="22sp"
    app:autoSizeMinTextSize="12sp"
    app:autoSizeStepGranularity="2sp"
    app:autoSizeTextType="uniform" />

업데이트 :

@ android-developer의 답변에 따르면 AppCompatActivity소스 코드를 확인 하고이 두 줄을 발견했습니다.onCreate

final AppCompatDelegate delegate = getDelegate(); delegate.installViewFactory();

과에서 AppCompatDelegateImplcreateView

    if (mAppCompatViewInflater == null) {
        mAppCompatViewInflater = new AppCompatViewInflater();
    }

AppCompatViewInflater팽창기보기를 사용AppCompatViewInflater createView "TextView"에 AppCompatTextView를 사용합니다.

public final View createView(){
    ...
    View view = null;
    switch (name) {
        case "TextView":
            view = new AppCompatTextView(context, attrs);
            break;
        case "ImageView":
            view = new AppCompatImageView(context, attrs);
            break;
        case "Button":
            view = new AppCompatButton(context, attrs);
            break;
    ...
}

내 프로젝트에서는을 사용하지 않으므로 xml에서 AppCompatActivity사용해야 <android.support.v7.widget.AppCompatTextView>합니다.


1
대부분의 경우 인플레이터는 "TextView"만 작성할 때 AppCompatTextView를 사용하므로 따로 할 필요가 없습니다.
Android 개발자

@androiddeveloper 그러나 TextView를 사용할 때 자동 크기 조정이 작동하지 않습니다.
weei.zh

@androiddeveloper 감사합니다, 이유는 내가 사용하지 않기 때문입니다 AppCompatActivity. AppCompatActivity는 AppCompatViewInflater 인플레이터 레이아웃을 사용합니다.
weei.zh

이해가 안됩니다
Android 개발자
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.