2013-03-28 3 views
24

Come posso ottenere il numero di linee che una stringa occuperà in un TextView prima del rendering.Come ottenere il conteggio delle righe di textview prima del rendering?

A ViewTreeObserver non funziona perché vengono attivati ​​solo dopo il rendering.

+3

possibile duplicati di http://stackoverflow.com/questions/12037377/how- to-get-number-of-lines-of-textview – Krishnabhadra

+2

Non è una domanda duplicata. perché voti per chiudere questa domanda? è una buona domanda e qualcuno ha risposto alla mia domanda ed è la risposta corretta. – Nandha

+1

Confermato non un duplicato. Ho usato questo nella mia soluzione a causa della necessità di calcolare il numero di linee senza utilizzare una vista precaricata (perché la vista precaricata è stata abbreviata dal metodo "SetMaxLines" di TextView). Il thread duplicato fornito non copre questa possibilità. Upvoting sia la soluzione che la domanda perché erano estremamente utili. – moscro

risposta

16
Rect bounds = new Rect(); 
Paint paint = new Paint(); 
paint.setTextSize(currentSize); 
paint.getTextBounds(testString, 0, testString.length(), bounds); 

int width = (int) Math.ceil((float) bounds.width()/currentSize); 

Ora dividi la larghezza del testo con la larghezza del tuo TextView e ottieni le linee totali.

+1

Perché codificare quando hai già un metodo readymade, 'TextView.getLineCount()'? – Raynold

+20

Chiede di misurarlo prima di renderlo – Triode

+0

Grazie Rajesh. Risolve il mio problema. – Nandha

17

La risposta accettata non funziona quando una parola intera è posto sulla riga successiva, al fine di evitare di rompere la parola:

|hello | 
|world! | 

L'unico modo per essere sicuri al 100% circa il numero di linee è utilizzare lo stesso motore di flusso di testo utilizzato da TextView. Poiché TextView non condivide la logica del reflusso, ecco un processore di stringhe personalizzato che suddivide il testo in più righe, ciascuna delle quali si adatta alla larghezza specificata. Lo fa anche del suo meglio per non spezzare le parole a meno che l'intera parola non va bene:

public List<String> splitWordsIntoStringsThatFit(String source, float maxWidthPx, Paint paint) { 
    ArrayList<String> result = new ArrayList<>(); 

    ArrayList<String> currentLine = new ArrayList<>(); 

    String[] sources = source.split("\\s"); 
    for(String chunk : sources) { 
     if(paint.measureText(chunk) < maxWidthPx) { 
      processFitChunk(maxWidthPx, paint, result, currentLine, chunk); 
     } else { 
      //the chunk is too big, split it. 
      List<String> splitChunk = splitIntoStringsThatFit(chunk, maxWidthPx, paint); 
      for(String chunkChunk : splitChunk) { 
       processFitChunk(maxWidthPx, paint, result, currentLine, chunkChunk); 
      } 
     } 
    } 

    if(! currentLine.isEmpty()) { 
     result.add(TextUtils.join(" ", currentLine)); 
    } 
    return result; 
} 

/** 
* Splits a string to multiple strings each of which does not exceed the width 
* of maxWidthPx. 
*/ 
private List<String> splitIntoStringsThatFit(String source, float maxWidthPx, Paint paint) { 
    if(TextUtils.isEmpty(source) || paint.measureText(source) <= maxWidthPx) { 
     return Arrays.asList(source); 
    } 

    ArrayList<String> result = new ArrayList<>(); 
    int start = 0; 
    for(int i = 1; i <= source.length(); i++) { 
     String substr = source.substring(start, i); 
     if(paint.measureText(substr) >= maxWidthPx) { 
      //this one doesn't fit, take the previous one which fits 
      String fits = source.substring(start, i - 1); 
      result.add(fits); 
      start = i - 1; 
     } 
     if (i == source.length()) { 
      String fits = source.substring(start, i); 
      result.add(fits); 
     } 
    } 

    return result; 
} 

/** 
* Processes the chunk which does not exceed maxWidth. 
*/ 
private void processFitChunk(float maxWidth, Paint paint, ArrayList<String> result, ArrayList<String> currentLine, String chunk) { 
    currentLine.add(chunk); 
    String currentLineStr = TextUtils.join(" ", currentLine); 
    if (paint.measureText(currentLineStr) >= maxWidth) { 
     //remove chunk 
     currentLine.remove(currentLine.size() - 1); 
     result.add(TextUtils.join(" ", currentLine)); 
     currentLine.clear(); 
     //ok because chunk fits 
     currentLine.add(chunk); 
    } 
} 

Ecco una parte di una prova di unità:

String text = "Hello this is a very long and meanless chunk: abcdefghijkonetuhosnahrc.pgraoneuhnotehurc.pgansohtunsaohtu. Hope you like it!"; 
    Paint paint = new Paint(); 
    paint.setTextSize(30); 
    paint.setTypeface(Typeface.DEFAULT_BOLD); 

    List<String> strings = splitWordsIntoStringsThatFit(text, 50, paint); 
    assertEquals(3, strings.size()); 
    assertEquals("Hello this is a very long and meanless chunk:", strings.get(0)); 
    assertEquals("abcdefghijkonetuhosnahrc.pgraoneuhnotehurc.pganso", strings.get(1)); 
    assertEquals("htunsaohtu. Hope you like it!", strings.get(2)); 

Ora si può essere sicuri al 100% sulla linea contare in TextView senza bisogno di renderlo:

TextView textView = ...   //text view must be of fixed width 

Paint paint = new Paint(); 
paint.setTextSize(yourTextViewTextSizePx); 
paint.setTypeface(yourTextViewTypeface); 

float textViewWidthPx = ...; 

List<String> strings = splitWordsIntoStringsThatFit(yourText, textViewWidthPx, paint); 
textView.setText(TextUtils.join("\n", strings); 

int lineCount = strings.size();  //will be the same as textView.getLineCount() 
+2

Questo dovrebbe essere accettato come la vera risposta. – kroky

+0

Questa è la vera soluzione esatta. Mi hai davvero aiutato così tanto. Grazie mille. –

+0

Questa è la soluzione migliore finora. Grazie –

0

Si dovrebbe usare il nuovo TextViewCompat per ridimensionare il testo. Include il supporto per le librerie di versione 26.

nel build.gradle:

compile "com.android.support:appcompat-v7:$supportLibVersion" 

Nella vostra attività:

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(textView, 
    textMinSizeAsAnInteger, 
    textMaxSizeAsAnInteger, 
    stepIncrement, 
    TypedValue.COMPLEX_UNIT_PX);