Adatta automaticamente TextView per Android


231

sfondo

Molte volte è necessario adattare automaticamente il carattere di TextView ai limiti dati ad esso.

Il problema

Purtroppo, anche se ci sono molti thread e post (e soluzioni suggerite) che parlano di questo problema (esempio qui , qui e qui ), nessuno di questi funziona davvero bene.

Ecco perché, ho deciso di testare ciascuno di essi fino a quando non trovo il vero affare.

Penso che i requisiti di tale textView dovrebbero essere:

  1. Dovrebbe consentire l'utilizzo di qualsiasi carattere, carattere tipografico, stile e set di caratteri.

  2. Dovrebbe gestire sia la larghezza che l'altezza

  3. Nessun troncamento a meno che il testo non possa adattarsi a causa della limitazione, gli abbiamo dato (esempio: testo troppo lungo, dimensione disponibile troppo piccola). Tuttavia, se lo desideriamo, potremmo richiedere la barra di scorrimento orizzontale / verticale, solo per quei casi.

  4. Dovrebbe consentire la linea multipla o singola. In caso di linee multiple, consentire le linee massima e minima.

  5. Non dovrebbe essere lento nel calcolo. Usando un anello per trovare la dimensione migliore? Almeno ottimizzalo e non incrementare il campionamento di 1 ogni volta.

  6. Nel caso di più righe, dovrebbe consentire di preferire il ridimensionamento o l'utilizzo di più righe e / o consentire di scegliere noi stessi le righe utilizzando il carattere "\ n".

Quello che ho provato

Ho provato così tanti esempi (compresi quelli dei collegamenti, di cui ho scritto) e ho anche provato a modificarli per gestire i casi, di cui ho parlato, ma nessuno funziona davvero.

Ho realizzato un progetto di esempio che mi consente di vedere visivamente se TextView si adatta automaticamente.

Attualmente, il mio progetto di esempio randomizza solo il testo (l'alfabeto inglese più le cifre) e le dimensioni di textView, e lascia che rimanga su una sola riga, ma anche questo non funziona bene su nessuno dei campioni che ho provato.

Ecco il codice (disponibile anche qui ):

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

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

La domanda

Qualcuno sa di una soluzione per questo problema comune che funziona davvero?

Perfino una soluzione che ha molte meno funzioni rispetto a ciò di cui ho scritto, ad esempio una che ha solo un numero costante di righe di testo, e regola il suo carattere in base alle sue dimensioni, senza mai avere strani problemi e avere anche il testo grande / piccolo rispetto al suo spazio disponibile.


Progetto GitHub

Dato che si tratta di un TextView così importante, ho deciso di pubblicare una libreria, in modo che tutti possano usarla facilmente e contribuire ad essa, qui .



@Thrakbad è uno dei link che ho citato. Inoltre non supera il test.
sviluppatore Android

Ah scusa, in qualche modo ho perso l'ultimo esempio
Thrakbad,

Sì, ti prego, credimi, ho provato molti campioni e ho anche provato a modificarli per risolvere i problemi che ho riscontrato, ma non ci sono mai riuscito. Se trovi qualcosa che ritieni possa funzionare, prova. Ho pubblicato un codice di esempio proprio per questo.
sviluppatore Android

1
@rule questo è uno dei post che ho già letto e ho testato tutti gli esempi di codice. anche io penso che tu abbia pubblicato due volte.
sviluppatore Android il

Risposte:


147

Grazie alla semplice correzione di MartinH qui , questo codice prende anche cura di android:drawableLeft, android:drawableRight, android:drawableTope android:drawableBottomtag.


La mia risposta qui dovrebbe renderti felice di ridimensionare Auto Text Text per adattarlo ai limiti

Ho modificato il tuo caso di prova:

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

Sto pubblicando il codice qui su richiesta dello sviluppatore Android :

Effetto finale:

Inserisci qui la descrizione dell'immagine

File di layout di esempio:

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

E il codice 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();
        }
    }
}

Avvertimento:

Fai attenzione a questo bug risolto in Android 3.1 (Honeycomb).


ok, ho testato il codice e sembra che vada bene. tuttavia, hai utilizzato getMaxLines (), che è per API 16, ma puoi archiviare maxLines e ottenerlo sostituendo setMaxLines e memorizzarne il valore. L'ho modificato e ora funziona correttamente. inoltre, poiché a volte il testo potrebbe essere anche troppo lungo per la dimensione più piccola, ho provato a usare setEllipsize, e anche quello ha funzionato! Penso che abbiamo un vincitore. per favore pubblica qui il tuo codice in modo che io possa contrassegnarlo come corretto.
sviluppatore Android il

non hai rimosso getMaxLines () e usato il tuo. funzionerà ancora solo da API16 ... ti preghiamo di cambiarlo in modo che tutti possano usarlo. suggerisco di sostituire setMaxLines per memorizzare il parametro in un campo e quindi accedere al campo invece di utilizzare getMaxLines.
sviluppatore Android il

controlla ora, aggiunto supporto per max linee.
M-WaJeEh,

sembra a posto. dovresti aggiungere "@TargetApi (Build.VERSION_CODES.JELLY_BEAN)" al metodo onTestSize () in modo che Lint non dia un errore / avviso al riguardo e inizializzalo invece nel CTOR. anche l'impostazione della ricerca binaria su statica è una buona cosa, e potresti fare l'inizializzazione sul CTOR più grande e chiamarla dagli altri CTOR. tuttavia, è davvero la risposta migliore e meriti una V. questa è finalmente una buona risposta a questa vecchia domanda. buon lavoro!
sviluppatore Android il

@ M-WaJeEh controlla il nuovo post in questa discussione. un utente chiamato "MartinH" dice che ha una correzione per il tuo codice.
sviluppatore Android

14

Ho modificato un po 'la risposta di M-WaJeEh per tenere conto dei disegni estraibili sui lati.

I getCompoundPaddingXXXX()metodi ritornano padding of the view + drawable space. Vedi ad esempio: TextView.getCompoundPaddingLeft ()

Problema: risolve la misurazione della larghezza e dell'altezza dello spazio TextView disponibile per il testo. Se non prendiamo in considerazione le dimensioni del disegno, questo viene ignorato e il testo finirà per sovrapporsi al disegno.


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

hai aggiornato la fonte di M-WaJeEh. perché l'hai messo come una nuova risposta? so che hai una buona volontà, ma è una buona cosa metterlo come una risposta? non sono sicuro che possa vedere notare la tua risposta ...
sviluppatore Android

1
Mi sono appena registrato per poter fornire quell'aggiornamento. Il mio numero di reputazione è solo 1, il che mi impedisce di commentare post che non sono miei (minimo 15).
MartinH,

Non preoccuparti, forse ti piacerebbe trasmettere la mia risposta a M-WajeEh dato che al momento non riesco nemmeno a contattarlo: /
MartinH,

Puoi per favore provare a descrivere meglio cosa hai fatto? hai corretto un bug? puoi mostrare uno screenshot per dimostrare il bug?
sviluppatore Android

1
Ciao @MartinH, benvenuto in SO. Lo esaminerò sicuramente e aggiornerò la mia risposta citando il tuo nome e pubblicando il link al tuo post dopo il test . Dammi solo qualche giorno, per favore. Oggi sono bloccato da qualche altra parte.
M-WaJeEh,

7

Ok, ho usato l'ultima settimana per riscrivere in modo massiccio il mio codice per adattarlo con precisione al tuo test. Ora puoi copiare questo 1: 1 e funzionerà immediatamente, incluso setSingleLine(). Ricordati di regolare MIN_TEXT_SIZEe MAX_TEXT_SIZEse stai cercando valori estremi.

L'algoritmo convergente è simile al seguente:

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

E l'intera classe può essere trovata qui.

Il risultato è molto flessibile ora. Funziona allo stesso modo dichiarato in xml in questo modo:

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

... oltre che costruito a livello di codice come nel tuo test.

Spero davvero che tu possa usarlo ora. Puoi chiamare setText(CharSequence text)ora per usarlo a proposito. La classe si occupa delle eccezioni incredibilmente rare e dovrebbe essere solida come una roccia. L'unica cosa che l'algoritmo non supporta ancora è:

  • Chiamate a setMaxLines(x)dovex >= 2

Ma ho aggiunto numerosi commenti per aiutarti a costruire questo, se lo desideri!


Notare che:

Se lo usi normalmente senza limitarlo a una sola riga, potrebbero esserci delle parole come hai detto prima. Questa è una funzione Android , non è colpa del AutoFitText. Android romperà sempre le parole troppo lunghe per un TextView ed è in realtà abbastanza conveniente. Se vuoi intervenire qui, per favore, vedi i miei commenti e il mio codice a partire dalla riga 203. Ho già scritto una divisione adeguata e il riconoscimento per te, tutto ciò che dovrai fare d'ora in poi è di dividere le parole e poi modificare come desideri .

In conclusione: dovresti considerare di riscrivere il tuo test per supportare anche i caratteri spaziali, in questo modo:

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

Ciò produrrà risultati molto belli (e più realistici).
Troverai anche dei commenti per iniziare in questa materia.

Buona fortuna e cordiali saluti


ci sono alcuni problemi, ma nel complesso funziona così bene. i problemi: non supporta la linea multipla (come hai scritto) e spezzerà la parola nel caso in cui provi a farlo, include la funzione API16 (addOnGlobalLayoutListener) che penso possa essere totalmente evitata (o almeno usa invece OnPreDrawListener), usa ricerca binaria, quindi potrebbe testare dimensioni che non si adattano affatto (e utilizzare più memoria senza motivo, il che può causare eccezioni se è troppo grande), non supporta la gestione quando cambia qualcosa (come il testo stesso).
sviluppatore Android il

tra l'altro, non è necessario utilizzare la variabile mTestView ed eseguire i test direttamente sulla vista corrente, e in questo modo non dovrai considerare tutte le cose speciali. inoltre, penso che invece di MAX_TEXT_SIZE sia possibile utilizzare targetFieldHeight.
sviluppatore Android il

penso che invece di una ricerca binaria pura (in particolare 2 di essi), puoi ottenere una migliore ipotesi dal rapporto tra l'obiettivo e la dimensione misurata. ci sono anche altri metodi (newton e qualcos'altro che non ricordo) ma non credo si adattino qui. quello che ho suggerito ora è migliore poiché puoi iniziare dalla dimensione minima invece di testare la dimensione massima. Il fatto è che non avrai bisogno di una dimensione massima poiché continui a fare una supposizione migliore.
sviluppatore Android il

penso anche che le 2 ricerche binarie potrebbero essere unite in una singola riga quando la condizione potrebbe essere semplicemente: if (getMeasuredWidth ()> = targetFieldWidth || getMeasuredHeight ()> = targetFieldHeight)
sviluppatore android

1
un altro problema che ho riscontrato è la gravità del testo. penso che non possa essere centrato verticalmente.
sviluppatore Android il

4

Il mio requisito è

  • Fare clic su ScalableTextView
  • Apri un elencoAttività e visualizza vari elementi stringa di lunghezza.
  • Seleziona un testo dall'elenco.
  • Riporta il testo su ScalableTextView in un'altra attività.

Ho fatto riferimento al collegamento: Ridimensiona automaticamente TextView per adattarlo ai limiti (compresi i commenti) e anche DialogTitle.java

Ho scoperto che la soluzione fornita è carina e semplice ma non cambia dinamicamente la dimensione della casella di testo. Funziona alla grande quando la lunghezza del testo selezionata dalla vista elenco è maggiore della lunghezza del testo esistente in ScalableTextView . Se selezionato, il testo ha una lunghezza inferiore al testo esistente in ScalableTextView, non aumenta le dimensioni del testo, mostrando il testo con dimensioni inferiori.

Ho modificato ScalableTextView.java per regolare nuovamente la dimensione del testo in base alla lunghezza del testo. Ecco il mioScalableTextView.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);
            }
        }
    }
}
}

Happy Coding ....


Non dovresti farlo usando la ricerca binaria invece di ridurre di uno per ogni iterazione? Inoltre, sembra che questo codice imponga a TextView di essere con una singola riga di testo, invece di consentirle di avere più righe.
sviluppatore Android

Questa è stata l'unica soluzione che ha funzionato per me. Il mio problema era legato al fatto che TExtView non si adattava alla vista solo in 4.1
FOliveira,

sembra che non corrisponda al genitore per il 100%. vedi screenshot: s14.postimg.org/93c2xgs75/Screenshot_2.png
user25

4

Spiegherò come funziona questo attributo versioni Android inferiori passo dopo passo:

1- Importa la libreria di supporto Android 26.xx sul tuo file di progetto. Se non esiste una libreria di supporto su IDE, verranno scaricati automaticamente.

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- Apri il tuo file XML di layout e refactor come questo tag TextView. Questo scenario è: quando si incorre nella dimensione del carattere sul sistema, adattare il testo alla larghezza disponibile, non a capo automatico.

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

La loro implementazione di Auto-fit TextView non funziona bene però. Ci sono volte che invece di ridimensionare la dimensione del carattere, il testo si sposta sulla riga successiva, malamente ... Viene persino mostrato sui loro video, come se fosse una buona cosa: youtu.be/fjUdJ2aVqE4?t=14 . Notate come la "w" di "TextView" passa alla riga successiva ... Ad ogni modo, ho creato una nuova richiesta al riguardo qui: issuetracker.google.com/issues/68787108 . Per favore, considera di interpretarlo.
sviluppatore Android

@androiddeveloper Hai ragione. Non ho spiegato che l'implementazione di questo tag funziona su una riga. Se hai bisogno di linee di avvolgimento, devi modificare questo attributo: android: layout_height = "wrap_content". Ma android.support.v7.widget.AppCompatTextView e la garanzia di implementazione gradle funzionano con questo attributo.
Sinan Ergin,

Non penso che tu capisca. Non voglio parole parziali per passare alla riga successiva, a meno che non ci sia altro modo per risolverlo. Nel video, puoi facilmente vedere che i caratteri potrebbero essere appena diventati più piccoli. Questo è il problema. Stai dicendo che sai come ripararlo? Significa che non ci saranno parole a capo e cambieranno solo le dimensioni del carattere?
sviluppatore Android

@androiddeveloper Ci sono molti scenari. Il mio scenario è: adattamento automatico del testo da correggere e non a capo automatico. Durante l'incrostazione della dimensione del carattere del sistema, il testo non deve mostrare il ritorno a capo automatico nella visualizzazione testo. Se la parola a capo non ha importanza per te, non preoccuparti di impostare l'altezza fissa. Questa proprietà non utilizzando solo il ritorno a capo automatico, è possibile adattare automaticamente il testo a larghezza fissa TextView.
Sinan Ergin,

Secondo i miei test, le dimensioni del carattere non cambiano correttamente e può verificarsi il problema di a capo automatico.
sviluppatore Android

3

Avviso, bug in Android 3 (Honeycomb) e Android 4.0 (Ice Cream Sandwich)

Versioni di Androids: 3.1 - 4.04 hanno un bug, che setTextSize () all'interno di TextView funziona solo per la prima volta (prima chiamata).

Il bug è descritto nel Problema 22493: Bug di altezza TextView in Android 4.0 e Problema 17343: l'altezza e il testo del pulsante non tornano al loro stato originale dopo l'aumento e la riduzione della dimensione del testo su HoneyComb .

La soluzione alternativa è aggiungere un carattere di nuova riga al testo assegnato a TextView prima di modificare le dimensioni:

final String DOUBLE_BYTE_SPACE = "\u3000";
textView.append(DOUBLE_BYTE_SPACE);

Lo uso nel mio codice come segue:

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

Aggiungo questo carattere "\ u3000" a sinistra e a destra del mio testo, per mantenerlo centrato. Se lo hai allineato a sinistra, aggiungi solo a destra. Naturalmente può anche essere incorporato con il widget AutoResizeTextView, ma volevo mantenere il codice di correzione all'esterno.


puoi mostrare esattamente su quali linee (prima / dopo quali linee) e in quale funzione?
sviluppatore Android il

ok grazie. spero che questo risolva tutto e che chiunque lo legga lo troverà utile. al momento non posso verificarlo. forse la prossima volta che lo userò.
sviluppatore Android il

Questo codice viene chiamato dall'esterno del codice della vista. Conosci forse un buon modo alternativo per gestire questo all'interno del codice della vista?
sviluppatore Android

potresti sovrascrivere textView.setText () e inserire questo codice all'interno della classe TextView
Malachiasz,

Non significherebbe anche che "getText ()" restituirebbe il testo con i caratteri extra?
sviluppatore Android

3

Ora c'è una soluzione ufficiale a questo problema. Il ridimensionamento automatico delle visualizzazioni di testo introdotte con Android O è disponibile nella libreria di supporto 26 ed è retrocompatibile fino ad Android 4.0.

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

Non sono sicuro del motivo per cui https://stackoverflow.com/a/42940171/47680 che includeva anche queste informazioni è stato eliminato da un amministratore.


1
Sì, ne ho sentito parlare anche io, ma quanto è buono? C'è qualche esempio di utilizzo? Anche nel loro video, ho notato un bug: una lettera di una parola arrivava alla riga dopo (incartata): youtu.be/1N9KveJ-FU8?t=781 (nota la "W")
sviluppatore Android

3

Da giugno 2018 Android supporta ufficialmente questa funzionalità per Android 4.0 (livello API 14) e versioni successive.
Con Android 8.0 (livello API 26) e versioni successive:

setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, int autoSizeMaxTextSize, 
        int autoSizeStepGranularity, int unit);

Versioni Android precedenti ad Android 8.0 (livello API 26):

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(TextView textView,
int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit)

Guarda la mia risposta dettagliata .


1

Converti la visualizzazione del testo in un'immagine e ridimensiona l'immagine entro i limiti.

Ecco un esempio su come convertire una vista in un'immagine: convertire una vista in Bitmap senza visualizzarla in Android?

Il problema è che il testo non sarà selezionabile, ma dovrebbe fare il trucco. Non l'ho provato, quindi non sono sicuro di come sarebbe (a causa del ridimensionamento).


È una buona idea, ma questo renderà anche il testo pixelato e rimuoverà qualsiasi funzione che posso usare con textView.
sviluppatore Android

0

Di seguito è avalancha TextView con funzionalità aggiunta per carattere personalizzato.

Uso:

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

Non dimenticare di aggiungere: xmlns: foo = "http://schemas.android.com/apk/res-auto". Il carattere dovrebbe essere nel caminetto delle risorse

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

bug noti: non funziona con Android 4.03 - i caratteri sono invisibili o molto piccoli (l'originale avalancha non funziona troppo) di seguito è una soluzione alternativa per quel bug: https://stackoverflow.com/a/21851239/2075875


0

Prova questo

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

Non penso che funzionerà, poiché ogni volta che cambi la dimensione del testo, causerà il disegno solo dopo aver terminato con il blocco (il che significa che verrà aggiunto alla coda degli eventi). Ciò significa che il ciclo funzionerà al massimo una volta ogni volta che il testo cambia e non garantisce che il numero di righe non superi le 3 righe di testo.
sviluppatore Android

0

Se stai cercando qualcosa di più semplice:

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

Sembra molto breve, ma può superare il test che ho preparato? Inoltre, non capisco quando viene chiamata questa funzione.
sviluppatore Android

No, è ancora più un suggerimento. Lo migliorerò in modo che possa superare il test al più presto.
Sander,

Bene, se è un suggerimento, puoi per favore pubblicarlo direttamente su Github?
sviluppatore Android

0

Soluzione rapida per il problema descritto da @Malachiasz

Ho risolto il problema aggiungendo un supporto personalizzato per questo nella classe di ridimensionamento automatico:

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

Basta chiamare yourView.setTextCompat(newTextValue)invece diyourView.setText(newTextValue)


Perché creare una nuova funzione di setTextCompat, anziché sostituire setText? Inoltre, quale problema?
sviluppatore Android

setText () è un metodo finale di TextView, quindi non sarai in grado di sovrascriverlo. Un'altra opzione sarebbe quella di farlo al di fuori del TextView personalizzato, ma questa soluzione è per usarlo all'interno del TextView.
Ionut Negru,

Non esattamente. Alcuni "setText" sono privati, altri pubblici e alcuni non pubblici. Sembra che la maggior parte possa essere gestita sostituendo il vuoto pubblico setText (testo CharSequence, tipo BufferType). Esiste un'ultima funzione che non conosco il suo scopo: il vuoto finale pubblico setText (char [] text, int start, int len). Forse dare un'occhiata più approfondita al codice.
sviluppatore Android

Tutte quelle varianti di setText () in realtà chiamerebbero il metodo setText (testo CharSequence) alla fine. Se si desidera garantire lo stesso comportamento, è necessario sovrascrivere tale metodo, altrimenti sarebbe molto meglio aggiungere semplicemente il proprio metodo setText () personalizzato.
Ionut Negru,

Sì, ma forse per la maggior parte dei casi, va bene.
sviluppatore Android l'

0

Prova ad aggiungere LayoutParamse MaxWidthe MaxHeightal TextView. Costringerà il layout a rispettare il contenitore padre e non a traboccare.

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

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

Non sono sicuro di cosa stia parlando. Suggerisci di aggiungerlo all'esempio textView, in modo che non abbia il piccolo problema che a volte vedo? in tal caso, perché non l'hai pubblicato sul sito Web di Github?
sviluppatore Android il

0

Da Android O, è possibile ridimensionare automaticamente il testo in 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 ti consente di indicare a TextView di consentire alle dimensioni del testo di espandersi o contrarsi automaticamente per riempire il layout in base alle caratteristiche e ai confini di TextView. Questa impostazione semplifica l'ottimizzazione delle dimensioni del testo su schermi diversi con contenuto dinamico.

La libreria di supporto 26.0 Beta offre pieno supporto alla funzione di ridimensionamento automatico di TextView su dispositivi con versioni di Android precedenti ad Android O. La libreria fornisce supporto per Android 4.0 (livello API 14) e versioni successive. Il pacchetto android.support.v4.widget contiene la classe TextViewCompat per accedere alle funzionalità in modo compatibile con le versioni precedenti.


Purtroppo, secondo i miei test, e anche nel video di Google IO, ho notato che ha problemi, come il confezionamento errato di parole-parole, invece di ridimensionare il carattere. Ne ho parlato qui: issuetracker.google.com/issues/38468964 , ed è per questo che ancora non lo uso.
sviluppatore Android

0

Dopo aver provato Autosizing TextView ufficiale di Android , ho scoperto se la tua versione di Android è precedente ad Android 8.0 (livello API 26), devi usarla android.support.v7.widget.AppCompatTextViewe assicurarti che la versione della tua libreria di supporto sia superiore alla 26.0.0. Esempio:

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

aggiornare :

Secondo la risposta di @ android-developer, controllo il AppCompatActivitycodice sorgente e ho trovato queste due righeonCreate

final AppCompatDelegate delegate = getDelegate(); delegate.installViewFactory();

e in AppCompatDelegateImpl'screateView

    if (mAppCompatViewInflater == null) {
        mAppCompatViewInflater = new AppCompatViewInflater();
    }

utilizza la AppCompatViewInflatervisualizzazione del gonfiatore, quando AppCompatViewInflatercreateView utilizzerà AppCompatTextView per "TextView".

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

Nel mio progetto non uso AppCompatActivity, quindi ho bisogno di usare <android.support.v7.widget.AppCompatTextView>in XML.


1
Nella maggior parte dei casi, il gonfiatore utilizza AppCompatTextView quando si scrive solo "TextView", quindi non è necessario farlo.
sviluppatore Android

@androiddeveloper Ma quando uso TextView, il ridimensionamento automatico non funziona.
weei.zh,

@androiddeveloper Grazie, il motivo è che non lo uso AppCompatActivity. AppCompatActivity utilizza il layout del gonfiatore AppCompatViewInflater.
weei.zh,

Non capisco
sviluppatore Android
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.