Android 용 TextView 자동 맞춤 설정


Answers

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

getCompoundPaddingXXXX() 메소드 padding of the view + drawable space 반환 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));
}
Question

배경

많은 경우 TextView의 글꼴을 주어진 경계에 자동으로 맞추어야합니다.

문제

슬프게도이 문제 ( 여기 , 여기 , 여기 )에 대해 많은 스레드와 게시물 (및 제안 된 솔루션)이 있지만 실제로는 아무 문제가 없습니다.

그게 바로 내가 실제 거래를 찾을 때까지 각자를 테스트하기로 결정한 이유입니다.

그런 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이기 때문에 모든 사람들이 쉽게 사용할 수 있고 여기에 기여할 수 있도록 라이브러리를 게시하기로 결정했습니다.




내 요구 사항은 다음과 같습니다.

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

필자는 링크를 참조했습니다 : TextView Text를 Bound (주석 포함) 내에서 자동으로 조정 하고 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);
            }
        }
    }
}
}

해피 코딩 ....




이 문제에 대한 공식적인 해결책이 있습니다. Android O에서 소개 된 TextViews 자동 크기 조정은 Support Library 26에서 사용할 수 있으며 Android 4.0까지 역 호환됩니다.

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

https://.com/a/42940171/47680 에이 정보가 포함 된 이유가 관리자에 의해 삭제되었는지 잘 모르겠습니다.




어쩌면 내가 여기에 마지막 대답이지만 간단한 솔루션을 발견하고 예상대로 작동합니다.

내 해결책 : TEXT_VIEW 보이지 않게 만들기 -> 부모가 적합하지 않을 때까지 크기를 줄이십시오.> 그런 다음 그 사실을 알려주십시오.

요구 사항 : TextView의 너비는 WRAP_CONTENT 여야 하며 여백 시작 또는 끝은 부모가 아닙니다.

public void autoResizeTextView(final TextView tvContent, final View parent,
                                   final float originalSize, final int maxLine) {

        tvContent.setVisibility(View.INVISIBLE);

        tvContent.getViewTreeObserver()
                .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

                    private float mOriginalSize = originalSize;
                    private float mParentWidth = 0f;

                    @Override
                    public void onGlobalLayout() {
                        // get parent width just only one time.
                        if (mParentWidth == 0f) {
                            mParentWidth = parent.getMeasuredWidth();
                        }

                        if (tvContent.getMeasuredWidth() == mParentWidth 
                                      && tvContent.getLineCount() > maxLine) {
                                mOriginalSize -= 0.5f;
                                tvContent.setTextSize(mOriginalSize);
                            } else {
                                tvContent.setVisibility(View.VISIBLE);
                                tvContent.getViewTreeObserver()
                                        .removeOnGlobalLayoutListener(this);
                            }
                    }
                });
    }

TextView의 setText () 를 호출 한 후이 메서드를 사용하십시오. 아마도 0.5 초가 걸릴 것입니다.




이 시도

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) { }
        };



@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)




안드로이드 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"
  />

안드로이드 O는 텍스트 크기가 확장되거나 계약이 자동으로 텍스트 뷰의 특성과 경계를 기반으로 레이아웃을 채우기 위해 할 수있는 텍스트 뷰를 지시 할 수 있습니다. 이 설정은 쉽게 동적 콘텐츠와 다른 화면에서 텍스트 크기를 최적화 할 수 있습니다.

지원 라이브러리 26.0 베타 라이브러리는 안드로이드 4.0 (API 레벨 14) 이상에 대한 지원을 제공 안드로이드 O. 이전 안드로이드 버전을 실행하는 장치에서 자동 크기의 텍스트 뷰 기능에 대한 완전한 지원을 제공합니다. android.support.v4.widget 패키지는 이전 버전과 호환되는 방식으로 기능에 액세스 할 수있는 TextViewCompat 클래스가 포함되어 있습니다.