Auto-fit TextView для Android


Answers

Я немного изменил ответ 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));
}
Question

Задний план

Много раз нам нужно автоподгонку шрифта TextView к указанным им границам.

Проблема

К сожалению, несмотря на то, что существует много потоков и сообщений (и предлагаемых решений), говорящих об этой проблеме (пример здесь , здесь и здесь ), ни один из них на самом деле не работает.

Вот почему я решил проверить каждую из них, пока не найду реальную сделку.

Я думаю, что требования к такому текстуру должны быть:

  1. Должен разрешить использование любого шрифта, шрифта, стиля и набора символов.

  2. Должны обрабатывать как ширину, так и высоту

  3. Нет усечения, если текст не может соответствовать из-за ограничения, которое мы ему дали (например: слишком длинный текст, слишком маленький доступный размер). Однако мы можем запросить горизонтальную / вертикальную полосу прокрутки, если хотим, только для этих случаев.

  4. Должен разрешить многострочный или однострочный. В случае многострочной линии, разрешите max & min lines.

  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 в другое действие.

Я ссылался на ссылку: Auto Scale TextView Text to Fit в пределах Bounds (включая комментарии), а также 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);
            }
        }
    }
}
}

Счастливое кодирование ....




В настоящее время существует официальное решение этой проблемы. Autosizing TextViews, представленные с Android O, доступны в Библиотеке поддержки 26 и полностью совместимы с Android 4.0.

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

Я не уверен, почему https://.com/a/42940171/47680, который также включил эту информацию, был удален администратором.




Может быть, я последний ответ здесь, но я нашел простое решение, и он работает так, как я ожидал.

Мое решение: MAKE 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);
                            }
                    }
                });
    }

Используйте этот метод после вызова setText () TextView. Возможно, это стоит 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)




Так как Android О, это возможно Авторазмер текст в 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 обеспечивает полную поддержку функции Autosizing TextView на устройствах, работающих под управлением Android версии до Android O. библиотека обеспечивает поддержку Android 4.0 (уровень API 14) и выше. Пакет android.support.v4.widget содержит класс TextViewCompat для доступа к функциям в обратной совместимости моды.