Подтвердить что ты не робот

Как получить количество строк текста перед рендерингом?

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Здесь часть unit test:

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

Ответ 3

Если вы знаете или можете определить ширину родительского элемента TextView, вы можете вызвать измерение представления, в результате которого вычисляется количество строк.

val parentWidth = PARENT_WIDTH // assumes this is known/can be found
myTextView.measure(
    MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY),
    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))

TextView layout больше не равен нулю, и вы можете проверить рассчитанное количество строк с помощью myTextView.lineCount.

Ответ 4

Вы должны использовать новый TextViewCompat для изменения размера текста. Включить поддержку libs версии 26.

в файле build.gradle:

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

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

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

Ответ 5

Ссылка: Получение высоты просмотра текста перед рендерингом в макет

Получить строку TextView перед рендерингом.

Это моя база кода по ссылке выше. Это работает для меня.

private int widthMeasureSpec;
private int heightMeasureSpec;
private int heightOfEachLine;
private int paddingFirstLine;
private void calculateHeightOfEachLine() {
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    int deviceWidth = size.x;
    widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
    heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    //1 line = 76; 2 lines = 76 + 66; 3 lines = 76 + 66 + 66
    //=> height of first line = 76 pixel; height of second line = third line =... n line = 66 pixel
    int heightOfFirstLine = getHeightOfTextView("A");
    int heightOfSecondLine = getHeightOfTextView("A\nA") - heightOfFirstLine;
    paddingFirstLine = heightOfFirstLine - heightOfSecondLine;
    heightOfEachLine = heightOfSecondLine;
}

private int getHeightOfTextView(String text) {
    // Getting height of text view before rendering to layout
    TextView textView = new TextView(context);
    textView.setPadding(10, 0, 10, 0);
    //textView.setTypeface(typeface);
    textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.tv_size_14sp));
    textView.setText(text, TextView.BufferType.SPANNABLE);
    textView.measure(widthMeasureSpec, heightMeasureSpec);
    return textView.getMeasuredHeight();
}

private int getLineCountOfTextViewBeforeRendering(String text) {
    return (getHeightOfTextView(text) - paddingFirstLine) / heightOfEachLine;
}

Примечание. Этот код также должен быть установлен для реального просмотра текста на экране

textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.tv_size_14sp));