Existe-t-il un moyen dans Android d'ajuster la taille du texte dans une vue de texte pour l'adapter à l'espace qu'elle occupe?
Par exemple, j'utilise un TableLayout
et j'ajoute plusieurs TextView
s à chaque ligne. Comme je ne veux pas que les TextView
s enveloppent le texte, je vois plutôt que cela réduit la taille de la police du contenu.
Des idées?
J'ai essayé measureText
, mais comme je ne connais pas la taille de la colonne, il semble difficile à utiliser. C'est le code où je veux changer la taille de la police en quelque chose qui convient
TableRow row = new TableRow(this);
for (int i=0; i < ColumnNames.length; i++) {
TextView textColumn = new TextView(this);
textColumn.setText(ColumnNames[i]);
textColumn.setPadding(0, 0, 1, 0);
textColumn.setTextColor(getResources().getColor(R.drawable.text_default));
row.addView(textColumn, new TableRow.LayoutParams());
}
table.addView(row, new TableLayout.LayoutParams());
Réponses:
La solution ci-dessous intègre toutes les suggestions ici. Cela commence par ce qui a été initialement publié par Dunni. Il utilise une recherche binaire comme celle de gjpc, mais c'est un peu plus lisible. Il inclut également les corrections de bogues de gregm et une correction de bogue de ma part.
import android.content.Context; import android.graphics.Paint; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialise(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialise(); } private void initialise() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); float hi = 100; float lo = 2; final float threshold = 0.5f; // How close we have to be mTestPaint.set(this.getPaint()); while((hi - lo) > threshold) { float size = (hi+lo)/2; mTestPaint.setTextSize(size); if(mTestPaint.measureText(text) >= targetWidth) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w); } } //Attributes private Paint mTestPaint; }
la source
<com.example.zengame1.FontFitTextView android:paddingTop="5dip" android:id="@+id/childs_name" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:layout_gravity="center" android:textSize="@dimen/text_size"/>
float hi = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
J'ai écrit une classe qui étend TextView et fait cela. Il utilise simplement measureText comme vous le suggérez. Fondamentalement, il a une taille de texte maximale et une taille de texte minimale (qui peut être modifiée) et il parcourt simplement les tailles entre eux par décréments de 1 jusqu'à ce qu'il trouve la plus grande qui conviendra. Pas particulièrement élégant, mais je ne connais pas d'autre moyen.
Voici le code:
import android.content.Context; import android.graphics.Paint; import android.util.AttributeSet; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialise(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialise(); } private void initialise() { testPaint = new Paint(); testPaint.set(this.getPaint()); //max size defaults to the intially specified text size unless it is too small maxTextSize = this.getTextSize(); if (maxTextSize < 11) { maxTextSize = 20; } minTextSize = 10; } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (textWidth > 0) { int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); float trySize = maxTextSize; testPaint.setTextSize(trySize); while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) { trySize -= 1; if (trySize <= minTextSize) { trySize = minTextSize; break; } testPaint.setTextSize(trySize); } this.setTextSize(trySize); } } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w); } } //Getters and Setters public float getMinTextSize() { return minTextSize; } public void setMinTextSize(int minTextSize) { this.minTextSize = minTextSize; } public float getMaxTextSize() { return maxTextSize; } public void setMaxTextSize(int minTextSize) { this.maxTextSize = minTextSize; } //Attributes private Paint testPaint; private float minTextSize; private float maxTextSize; }
la source
Il s'agit de speedplane
FontFitTextView
, mais il ne réduit la taille de la police que si nécessaire pour adapter le texte et conserve sa taille de police dans le cas contraire. Il n'augmente pas la taille de la police pour l'adapter à la hauteur.public class FontFitTextView extends TextView { // Attributes private Paint mTestPaint; private float defaultTextSize; public FontFitTextView(Context context) { super(context); initialize(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); defaultTextSize = getTextSize(); } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (textWidth <= 0 || text.isEmpty()) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); // this is most likely a non-relevant call if( targetWidth<=2 ) return; // text already fits with the xml-defined font size? mTestPaint.set(this.getPaint()); mTestPaint.setTextSize(defaultTextSize); if(mTestPaint.measureText(text) <= targetWidth) { this.setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize); return; } // adjust text size using binary search for efficiency float hi = defaultTextSize; float lo = 2; final float threshold = 0.5f; // How close we have to be while (hi - lo > threshold) { float size = (hi + lo) / 2; mTestPaint.setTextSize(size); if(mTestPaint.measureText(text) >= targetWidth ) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw || h != oldh) { refitText(this.getText().toString(), w); } } }
Voici un exemple comment il pourrait être utilisé en xml:
<com.your.package.activity.widget.FontFitTextView android:id="@+id/my_id" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="My Text" android:textSize="60sp" />
Cela garderait la taille de la police à 60sp tant que le texte tient dans la largeur. Si le texte est plus long, cela réduira la taille de la police. Dans ce cas, la
TextView
hauteur s changera également en raison deheight=wrap_content
.Si vous trouvez des bogues, n'hésitez pas à les modifier.
la source
this.setMeasuredDimension(parentWidth, height);
match_parent
etwrap_content
.text.isEmpty()
serait"".equals(text)
.Voici ma solution qui fonctionne sur l'émulateur et les téléphones mais pas très bien sur l'éditeur de mise en page Eclipse. Il est inspiré du code de kilaka mais la taille du texte n'est pas obtenue à partir du Paint mais de la mesure du TextView lui-même appelant
measure(0, 0)
.La classe Java:
public class FontFitTextView extends TextView { private static final float THRESHOLD = 0.5f; private enum Mode { Width, Height, Both, None } private int minTextSize = 1; private int maxTextSize = 1000; private Mode mode = Mode.None; private boolean inComputation; private int widthMeasureSpec; private int heightMeasureSpec; public FontFitTextView(Context context) { super(context); } public FontFitTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FontFitTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray tAttrs = context.obtainStyledAttributes(attrs, R.styleable.FontFitTextView, defStyle, 0); maxTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_maxTextSize, maxTextSize); minTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_minTextSize, minTextSize); tAttrs.recycle(); } private void resizeText() { if (getWidth() <= 0 || getHeight() <= 0) return; if(mode == Mode.None) return; final int targetWidth = getWidth(); final int targetHeight = getHeight(); inComputation = true; float higherSize = maxTextSize; float lowerSize = minTextSize; float textSize = getTextSize(); while(higherSize - lowerSize > THRESHOLD) { textSize = (higherSize + lowerSize) / 2; if (isTooBig(textSize, targetWidth, targetHeight)) { higherSize = textSize; } else { lowerSize = textSize; } } setTextSize(TypedValue.COMPLEX_UNIT_PX, lowerSize); measure(widthMeasureSpec, heightMeasureSpec); inComputation = false; } private boolean isTooBig(float textSize, int targetWidth, int targetHeight) { setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); measure(0, 0); if(mode == Mode.Both) return getMeasuredWidth() >= targetWidth || getMeasuredHeight() >= targetHeight; if(mode == Mode.Width) return getMeasuredWidth() >= targetWidth; else return getMeasuredHeight() >= targetHeight; } private Mode getMode(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) return Mode.Both; if(widthMode == MeasureSpec.EXACTLY) return Mode.Width; if(heightMode == MeasureSpec.EXACTLY) return Mode.Height; return Mode.None; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(!inComputation) { this.widthMeasureSpec = widthMeasureSpec; this.heightMeasureSpec = heightMeasureSpec; mode = getMode(widthMeasureSpec, heightMeasureSpec); resizeText(); } } protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { resizeText(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw || h != oldh) resizeText(); } public int getMinTextSize() { return minTextSize; } public void setMinTextSize(int minTextSize) { this.minTextSize = minTextSize; resizeText(); } public int getMaxTextSize() { return maxTextSize; } public void setMaxTextSize(int maxTextSize) { this.maxTextSize = maxTextSize; resizeText(); } }
Le fichier d'attributs XML:
<resources> <declare-styleable name="FontFitTextView"> <attr name="minTextSize" format="dimension" /> <attr name="maxTextSize" format="dimension" /> </declare-styleable> </resources>
Vérifiez mon github pour la dernière version de cette classe. J'espère que cela peut être utile pour quelqu'un. Si un bogue est trouvé ou si le code nécessite des explications, n'hésitez pas à ouvrir un problème sur Github.
la source
Merci beaucoup à https://stackoverflow.com/users/234270/speedplane . Très bonne réponse!
Voici une version améliorée de sa réponse qui prend également en charge la hauteur et est livrée avec un attribut maxFontSize pour limiter la taille de la police (c'était utile dans mon cas, je voulais donc la partager):
package com.<your_package>; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { private Paint mTestPaint; private float maxFontSize; private static final float MAX_FONT_SIZE_DEFAULT_VALUE = 20f; public FontFitTextView(Context context) { super(context); initialise(context, null); } public FontFitTextView(Context context, AttributeSet attributeSet) { super(context, attributeSet); initialise(context, attributeSet); } public FontFitTextView(Context context, AttributeSet attributeSet, int defStyle) { super(context, attributeSet, defStyle); initialise(context, attributeSet); } private void initialise(Context context, AttributeSet attributeSet) { if(attributeSet!=null) { TypedArray styledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.FontFitTextView); maxFontSize = styledAttributes.getDimension(R.styleable.FontFitTextView_maxFontSize, MAX_FONT_SIZE_DEFAULT_VALUE); styledAttributes.recycle(); } else { maxFontSize = MAX_FONT_SIZE_DEFAULT_VALUE; } mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth, int textHeight) { if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom(); float hi = maxFontSize; float lo = 2; // final float threshold = 0.5f; // How close we have to be final float threshold = 1f; // How close we have to be mTestPaint.set(this.getPaint()); Rect bounds = new Rect(); while ((hi - lo) > threshold) { float size = (hi + lo) / 2; mTestPaint.setTextSize(size); mTestPaint.getTextBounds(text, 0, text.length(), bounds); if (bounds.width() >= targetWidth || bounds.height() >= targetHeight) hi = size; // too big else lo = size; // too small // if (mTestPaint.measureText(text) >= targetWidth) // hi = size; // too big // else // lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth, height); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth(), this.getHeight()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w, h); } } }
Fichier /res/values/attr.xml correspondant:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="FontFitTextView"> <attr name="maxFontSize" format="dimension" /> </declare-styleable> </resources>
Exemple:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:res-auto="http://schemas.android.com/apk/res-auto" android:id="@+id/home_Layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" tools:ignore="ContentDescription" > ... <com.<your_package>.FontFitTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:text="Sample Text" android:textSize="28sp" res-auto:maxFontSize="35sp"/> ... </RelativeLayout>
Pour utiliser le nouvel
maxFontSize
attribut, n'oubliez pas d'ajouterxmlns:res-auto="http://schemas.android.com/apk/res-auto"
comme indiqué dans l'exemple.la source
Vous pouvez maintenant le faire sans bibliothèque tierce ni widget. Il est intégré à TextView au niveau de l'API 26. Ajoutez-
android:autoSizeTextType="uniform"
y votreTextView
hauteur et définissez-la. C'est tout. Utiliserapp:autoSizeTextType="uniform"
pour la compatibilité descendantehttps://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
<?xml version="1.0" encoding="utf-8"?> <TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" />
Vous pouvez également utiliser
TextViewCompat
pour la compatibilité.la source
J'ai eu le même problème et j'ai écrit un cours qui semble fonctionner pour moi. Fondamentalement, j'ai utilisé une mise en page statique pour dessiner le texte dans un canevas séparé et remesurer jusqu'à ce que je trouve une taille de police qui convient. Vous pouvez voir la classe publiée dans le sujet ci-dessous. J'espère que cela aide.
Mise à l'échelle automatique du texte TextView pour s'adapter aux limites
la source
Légère modification de onMeasure:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, parentHeight); }
Et recherche binaire sur refitText:
private void refitText(String text, int textWidth) { if (textWidth > 0) { int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); int trySize = (int)maxTextSize; int increment = ~( trySize - (int)minTextSize ) / 2; testPaint.setTextSize(trySize); while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) { trySize += increment; increment = ( increment == 0 ) ? -1 : ~increment / 2; if (trySize <= minTextSize) { trySize = (int)minTextSize; break; } testPaint.setTextSize(trySize); } this.setTextSize( TypedValue.COMPLEX_UNIT_PX, trySize); } }
la source
trySize *= availableWidth / measured_width
(puis fixée à minTextSize).J'ai trouvé que ce qui suit fonctionnait bien pour moi. Il ne boucle pas et tient compte à la fois de la hauteur et de la largeur. Notez qu'il est important de spécifier l'unité PX lors de l'appel de setTextSize sur la vue. Merci à l'astuce dans un article précédent pour cela!
Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight); setTextSize(TypedValue.COMPLEX_UNIT_PX,paint.getTextSize());
Voici la routine que j'utilise, en passant le getPaint () depuis la vue. Une chaîne de 10 caractères avec un caractère «large» est utilisée pour estimer la largeur indépendamment de la chaîne réelle.
private static final String text10="OOOOOOOOOO"; public static Paint adjustTextSize(Paint paint, int numCharacters, int widthPixels, int heightPixels) { float width = paint.measureText(text10)*numCharacters/text10.length(); float newSize = (int)((widthPixels/width)*paint.getTextSize()); paint.setTextSize(newSize); // remeasure with font size near our desired result width = paint.measureText(text10)*numCharacters/text10.length(); newSize = (int)((widthPixels/width)*paint.getTextSize()); paint.setTextSize(newSize); // Check height constraints FontMetricsInt metrics = paint.getFontMetricsInt(); float textHeight = metrics.descent-metrics.ascent; if (textHeight > heightPixels) { newSize = (int)(newSize * (heightPixels/textHeight)); paint.setTextSize(newSize); } return paint; }
la source
À utiliser
app:autoSizeTextType="uniform"
pour la compatibilité descendante, carandroid:autoSizeTextType="uniform"
ne fonctionne qu'avec le niveau d'API 26 et supérieur.la source
app:..
cela fonctionnera pour SDK low 26?Fonctionne avec modification
Vous devez définir la taille de la vue du texte comme ceci, car sinon setTextSize suppose que la valeur est en unités SP:
setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize);
Et vous deviez ajouter explicitement ce code.
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); refitText(this.getText().toString(), parentWidth); }
la source
J'ai eu cette douleur dans mes projets pendant tellement longtemps jusqu'à ce que je trouve cette bibliothèque:
compile 'me.grantland:autofittextview:0.2.+'
Il vous suffit d'ajouter le xml selon vos besoins et c'est fait. Par exemple:
<me.grantland.widget.AutofitTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:maxLines="2" android:textSize="40sp" autofit:minTextSize="16sp" />
la source
J'ai utilisé une variante de la solution Dunni ci-dessus, mais ce code particulier n'a pas fonctionné pour moi. En particulier, lorsque vous essayez d'utiliser le jeu d'objets Paint pour avoir les caractéristiques de l'objet Paint de la vue, puis que vous appelez measureText (), il ne renvoie pas la même valeur que l'appel direct de l'objet Paint de la vue. Peut-être y a-t-il des différences dans la façon dont mes opinions sont établies qui rendent le comportement différent.
Ma solution consistait à utiliser directement la peinture de la vue, même s'il pouvait y avoir des pénalités de performance en changeant la taille de la police pour la vue plusieurs fois.
la source
J'ai travaillé à l'amélioration de l'excellente solution de speedplane et j'ai trouvé ça. Il gère la hauteur, y compris la définition de la marge pour que le texte soit correctement centré verticalement.
Cela utilise la même fonction pour obtenir la largeur, car cela semble fonctionner le mieux, mais il utilise une fonction différente pour obtenir la hauteur, car la hauteur n'est fournie nulle part. Il y a quelques corrections à apporter, mais j'ai trouvé un moyen de le faire, tout en ayant l'air agréable à regarder.
import android.content.Context; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialize(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth,int textHeight) { if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom(); float hi = Math.min(targetHeight,100); float lo = 2; final float threshold = 0.5f; // How close we have to be Rect bounds = new Rect(); mTestPaint.set(this.getPaint()); while((hi - lo) > threshold) { float size = (hi+lo)/2; mTestPaint.setTextSize(size); mTestPaint.getTextBounds(text, 0, text.length(), bounds); if((mTestPaint.measureText(text)) >= targetWidth || (1+(2*(size+(float)bounds.top)-bounds.bottom)) >=targetHeight) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX,(float) lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth,height); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth(),this.getHeight()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w,h); } } //Attributes private Paint mTestPaint; }
la source
Google a déjà créé cette fonctionnalité.
<TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" />
https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
la source
Inspiré des affiches précédentes, j'ai voulu partager ma solution. Il fonctionne avec un facteur d'échelle qui est appliqué à la taille de police précédente pour l'adapter à l'espace disponible. En plus d'éviter un comportement inattendu de la méthode TextViews onDraw, elle dessine simplement le texte seul.
public class FontFitTextView extends TextView { // How much of the available space should be used in percent. private static final float MARGINHEIGHT = 0.8f; private static final float MARGINWIDTH = 0.8f; private Paint paint; private int viewWidth; private int viewHeight; private float textHeight; private float textWidth; public FontFitTextView(Context c) { this(c, null); } public FontFitTextView(Context c, AttributeSet attrs) { super(c, attrs); initComponent(); } // Default constructor override public FontFitTextView(Context c, AttributeSet attrs, int defStyle) { super(c, attrs, defStyle); initComponent(); } private void initComponent() { paint = new Paint(); paint.setTextSize(30); paint.setTextAlign(Align.CENTER); paint.setAntiAlias(true); } public void setFontColor(int c) { paint.setColor(c); } private void calcTextSize(String s, Canvas c) { float availableHeight = viewHeight; float availableWidth = viewWidth; // This value scales the old font up or down to match the available // space. float scale = 1.0f; // Rectangle for measuring the text dimensions Rect rect = new Rect(); float oldFontSize = paint.getTextSize(); // Calculate the space used with old font size paint.getTextBounds(s, 0, s.length(), rect); textWidth = rect.width(); textHeight = rect.height(); // find scale-value to fit the text horizontally float scaleWidth = 1f; if (textWidth > 0.0f) { scaleWidth = (availableWidth) / textWidth * MARGINWIDTH; } // find scale-value to fit the text vertically float scaleHeight = 1f; if (textHeight > 0.0f) { scaleHeight = (availableHeight) / textHeight * MARGINHEIGHT; } // We are always limited by the smaller one if (scaleWidth < scaleHeight) { scale = scaleWidth; } else { scale = scaleHeight; } // We apply the scale to the old font size to make it bigger or smaller float newFontSize = (oldFontSize * scale); paint.setTextSize(newFontSize); } /** * Calculates the origin on the Y-Axis (width) for the text in this view. * * @return */ private float calcStartDrawingPosX() { float left = getMeasuredWidth(); float centerY = left - (viewWidth / 2); return centerY; } /** * Calculates the origin on the Y-Axis (height) for the text in this view. * * @return */ private float calcStartDrawingPosY() { float bottom = getMeasuredHeight(); // The paint only centers horizontally, origin on the Y-Axis stays at // the bottom, thus we have to lift the origin additionally by the // height of the font. float centerX = bottom - (viewHeight / 2) + (textHeight / 2); return centerX; } @Override protected void onDraw(Canvas canvas) { String text = getText().toString(); if (text.length() > 0) { calcTextSize(text, canvas); canvas.drawText(text, calcStartDrawingPosX(), calcStartDrawingPosY(), paint); } }; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { viewWidth = w; viewHeight = h; super.onSizeChanged(w, h, oldw, oldh); } }
la source
/* get your context */ Context c = getActivity().getApplicationContext(); LinearLayout l = new LinearLayout(c); l.setOrientation(LinearLayout.VERTICAL); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0); l.setLayoutParams(params); l.setBackgroundResource(R.drawable.border); TextView tv=new TextView(c); tv.setText(" your text here"); /* set typeface if needed */ Typeface tf = Typeface.createFromAsset(c.getAssets(),"fonts/VERDANA.TTF"); tv.setTypeface(tf); // LayoutParams lp = new LayoutParams(); tv.setTextColor(Color.parseColor("#282828")); tv.setGravity(Gravity.CENTER | Gravity.BOTTOM); // tv.setLayoutParams(lp); tv.setTextSize(20); l.addView(tv); return l;
la source
Cela devrait être une solution simple:
public void correctWidth(TextView textView, int desiredWidth) { Paint paint = new Paint(); Rect bounds = new Rect(); paint.setTypeface(textView.getTypeface()); float textSize = textView.getTextSize(); paint.setTextSize(textSize); String text = textView.getText().toString(); paint.getTextBounds(text, 0, text.length(), bounds); while (bounds.width() > desiredWidth) { textSize--; paint.setTextSize(textSize); paint.getTextBounds(text, 0, text.length(), bounds); } textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); }
la source
Étendez TextView et remplacez onDraw avec le code ci-dessous. Il conservera les proportions du texte mais le dimensionnera pour remplir l'espace. Vous pouvez facilement modifier le code pour l'étirer si nécessaire.
@Override protected void onDraw(@NonNull Canvas canvas) { TextPaint textPaint = getPaint(); textPaint.setColor(getCurrentTextColor()); textPaint.setTextAlign(Paint.Align.CENTER); textPaint.drawableState = getDrawableState(); String text = getText().toString(); float desiredWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - 2; float desiredHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - 2; float textSize = textPaint.getTextSize(); for (int i = 0; i < 10; i++) { textPaint.getTextBounds(text, 0, text.length(), rect); float width = rect.width(); float height = rect.height(); float deltaWidth = width - desiredWidth; float deltaHeight = height - desiredHeight; boolean fitsWidth = deltaWidth <= 0; boolean fitsHeight = deltaHeight <= 0; if ((fitsWidth && Math.abs(deltaHeight) < 1.0) || (fitsHeight && Math.abs(deltaWidth) < 1.0)) { // close enough break; } float adjustX = desiredWidth / width; float adjustY = desiredHeight / height; textSize = textSize * (adjustY < adjustX ? adjustY : adjustX); // adjust text size textPaint.setTextSize(textSize); } float x = desiredWidth / 2f; float y = desiredHeight / 2f - rect.top - rect.height() / 2f; canvas.drawText(text, x, y, textPaint); }
la source
J'ai écrit une courte classe d'aide qui fait tenir une vue de texte dans une certaine largeur et ajoute ellipsize "..." à la fin si la taille minimale du texte ne peut pas être atteinte.
Gardez à l'esprit que cela ne réduit le texte que jusqu'à ce qu'il tienne ou jusqu'à ce que la taille minimale du texte soit atteinte. Pour tester avec de grandes tailles, définissez la taille du texte sur un grand nombre avant d'appeler la méthode d'aide.
Cela prend des pixels, donc si vous utilisez des valeurs de dimen, vous pouvez l'appeler comme ceci:
float minTextSizePx = getResources().getDimensionPixelSize(R.dimen.min_text_size); float maxTextWidthPx = getResources().getDimensionPixelSize(R.dimen.max_text_width); WidgetUtils.fitText(textView, text, minTextSizePx, maxTextWidthPx);
C'est la classe que j'utilise:
public class WidgetUtils { public static void fitText(TextView textView, String text, float minTextSizePx, float maxWidthPx) { textView.setEllipsize(null); int size = (int)textView.getTextSize(); while (true) { Rect bounds = new Rect(); Paint textPaint = textView.getPaint(); textPaint.getTextBounds(text, 0, text.length(), bounds); if(bounds.width() < maxWidthPx){ break; } if (size <= minTextSizePx) { textView.setEllipsize(TextUtils.TruncateAt.END); break; } size -= 1; textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); } } }
la source
Si une transformation comme allCaps est définie, l'approche du speedplane est boguée. Je l'ai corrigé, ce qui a donné le code suivant (désolé, ma réputation ne me permet pas d'ajouter cela en commentaire à la solution de speedplane):
import android.content.Context; import android.graphics.Paint; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialise(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialise(); } private void initialise() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (getTransformationMethod() != null) { text = getTransformationMethod().getTransformation(text, this).toString(); } if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); float hi = 100; float lo = 2; final float threshold = 0.5f; // How close we have to be mTestPaint.set(this.getPaint()); while((hi - lo) > threshold) { float size = (hi+lo)/2; if(mTestPaint.measureText(text) >= targetWidth) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w); } } //Attributes private Paint mTestPaint; }
la source
Je ne savais pas que c'était la bonne façon ou non parce que cela fonctionnait ... prenez votre avis et vérifiez OnGlobalLayoutListener () et obtenez le nombre de lignes textview, puis définissez textSize.
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (textView.getLineCount()>=3) { textView.setTextSize(20); }else{ //add somthing } } });
Son code en quelques lignes très simple.
la source