web-dev-qa-db-fra.com

Comment obtenir le nombre de lignes de textview avant le rendu?

Comment puis-je obtenir le nombre de lignes qu'une chaîne prendra dans un TextView avant son rendu. 

Une ViewTreeObserver ne fonctionnera pas, car ceux-ci ne sont déclenchés qu'après son rendu.

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

Divisez maintenant la largeur du texte par la largeur de votre TextView pour obtenir le nombre total de lignes. 

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

La réponse acceptée ne fonctionne pas lorsqu'un mot entier est placé sur la ligne suivante afin d'éviter de casser le mot: 

|hello   |
|world!  |

Le seul moyen d'être sûr à 100% du nombre de lignes est d'utiliser le même moteur de flux de texte que TextView. Puisque TextView ne partage pas sa logique de redistribution, voici un processeur de chaînes personnalisé qui divise le texte en plusieurs lignes dont chacune correspond à la largeur donnée. Il fait également de son mieux pour ne pas casser les mots à moins que la Parole ne s’ajuste pas:

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

Voici une partie d'un test unitaire:

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

Maintenant, on peut être sûr à 100% du nombre de lignes dans TextView sans avoir à le rendre:

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

Si vous connaissez ou pouvez déterminer la largeur du parent du TextView, vous pouvez appeler une mesure de vue qui entraîne le calcul du nombre de lignes.

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

layout du TextView n'est plus nul et vous pouvez vérifier le nombre de lignes calculé avec myTextView.lineCount.

2
M. Palsich

Référence: Obtenir la hauteur de la vue texte avant de rendre la mise en page

Obtenir une ligne de TextView avant le rendu.

Ceci est mon code base le lien ci-dessus. Cela fonctionne pour moi.

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

Remarque: Ce code doit également être défini pour l'affichage de texte réel à l'écran.

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