自动调整Android版TextView


231

背景

很多时候,我们需要将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,因此我决定发布一个库,以便每个人都可以在此处轻松使用它并做出贡献。



@Thrakbad,这是我提到的链接之一。它也没有通过测试。
android开发人员

抱歉,我以某种方式错过了最后一个示例
Thrakbad 2013年

是的,请相信我,我已经尝试了很多示例,并且还尝试修改它们以解决我发现的问题,但从未成功。如果发现您认为可行的功能,请进行测试。我已经为此发布了示例代码。
android开发人员

1
@rule,这是我已经阅读的帖子之一,并且我已经测试了所有代码示例。我也认为你有双重职务。
Android开发者

Answers:


147

由于MartinH的简单的解决这里,这个代码也需要照顾android:drawableLeftandroid:drawableRightandroid:drawableTopandroid:drawableBottom标签。


我在这里的答案应该让您满意自动缩放TextView文本以适合边界

我已经修改了您的测试用例:

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

我应Android开发人员的要求在此处发布代码:

最终效果:

在此处输入图片说明

样例版面文件:

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

和Java代码:

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(),但是您可以存储maxLines并通过覆盖setMaxLines并存储其值来获取它。我已经对其进行了更改,现在可以正常使用了。另外,由于有时文本对于最小的尺寸可能甚至太长,因此我尝试使用setEllipsize,这也起作用!我认为我们有一个赢家。请在此处发布您的代码,以便我将其标记为正确的代码。
android开发人员

您没有删除getMaxLines()而是使用了自己的。它仍然只能在API16上运行...请对其进行更改,以便每个人都可以使用它。我建议重写setMaxLines以将参数存储到字段中,然后访问该字段而不是使用getMaxLines。
android开发人员

现在检查,增加了对最大行的支持。
M-WaJeEh

看起来还好 您应该在onTestSize()方法中添加“ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)”,以使Lint不会给出错误/警告,而是在CTOR中对其进行初始化。将二进制搜索设置为静态也是一件好事,您可以在最大的CTOR上进行初始化,然后从其他CTOR调用它。但是,这确实是最好的答案,您应该得到V。对于这个老问题,这最终是一个很好的答案。做得好!
android开发人员

@ 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的来源。你为什么把它作为新答案?我知道您有良好的意愿,但是将其作为答案是一件好事吗?我不确定他能看到您的答案吗?
android开发人员

1
我刚刚注册就能提供该更新。我的声誉数仅为1,这使我无法评论不是我的帖子(最少15个)。
MartinH 2013年

不用担心,您可能想将我的回复转发给M-WajeEh,因为目前我什至无法与他联系:/
MartinH 2013年

您能否尝试更好地描述您做了什么?您是否修复了错误?您可以显示截图以演示该错误吗?
Android开发人员

1
大家好,MartinH,您好!我会认真研究这个问题,并通过提及您的名字并在测试后发布指向您帖子的链接来更新我的答案。请给我几天。如今,我被困在其他地方。
M-WaJeEh

7

好的,我已经用上一周的时间来大量重写我的代码以完全适合您的测试。现在,您可以复制1:1,它将立即生效-包括setSingleLine()。请记住要进行调整MIN_TEXT_SIZEMAX_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

但是,如果您愿意,我添加了广泛的评论来帮助您构建此文件!


请注意:

如果您只是正常使用它而不将其限制为一行,则可能会出现如前所述的断字现象。这是Android的功能而不是的错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代替),使用二进制搜索,因此它可能会测试大小根本不适合的大小(并且无缘无故地使用更多的内存,如果大小太大则可能导致异常),不支持任何更改(例如文本本身)时的处理。
android开发人员

顺便说一句,您不必使用mTestView变量并直接在当前视图上进行测试,因此,您不必考虑所有特殊事项。另外,我认为可以使用targetFieldHeight代替MAX_TEXT_SIZE。
android开发人员

我认为,不是单纯的二进制搜索(尤其是其中的2个),您可以通过目标与所测量大小的比率来更好地猜测。还有其他方法(牛顿和其他我不记得的方法),但我认为它们不适合这里。我现在建议的那个更好,因为您可以从最小尺寸开始而不是测试最大尺寸。事实是,您将不需要最大尺寸,因为您会不断做出更好的猜测。
android开发人员

我还认为,当条件可能简单地是:if(getMeasuredWidth()> = targetFieldWidth || getMeasuredHeight()> = targetFieldHeight)时,这2个二进制搜索可以合并为一行
android开发人员

1
我发现的另一个问题是文字重力。我认为它不能垂直居中。
android开发人员

4

我的要求是

  • 单击可缩放文本视图
  • 打开listActivity并显示各种长度的字符串项目。
  • 从列表中选择一个文本。
  • 在另一个活动中,将文本设置回可缩放文本视图。

我引用了以下链接:自动缩放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);
            }
        }
    }
}
}

编码愉快。


您不应该使用二进制搜索而不是每次迭代减少一吗?同样,该代码似乎强制TextView使用一行文本,而不是允许它具有多行。
android开发者

这是唯一对我有用的解决方案。我的问题与TExtView不适合仅在4.1中使用视图有关
FOliveira '16

似乎与父项不匹配100%。看到截图:s14.postimg.org/93c2xgs75/Screenshot_2.png
25年

4

我将逐步解释此属性如何降低Android版本的工作方式:

1-在项目gradle文件上导入android支持库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。请注意,“ TextView”的“ w”如何转到下一行...无论如何,请在此处创建关于它的新请求:issuetracker.google.com/issues/68787108。请考虑为其加注星标。
Android开发人员

@androiddeveloper你是对的。我没有解释该标签实现仅在一行中有效。如果需要换行,则必须更改以下属性:android:layout_height =“ wrap_content”。但是android.support.v7.widget.AppCompatTextView和gradle实现保证可以使用此属性。
锡南·艾尔金

我不明白你的意思。我不希望将分词包装到下一行,除非没有其他解决方法。在视频中,您可以轻松地看到字体变小了。这就是问题。您是说知道如何解决吗?意味着不会自动换行,只有字体大小会改变吗?
Android开发人员

@androiddeveloper有很多场景。我的情况是:自动调整文本以固定而不是自动换行。在增加系统字体大小期间,文本必须在textview中不显示自动换行。如果自动换行对您来说并不重要,请不要设置固定的高度。此属性不仅使用自动换行,还可以自动调整具有固定宽度的TextView的文本。
思南·艾尔金

根据我的测试,字体大小没有正确更改,并且可能出现自动换行问题。
Android开发人员

3

警告,Android 3(蜂窝)和Android 4.0(冰淇淋三明治)中的错误

Android版本: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开发人员

好,谢谢。我希望这可以解决所有问题,并且希望阅读此书的人对它有所帮助。目前我无法查看。也许下次我会用它。
android开发人员

该代码是从视图代码之外调用的。您是否知道在视图代码中处理此问题的另一种好方法?
Android开发人员

你可以覆盖textView.setText(),并把TextView的类的代码里
Malachiasz

这还不是意味着“ getText()”将返回带有多余字符的文本吗?
Android开发人员

3

现在有一个官方解决方案。支持库26中提供了随Android O引入的自动调整TextView大小,并且一直向下兼容至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开发人员

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一起使用的任何功能。
android开发人员

0

以下是avalancha 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);
    }
}

已知的错误:不适用于Android 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行。
Android开发人员

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

它似乎很短,但是可以通过我准备的测试吗?另外,调用此函数时我不知道。
android开发人员

不,这只是一个建议。我会对其进行改进,以便尽快通过您的测试。
桑德2014年

好吧,如果有建议,可以直接将它发布到Github吗?
android开发人员

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)


为什么要创建一个新的setTextCompat函数,而不是覆盖setText?另外,哪个问题?
Android开发人员

setText()是TextView的最终方法,因此您将无法覆盖它。另一个选择是在自定义TextView之外执行此操作,但是此解决方案是在TextView内部使用它。
Ionut Negru

不完全是。有些“ setText”是私有的,有些是公共的,有些不是最终的。似乎大多数方法都可以通过重写public void setText(CharSequence text,BufferType type)来处理。有一个我最终不知道其目的的最终函数:public final void setText(char [] text,int start,int len)。也许更深入地看一下代码。
Android开发人员

setText()的所有这些变体最终实际上都会调用setText(CharSequence text)方法。如果要确保相同的行为,则需要重写该方法,否则最好简单地添加自己的自定义setText()方法。
Ionut Negru

是的,但也许在大多数情况下都可以。
Android开发人员

0

尝试添加LayoutParamsMaxWidthMaxHeightTextView。它将强制布局尊重父容器,而不溢出。

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

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

我不确定你在说什么。您是否建议将其添加到textView示例中,以免出现我有时看到的小问题?如果是这样,为什么不在github网站上发布它呢?
android开发人员

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的特征和边界自动扩大或缩小文本大小以填充其布局。通过此设置,可以更轻松地优化带有动态内容的不同屏幕上的文本大小。

支持库26.0 Beta为运行Android O之前的Android版本的设备上的自动调整TextView功能提供完全支持。该库为Android 4.0(API级别14)及更高版本提供支持。android.support.v4.widget包包含TextViewCompat类,以向后兼容的方式访问功能。


可悲的是,根据我的测试,甚至在Google IO的视频中,我都注意到它存在一些问题,例如错误地包装了部分单词,而不是调整字体大小。我在这里报告了此问题:issuetracker.google.com/issues/38468964,这就是为什么我仍然不使用它的原因。
Android开发人员

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充气机视图,当AppCompatViewInflatercreateView时,它将对“ 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;
    ...
}

在我的项目中,我不使用AppCompatActivity,因此我需要<android.support.v7.widget.AppCompatTextView>在xml中使用。


1
在大多数情况下,充气机仅在编写“ TextView”时使用AppCompatTextView,因此您不必这样做。
android开发人员

@androiddeveloper但是当我使用TextView时,自动调整功能无法正常工作。
weei.zh'19

@androiddeveloper谢谢,原因是我没有使用AppCompatActivity。AppCompatActivity使用AppCompatViewInflater充气机布局。
weei.zh'19

我不明白
android开发人员
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.