背景

很多时候,我们需要自动适应TextView的字体给它的边界。

这个问题

遗憾的是,尽管有许多帖子和帖子(以及建议的解决方案)讨论这个问题(例如这里,这里和这里),但实际上没有一个能很好地工作。

这就是为什么,我决定测试他们,直到我找到真正的交易。

我认为这样一个textView的要求应该是:

Should allow using any font, typeface, style, and set of characters. Should handle both width and height No truncation unless text cannot fit because of the limitation, we've given to it (example: too long text, too small available size). However, we could request for horizontal/vertical scrollbar if we wish, just for those cases. Should allow multi-line or single-line. In case of multi-line, allow max & min lines. Should not be slow in computation. Using a loop for finding the best size? At least optimize it and don't increment your sampling by 1 each time. In case of multi-line, should allow to prefer resizing or using more lines, and/or allow to choose the lines ourselves by using the "\n" character.

我的努力

我尝试了很多样例(包括我写过的那些链接),我也试图修改它们来处理我所说的情况,但没有一个真正有效。

我已经做了一个示例项目,让我可以直观地看到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,我决定发布一个库,这样每个人都可以轻松地使用它,并在这里为它做出贡献。


当前回答

从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 allows you to instruct a TextView to let the text size expand or contract automatically to fill its layout based on the TextView's characteristics and boundaries. This setting makes it easier to optimize the text size on different screens with dynamic content. The Support Library 26.0 Beta provides full support to the autosizing TextView feature on devices running Android versions prior to Android O. The library provides support to Android 4.0 (API level 14) and higher. The android.support.v4.widget package contains the TextViewCompat class to access features in a backward-compatible fashion.

其他回答

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

看看我的详细答案。

在我尝试了Android官方自动调整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();

和在AppCompatDelegateImpl的createView中

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

它使用AppCompatViewInflater膨胀器视图,当AppCompatViewInflater createView它将使用AppCompatTextView为“TextView”。

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;
    ...
}

在我的项目中我不使用AppCompatActivity,所以我需要使用<android.support.v7.widget。AppCompatTextView>的xml格式。

试试这个

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

从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 allows you to instruct a TextView to let the text size expand or contract automatically to fill its layout based on the TextView's characteristics and boundaries. This setting makes it easier to optimize the text size on different screens with dynamic content. The Support Library 26.0 Beta provides full support to the autosizing TextView feature on devices running Android versions prior to Android O. The library provides support to Android 4.0 (API level 14) and higher. The android.support.v4.widget package contains the TextViewCompat class to access features in a backward-compatible fashion.

快速修复了@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)