背景

很多时候,我们需要自动适应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,我决定发布一个库,这样每个人都可以轻松地使用它,并在这里为它做出贡献。


当前回答

我的要求是

单击ScalableTextView 打开一个listActivity并显示各种长度的字符串项。 从列表中选择一个文本。 将文本设置回另一个活动中的ScalableTextView。

我引用了链接:自动缩放TextView文本以适应范围(包括注释),以及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在支持库26中可用,并向后兼容一直到Android 4.0。

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

我不知道为什么包括这个信息的https://stackoverflow.com/a/42940171/47680被管理员删除了。

我对M-WaJeEh的回答做了一些修改,以考虑到两边的复合提款。

getCompoundPaddingXXXX()方法返回视图的填充+可绘制空间。例如: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));
}

从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 3(蜂巢)和Android 4.0(冰淇淋三明治)中的bug

android版本:3.1 - 4.04有一个错误,setTextSize()内部的TextView只工作第一次(第一次调用)。

该错误在版本22493:Android 4.0中的TextView高度错误和版本17343:在HoneyComb上增加或减少文本大小后,按钮的高度和文本不会恢复到原来的状态。

变通的方法是在改变大小之前给文本添加换行符:

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小部件中,但我希望将修复代码保留在外部。