TextView for Androidの自動フィット


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のサイズをランダム化し、1行にとどめていますが、試したサンプルでもうまくいきません。

コードは次のとおりです( ここでも利用可能です ):

ファイル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をTextテキストの自動スケール )の枠内に収める (コメントを含む)と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で導入されたTextViewの自動サイズ設定は、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)




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ベータ版では、ライブラリは、Android 4.0(APIレベル14)以上のサポートを提供するAndroidのO.前にAndroidのバージョンを実行しているデバイス上の自動サイズのTextViewの機能へのフルサポートを提供します。android.support.v4.widgetパッケージには、下位互換性の方法での機能にアクセスするためにTextViewCompatクラスが含まれています。