2013-03-28 5 views
24

Как я могу получить количество строк, которые строка будет занимать в TextView перед ее визуализацией.Как получить количество строк текста перед рендерингом?

A ViewTreeObserver не будет работать, потому что они запускаются только после его рендеринга.

+3

возможно дубликат http://stackoverflow.com/questions/12037377/how- to-get-number-of-lines-of-textview – Krishnabhadra

+2

Это не дублирующий вопрос. почему вы голосуете, чтобы закрыть этот вопрос? это хороший вопрос, и кому-то ответили на мой вопрос, и это правильный ответ. – Nandha

+1

Подтвержденный не дубликат. Я использовал это в своем собственном решении из-за необходимости вычислить количество строк без использования предустановленного представления (поскольку предустановленное представление было укорочено методом «SetMaxLines» TextView). Дублированный поток не распространяется на эту возможность. Упреждающее решение и вопрос, потому что они были чрезвычайно полезны. – moscro

ответ

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

Теперь разделите ширину текста на ширину вашего TextView и получите полные строки.

+1

Зачем кодировать, когда у вас уже есть готовый метод, 'TextView.getLineCount()'? – Raynold

+20

Он просит измерить его перед рендерингом – Triode

+0

Спасибо Раджеш. Это решает мою проблему. – Nandha

17

Принятый ответ не работает, когда целое слово помещается на следующей строке, чтобы избежать разрыва слова:

|hello | 
|world! | 

Единственный способ быть на 100% уверен, что количество линий использовать тот же движок текстового потока, который использует TextView. Так как TextView не использует логику повторного потока, это пользовательский строковый процессор, который разбивает текст на несколько строк, каждый из которых соответствует заданной ширине. Она также делает все возможное, чтобы не нарушать слова, если слово целиком не укладывается:

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

Вот часть модульного теста:

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

Теперь один может быть 100% уверены в линии счет в TextView без необходимости его визуализации:

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

Это должно быть принято как реальный ответ. – kroky

+0

Это настоящее точное решение. Ты мне очень помог. Спасибо. –

+0

Это лучшее решение. Спасибо –

0

Чтобы изменить размер текста, вы должны использовать новый TextViewCompat. Включите поддержку ЛИЭС версия 26.

в вашем build.gradle:

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

В вашей деятельности:

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(textView, 
    textMinSizeAsAnInteger, 
    textMaxSizeAsAnInteger, 
    stepIncrement, 
    TypedValue.COMPLEX_UNIT_PX); 
Смежные вопросы