Automatyczne dopasowanie TextView dla Androida

tło

Wiele razy musimy automatycznie dopasować czcionkę TextView do nadanych jej granic.

Problem

Niestety, mimo że istnieje wiele wątków i postów (i sugerowanych rozwiązań) mówiących o tym problemie (przykładtutaj, tutaj itutaj), żaden z nich nie działa dobrze.

Dlatego zdecydowałem się przetestować każdą z nich, dopóki nie znajdę prawdziwej oferty.

Myślę, że wymagania takiego tekstu View powinny być:

Powinien umożliwiać użycie dowolnej czcionki, kroju pisma, stylu i zestawu znaków.

Powinien obsługiwać zarówno szerokość, jak i wysokość

Bez obcięcia, chyba że tekst nie pasuje z powodu ograniczenia, podaliśmy go (przykład: zbyt długi tekst, zbyt mały dostępny rozmiar). Możemy jednak poprosić o poziomy / pionowy pasek przewijania, jeśli chcemy, tylko dla tych przypadków.

Powinien zezwalać na wiele linii lub jedną linię. W przypadku wielu linii, zezwalaj na linie max i min.

Nie powinno być powolne w obliczeniach. Używając pętli do znalezienia najlepszego rozmiaru? Przynajmniej zoptymalizuj go i nie zwiększaj próbkowania o 1 za każdym razem.

W przypadku wieloliniowości należy pozwolić na zmianę rozmiaru lub użycie większej liczby linii i / lub pozwolić na samodzielne wybranie linii za pomocą znaku „n”.

Co próbowałem

Próbowałem tak wielu próbek (w tym tych linków, o których pisałem), a także próbowałem je zmodyfikować, aby obsługiwały przypadki, o których mówiłem, ale żadna naprawdę nie działa.

Zrobiłem przykładowy projekt, który pozwala mi zobaczyć wizualnie, czy TextView pasuje poprawnie.

Obecnie mój przykładowy projekt losuje tylko tekst (alfabet angielski plus cyfry) i rozmiar textView, i pozwala mu pozostać w pojedynczej linii, ale nawet to nie działa dobrze na żadnej próbce, którą próbowałem.

Oto kod (dostępny równieżtutaj):

Plikres/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>
Pliksrc/.../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();
    }
  }
Pytanie

Czy ktoś zna rozwiązanie tego typowego problemu, który faktycznie działa?

Nawet rozwiązanie, które ma znacznie mniej funkcji niż to, o czym pisałem, na przykład takie, które ma tylko stałą liczbę wierszy tekstu, i dostosowuje swoją czcionkę do rozmiaru, ale nigdy nie ma dziwnych trzasków i tekstów też się pojawia duży / mały w porównaniu z dostępną przestrzenią.

Projekt GitHub

Ponieważ jest to tak ważny TextView, zdecydowałem się opublikować bibliotekę, tak aby każdy mógł łatwo z niej korzystać i przyczyniać się do niej,tutaj.

questionAnswers(15)

yourAnswerToTheQuestion