webentwicklung-frage-antwort-db.com.de

Wie bekomme ich die Zeilenanzahl der Textansicht vor dem Rendern?

Wie kann ich die Anzahl der Zeilen ermitteln, die ein String in einer TextView einnehmen soll, bevor er gerendert wird 

Eine ViewTreeObserver funktioniert nicht, da diese erst ausgelöst werden, nachdem sie gerendert wurde.

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

Teilen Sie nun die Breite des Textes durch die Breite Ihres TextView, um die Gesamtzahl der Zeilen zu erhalten. 

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

Die akzeptierte Antwort funktioniert nicht, wenn ein ganzes Wort in die nächste Zeile eingefügt wird, um zu vermeiden, dass das Wort gebrochen wird: 

|hello   |
|world!  |

Die einzige Möglichkeit, die Anzahl der Zeilen zu 100% zu bestimmen, besteht darin, dieselbe Textfluss-Engine zu verwenden, die auch TextView verwendet. Da TextView seine Rückflusslogik nicht freigibt, gibt es hier einen benutzerdefinierten String-Prozessor, der Text in mehrere Zeilen aufteilt, die jeweils in die angegebene Breite passen. Es tut auch sein Bestes, die Wörter nicht zu brechen, es sei denn das ganze Wort passt nicht:

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

Hier ist ein Teil eines Unit-Tests:

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

Jetzt kann man die Zeilenzahl in TextView zu 100% sicher sein, ohne sie rendern zu müssen:

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()
21
Denis Kniazhev

Wenn Sie die Breite des übergeordneten TextView-Objekts kennen oder bestimmen können, können Sie eine Ansichtsmessung aufrufen, die dazu führt, dass die Zeilenzahl berechnet wird.

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

Die layout der TextView ist nicht mehr null und Sie können die berechnete Zeilenzahl mit myTextView.lineCount überprüfen.

2
M. Palsich

Referenz: Abrufen der Höhe der Textansicht vor dem Rendern in das Layout

Holen Sie sich vor dem Rendern eine TextView-Zeile.

Dies ist meine Codebasis der Link oben. Es arbeitet für mich.

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

Hinweis: Dieser Code muss auch für eine echte Textansicht auf dem Bildschirm festgelegt werden

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