Je veux obtenir le nombre de lignes d'une vue texte
textView.setText("Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............")
textView.getLineCount();
renvoie toujours zéro
Alors j'ai aussi essayé:
ViewTreeObserver vto = this.textView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewTreeObserver obs = textView.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
System.out.println(": " + textView.getLineCount());
}
});
Il retourne la sortie exacte.
Mais cela ne fonctionne que pour une mise en page statique.
Lorsque je gonfle dynamiquement la mise en page, cela ne fonctionne plus.
Comment trouver le nombre de lignes dans un TextView?
J'ai pu obtenir que getLineCount()
ne renvoie pas 0 en utilisant une post
, comme ceci:
textview.setText(“Some text”);
textview.post(new Runnable() {
@Override
public void run() {
int lineCount = textview.getLineCount();
// Use lineCount here
}
});
Comme mentionné dans ce post ,
getLineCount()
vous donnera le nombre correct de lignes seulement après une passe de mise en page.
Cela signifie que vous devez d'abord rendre TextView
avant d'appeler la méthode getLineCount()
.
ViewTreeObserver n’est pas aussi fiable, en particulier lors de l’utilisation de dispositions dynamiques telles que ListView.
Supposons que: 1. Vous ferez quelques travaux en fonction des lignes de TextView . 2. Le travail n'est pas très urgent et peut être fait plus tard.
Voici ma solution:
public class LayoutedTextView extends TextView {
public LayoutedTextView(Context context) {
super(context);
}
public LayoutedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LayoutedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public interface OnLayoutListener {
void onLayouted(TextView view);
}
private OnLayoutListener mOnLayoutListener;
public void setOnLayoutListener(OnLayoutListener listener) {
mOnLayoutListener = listener;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mOnLayoutListener != null) {
mOnLayoutListener.onLayouted(this);
}
}
}
Usage:
LayoutedTextView tv = new LayoutedTextView(context);
tv.setOnLayoutListener(new OnLayoutListener() {
@Override
public void onLayouted(TextView view) {
int lineCount = view.getLineCount();
// do your work
}
});
Je pense que le noeud de cette question est que les utilisateurs veulent pouvoir connaître la taille d'un TextView à l'avance afin de pouvoir le redimensionner de manière dynamique afin de l'adapter au texte. Une utilisation typique pourrait être de créer des bulles de conversation (du moins c'est ce sur quoi je travaillais).
J'ai essayé plusieurs solutions, notamment l'utilisation de getTextBounds () et de measureText (), comme indiqué ici . Malheureusement, les deux méthodes sont légèrement inexactes et n'ont aucun moyen de prendre en compte les sauts de ligne et l'espace de ligne inutilisé. J'ai donc abandonné cette approche.
Cela laisse getLineCount (), dont le problème est que vous devez "rendre" le texte avant que getLineCount () vous donne le nombre de lignes, ce qui en fait une situation poule-et-oeuf. J'ai lu diverses solutions impliquant des auditeurs et des mises en page, mais je ne pouvais tout simplement pas croire qu'il n'y avait rien de plus simple.
Après deux jours de bricolage, j'ai finalement trouvé ce que je cherchais (au moins, ça marche pour moi). Tout se résume à ce que signifie "rendre" le texte. Cela ne signifie pas que le texte doit apparaître à l'écran, mais qu'il doit être préparé pour l'affichage en interne. Cela se produit chaque fois qu'un appel est fait directement à invalidate () ou indirectement, comme lorsque vous effectuez un setText () sur votre TextView, qui appelle invalidate () pour vous puisque l'apparence de la vue a changé.
Quoi qu'il en soit, voici le code de la clé (supposons que vous connaissez déjà les valeurs lineWidth et lineHeight de la bulle de dialogue d'une ligne sur la base de la police):
TextView talkBubble;
// No peeking while we set the bubble up.
talkBubble.setVisibility( View.INVISIBLE );
// I use FrameLayouts so my talk bubbles can overlap
// lineHeight is just a filler at this point
talkBubble.setLayoutParams( new FrameLayout.LayoutParams( lineWidth, lineHeight ) );
// setText() calls invalidate(), which makes getLineCount() do the right thing.
talkBubble.setText( "This is the string we want to dynamically deal with." );
int lineCount = getLineCount();
// Now we set the real size of the talkBubble.
talkBubble.setLayoutParams( new FrameLayout.LayoutParams( lineWidth, lineCount * lineHeight ) );
talkBubble.setVisibility( View.VISIBLE );
En tout cas, c'est ça. La prochaine retouche donnera une bulle sur mesure pour votre texte.
Remarque: dans le programme actuel, j’utilise une bulle distincte pour déterminer les lignes de texte, ce qui me permet de redimensionner ma bulle réelle de manière dynamique, en longueur et en largeur. Cela me permet de réduire mes bulles de gauche à droite pour de courtes déclarations, etc.
Prendre plaisir!
Basé sur @secnelis idea , il existe même une méthode plus simple si vous ciblez l’API 11 ou plus.
Au lieu d’étendre une TextView
, vous pouvez utiliser les fonctionnalités déjà intégrées si View.OnLayoutChangeListener
Dans ListAdapter.getView()
, par exemple
if (h.mTitle.getLineCount() == 0 && h.mTitle.getText().length() != 0) {
h.mTitle.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(final View v, final int left, final int top,
final int right, final int bottom, final int oldLeft,
final int oldTop, final int oldRight, final int oldBottom) {
h.mTitle.removeOnLayoutChangeListener(this);
final int count = h.mTitle.getLineCount();
// do stuff
}
});
} else {
final int count = h.mTitle.getLineCount();
// do stuff
}
textView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// Remove listener because we don't want this called before _every_ frame
textView.getViewTreeObserver().removeOnPreDrawListener(this)
// Drawing happens after layout so we can assume getLineCount() returns the correct value
if(textView.getLineCount() > 2) {
// Do whatever you want in case text view has more than 2 lines
}
return true; // true because we don't want to skip this frame
}
});
Vous pouvez également calculer le nombre de lignes par cette fonction:
private fun countLines(textView: TextView): Int {
return Math.ceil(textView.Paint.measureText(textView.text.toString()) /
textView.measuredWidth.toDouble()).toInt()
}
Gardez à l’esprit que cela ne fonctionnera peut-être pas très bien sur un RecyclerView
.
Est-ce que vous faites ceci onCreate
? Les vues ne sont pas encore définies, donc getLineCount()
est égal à 0 pendant un certain temps. Si vous faites cela plus tard dans la fenêtre LifeCycle, vous obtiendrez le nombre de lignes. Vous aurez du mal à le faire onCreate
, mais onWindowFocusChanged
avec hasFocus=true
a généralement les vues mesurées maintenant.
La suggestion textView.post()
est également bonne