J'essaie de créer une méthode de redimensionnement d'un texte multiligne dans une variable TextView
telle qu'elle s'adapte aux limites (dimensions X et Y) de la variable TextView
.
Pour le moment, j'ai quelque chose à faire, mais tout ce que cela fait est de redimensionner le texte de sorte que seule la première lettre/caractère du texte remplisse les dimensions de la variable TextView
(c'est-à-dire que seule la première lettre est visible et qu'elle est énorme). J'en ai besoin pour que toutes les lignes du texte tiennent dans les limites de TextView.
Voici ce que j'ai jusqu'à présent:
public static void autoScaleTextViewTextToHeight(TextView tv)
{
final float initSize = tv.getTextSize();
//get the width of the view's back image (unscaled)....
float minViewHeight;
if(tv.getBackground()!=null)
{
minViewHeight = tv.getBackground().getIntrinsicHeight();
}
else
{
minViewHeight = 10f;//some min.
}
final float maxViewHeight = tv.getHeight() - (tv.getPaddingBottom()+tv.getPaddingTop())-12;// -12 just to be sure
final String s = tv.getText().toString();
//System.out.println(""+tv.getPaddingTop()+"/"+tv.getPaddingBottom());
if(minViewHeight >0 && maxViewHeight >2)
{
Rect currentBounds = new Rect();
tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
//System.out.println(""+initSize);
//System.out.println(""+maxViewHeight);
//System.out.println(""+(currentBounds.height()));
float resultingSize = 1;
while(currentBounds.height() < maxViewHeight)
{
resultingSize ++;
tv.setTextSize(resultingSize);
tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
//System.out.println(""+(currentBounds.height()+tv.getPaddingBottom()+tv.getPaddingTop()));
//System.out.println("Resulting: "+resultingSize);
}
if(currentBounds.height()>=maxViewHeight)
{
//just to be sure, reduce the value
tv.setTextSize(resultingSize-1);
}
}
}
Je pense que le problème réside dans l'utilisation de tv.getPaint().getTextBounds(...)
. Il renvoie toujours de petits nombres pour les limites du texte ... petit par rapport aux valeurs tv.getWidth()
et tv.getHeight()
... même si la taille du texte est beaucoup plus grande que la largeur ou la hauteur de la TextView
.
La bibliothèque AutofitTextView de MavenCentral s’occupe bien de cela. La source hébergée sur Github (1k + étoiles) à https://github.com/grantland/Android-autofittextview
Ajoutez ce qui suit à votre app/build.gradle
repositories {
mavenCentral()
}
dependencies {
compile 'me.grantland:autofittextview:0.2.+'
}
Activez n’importe quelle vue qui étend TextView dans le code:
AutofitHelper.create(textView);
Activez n’importe quelle vue qui étend TextView en XML:
<me.grantland.widget.AutofitLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
>
<Button
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:singleLine="true"
/>
</me.grantland.widget.AutofitLayout>
Utilisez le widget intégré en code ou XML:
<me.grantland.widget.AutofitTextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:singleLine="true"
/>
Nouveau depuis Android O:
https://developer.Android.com/preview/features/autosizing-textview.html
<TextView
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:autoSizeTextType="uniform"
Android:autoSizeMinTextSize="12sp"
Android:autoSizeMaxTextSize="100sp"
Android:autoSizeStepGranularity="2sp"
/>
Cela fait un certain temps que je joue à cela, essayant de corriger la taille de mes polices sur une grande variété de tablettes 7 "(Kindle Fire, Nexus7 et quelques-unes peu coûteuses en Chine avec des écrans basse résolution) et des périphériques.
L'approche qui a finalement fonctionné pour moi est la suivante. Le "32" est un facteur arbitraire qui donne environ 70 caractères sur une ligne horizontale de 7 ", qui correspond à la taille de la police que je recherchais. Réglez en conséquence.
textView.setTextSize(getFontSize(activity));
public static int getFontSize (Activity activity) {
DisplayMetrics dMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
// lets try to get them back a font size realtive to the pixel width of the screen
final float WIDE = activity.getResources().getDisplayMetrics().widthPixels;
int valueWide = (int)(WIDE / 32.0f / (dMetrics.scaledDensity));
return valueWide;
}
J'ai pu répondre à ma propre question en utilisant le code suivant (voir ci-dessous), mais ma solution était très spécifique à l'application. Par exemple, cela ne sera probablement bon et/ou ne fonctionnera que pour un TextView de taille env. La moitié de l'écran (avec également une marge supérieure de 40 pixels et des marges latérales de 20 pixels ... pas de marge inférieure).
En utilisant cette approche, vous pouvez créer votre propre implémentation similaire. La méthode statique se contente essentiellement de regarder le nombre de caractères et détermine un facteur d’échelle à appliquer à la taille du texte du TextView, puis augmente progressivement la taille du texte jusqu’à la hauteur totale (hauteur estimée - en utilisant la largeur du texte, le texte hauteur, et la largeur de TextView) est juste en dessous de celle de TextView. Les paramètres nécessaires pour déterminer le facteur d’échelle (c’est-à-dire les instructions if/else if) ont été définis par essai-et-vérification. Vous devrez probablement jouer avec les chiffres pour le faire fonctionner pour votre application particulière.
Ce n’est pas la solution la plus élégante, même s’il était facile de coder et que cela fonctionne pour moi. Est-ce que quelqu'un a une meilleure approche?
public static void autoScaleTextViewTextToHeight(final TextView tv, String s)
{
float currentWidth=tv.getPaint().measureText(s);
int scalingFactor = 0;
final int characters = s.length();
//scale based on # of characters in the string
if(characters<5)
{
scalingFactor = 1;
}
else if(characters>=5 && characters<10)
{
scalingFactor = 2;
}
else if(characters>=10 && characters<15)
{
scalingFactor = 3;
}
else if(characters>=15 && characters<20)
{
scalingFactor = 3;
}
else if(characters>=20 && characters<25)
{
scalingFactor = 3;
}
else if(characters>=25 && characters<30)
{
scalingFactor = 3;
}
else if(characters>=30 && characters<35)
{
scalingFactor = 3;
}
else if(characters>=35 && characters<40)
{
scalingFactor = 3;
}
else if(characters>=40 && characters<45)
{
scalingFactor = 3;
}
else if(characters>=45 && characters<50)
{
scalingFactor = 3;
}
else if(characters>=50 && characters<55)
{
scalingFactor = 3;
}
else if(characters>=55 && characters<60)
{
scalingFactor = 3;
}
else if(characters>=60 && characters<65)
{
scalingFactor = 3;
}
else if(characters>=65 && characters<70)
{
scalingFactor = 3;
}
else if(characters>=70 && characters<75)
{
scalingFactor = 3;
}
else if(characters>=75)
{
scalingFactor = 5;
}
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
//the +scalingFactor is important... increase this if nec. later
while((((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor)*tv.getTextSize())<tv.getHeight())
{
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, tv.getTextSize()+0.25f);
currentWidth=tv.getPaint().measureText(s);
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
}
tv.setText(s);
}
Merci.
Je suis tombé sur ça en cherchant moi-même une solution ... J'avais essayé toutes les autres solutions que je pouvais voir sur le dépassement de capacité de la pile, etc.
Fondamentalement, en encapsulant la vue du texte dans une présentation linéaire personnalisée, j'ai pu mesurer correctement le texte en veillant à ce qu'il soit mesuré avec une largeur fixe.
<!-- TextView wrapped in the custom LinearLayout that expects one child TextView -->
<!-- This view should specify the size you would want the text view to be displayed at -->
<com.custom.ResizeView
Android:layout_width="fill_parent"
Android:layout_height="0dp"
Android:layout_margin="10dp"
Android:layout_weight="1"
Android:orientation="horizontal" >
<TextView
Android:id="@+id/CustomTextView"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
</com.custom.ResizeView>
Puis le code de mise en page linéaire
public class ResizeView extends LinearLayout {
public ResizeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizeView(Context context) {
super(context);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// oldWidth used as a fixed width when measuring the size of the text
// view at different font sizes
final int oldWidth = getMeasuredWidth() - getPaddingBottom() - getPaddingTop();
final int oldHeight = getMeasuredHeight() - getPaddingLeft() - getPaddingRight();
// Assume we only have one child and it is the text view to scale
TextView textView = (TextView) getChildAt(0);
// This is the maximum font size... we iterate down from this
// I've specified the sizes in pixels, but sp can be used, just modify
// the call to setTextSize
float size = getResources().getDimensionPixelSize(R.dimen.solutions_view_max_font_size);
for (int textViewHeight = Integer.MAX_VALUE; textViewHeight > oldHeight; size -= 0.1f) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
// measure the text views size using a fixed width and an
// unspecified height - the unspecified height means measure
// returns the textviews ideal height
textView.measure(MeasureSpec.makeMeasureSpec(oldWidth, MeasureSpec.EXACTLY), MeasureSpec.UNSPECIFIED);
textViewHeight = textView.getMeasuredHeight();
}
}
}
J'espère que ça aide quelqu'un.
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 le remesurer jusqu’à ce que je trouve une taille de police qui convient. Vous pouvez voir la classe postée dans le sujet ci-dessous. J'espère que ça aide.
Le texte TextView à l'échelle automatique s'adapte aux limites
Une solution consisterait à spécifier différentes dimensions sp pour chacune des tailles d'écran généralisées. Par exemple, fournissez 8 ps pour les petits écrans, 12 ps pour les écrans normaux, 16 sp pour les grands et 20 sp pour les xlarge. Ensuite, il vous suffit de faire référence à @dimen text_size ou quoi que ce soit d'autre et vous pouvez être rassuré, car la densité est prise en charge via l'unité sp. Voir le lien suivant pour plus d'informations sur cette approche.
http://www.developer.Android.com/guide/topics/resources/more-resources.html#Dimension
Je dois toutefois noter que supporter plus de langues signifie plus de travail pendant la phase de test, en particulier si vous souhaitez conserver le texte sur une ligne, car certaines langues ont des mots beaucoup plus longs. Dans ce cas, créez un fichier dimens.xml dans le dossier values-de-large, par exemple, et ajustez la valeur manuellement. J'espère que cela t'aides.
essayez peut-être de définir setHoriztonallyScrolling () sur true avant de prendre des mesures de texte afin que textView n'essaye pas de mettre en forme votre texte sur plusieurs lignes
Voici une solution que j'ai créée à partir d'autres commentaires. Cette solution vous permet de définir la taille du texte en XML qui sera la taille maximale et s’ajustera à la hauteur de la vue. Réglage de la taille TextView
private float findNewTextSize(int width, int height, CharSequence text) {
TextPaint textPaint = new TextPaint(getPaint());
float targetTextSize = textPaint.getTextSize();
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
while(textHeight > height && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 1, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
}
return targetTextSize;
}
private int getTextHeight(CharSequence source, TextPaint Paint, int width, float textSize) {
Paint.setTextSize(textSize);
StaticLayout layout = new StaticLayout(source, Paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
return layout.getHeight();
}
Si votre seule exigence est que le texte soit automatiquement divisé et maintenu à la ligne suivante et que la hauteur ne soit pas importante, faites-le simplement comme ceci.
<TextView
Android:layout_height="wrap_content"
Android:layout_width="wrap_content"
Android:maxEms="integer"
Android:width="integer"/>
Cela aura pour effet que votre TextView retourne à son contenu verticalement en fonction de votre valeur maxEms.
Ceci est basé sur la réponse de mattmook. Cela a bien fonctionné sur certains appareils, mais pas sur tous. J'ai déplacé le redimensionnement vers l'étape de mesure, transformé la taille de police maximale en attribut personnalisé, pris en compte les marges et étendu FrameLayout au lieu de LineairLayout.
public class ResizeView extends FrameLayout {
protected float max_font_size;
public ResizeView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ResizeView,
0, 0);
max_font_size = a.getDimension(R.styleable.ResizeView_maxFontSize, 30.0f);
}
public ResizeView(Context context) {
super(context);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
// Use the parent's code for the first measure
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Assume we only have one child and it is the text view to scale
final TextView textView = (TextView) getChildAt(0);
// Check if the default measure resulted in a fitting textView
LayoutParams childLayout = (LayoutParams) textView.getLayoutParams();
final int textHeightAvailable = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - childLayout.topMargin - childLayout.bottomMargin;
int textViewHeight = textView.getMeasuredHeight();
if (textViewHeight < textHeightAvailable) {
return;
}
final int textWidthSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight() - childLayout.leftMargin - childLayout.rightMargin,
MeasureSpec.EXACTLY);
final int textHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
for (float size = max_font_size; size >= 1.05f; size-=0.1f) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
textView.measure(textWidthSpec, textHeightSpec);
textViewHeight = textView.getMeasuredHeight();
if (textViewHeight <= textHeightAvailable) {
break;
}
}
}
}
Et ceci dans attrs.xml:
<declare-styleable name="ResizeView">
<attr name="maxFontSize" format="reference|dimension"/>
</declare-styleable>
Et finalement utilisé comme ça:
<PACKAGE_NAME.ui.ResizeView xmlns:custom="http://schemas.Android.com/apk/res/PACKAGE_NAME"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:gravity="start|center_vertical"
Android:padding="5dp"
custom:maxFontSize="@dimen/normal_text">
<TextView Android:id="@+id/tabTitle2"
Android:layout_width="match_parent"
Android:layout_height="match_parent"/>
</PACKAGE_NAME.ui.ResizeView>
Vérifiez si ma solution vous aide:
Le texte TextView à l'échelle automatique s'adapte aux limites
Essaye ça...
tv.setText("Give a very large text anc check , this xample is very usefull");
countLine=tv.getLineHeight();
System.out.println("LineCount " + countLine);
if (countLine>=40){
tv.setTextSize(15);
}
J'ai trouvé que cela fonctionnait bien pour moi. voir: https://play.google.com/store/apps/details?id=au.id.rupert.chauffeurs_name_board&hl=fr
Code source à http://www.rupert.id.au/chauffeurs_name_board/verson2.php
http://catchthecows.com/?p=72 et https://github.com/catchthecows/BigTextButton