Android TextView Justifica el texto

¿Cómo se justifica el texto de un TextView (con texto alineado a la izquierda y a la derecha)?

Encontré una posible solución aquí , pero no funciona (incluso si cambia vertical-center a center_vertical, etc.).

No creo que Android admita una justificación completa.

ACTUALIZACIÓN 2018-01-01 : Android 8.0+ admite modos de justificación con TextView .

La respuesta de @CommonsWare es correcta. Android no admite “Justificación completa” (o simplemente “Justificación”, ya que a veces se hace referencia ambigua).

Sin embargo, Android admite “alineación de texto a la izquierda / derecha”. Vea el artículo de la wikipedia sobre Justificación para la distinción. Muchas personas consideran que el concepto de ‘justificación’ abarca la justificación completa, así como la alineación de texto izquierda / derecha, que es lo que terminan buscando cuando quieren alinear el texto a la izquierda / derecha. Esta respuesta explica cómo lograr la alineación de texto izquierda / derecha.

Es posible lograr la alineación de texto a ras de izquierda / derecha (a diferencia de la justificación completa, ya que la pregunta es acerca de). Para demostrar, usaré un formulario básico de 2 columnas (tags en la columna de la izquierda y campos de texto en la columna de la derecha) como ejemplo. En este ejemplo, el texto de las tags de la columna izquierda se alineará a la derecha para que aparezcan al ras contra sus campos de texto en la columna de la derecha.

En el diseño XML, puede obtener los elementos TextView en sí (la columna izquierda) para alinearlos a la derecha agregando el siguiente atributo dentro de todos los TextViews:

  ...  

Sin embargo, si el texto se ajusta a varias líneas, el texto aún se alineará a la izquierda dentro de TextView. Agregar el siguiente atributo hace que el texto real se alinee a la derecha alineado (rasgado a la izquierda) dentro de TextView:

  ...  

Entonces, el atributo de gravedad especifica cómo alinear el texto dentro de TextView layout_gravity especifica cómo alinear / diseñar el elemento TextView.

Para justificar el texto en Android, utilicé WebView

  setContentView(R.layout.main); WebView view = new WebView(this); view.setVerticalScrollBarEnabled(false); ((LinearLayout)findViewById(R.id.inset_web_view)).addView(view); view.loadData(getString(R.string.hello), "text/html; charset=utf-8", "utf-8"); 

y html.

  < ![CDATA[    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pellentesque, urna nec hendrerit pellentesque, risus massa   ]]>  

Todavía no puedo subir imágenes para probarlo pero “funciona para mí”.

ACTUALIZADO

Hemos creado una clase simple para esto. Actualmente hay dos métodos para lograr lo que estás buscando. Ambos requieren NINGUNA WEBVIEW y SOPORTABLE SPABLES .

BIBLIOTECA : https://github.com/bluejamesbond/TextJustify-Android

SOPORTES : Android 2.0 a 5.X

PREPARAR

 // Please visit Github for latest setup instructions. 

CAPTURA DE PANTALLA

Comparison.png

Puede usar el proyecto JustifiedTextView para Android en github. esta es una vista personalizada que simula texto justificado para usted. Es compatible con Android 2.0+ y de derecha a izquierda. enter image description here

TextView en Android O ofrece una justificación completa (nueva alineación tipográfica).

Solo necesitas hacer esto,

 textView.setJustificationMode(JUSTIFICATION_MODE_INTER_WORD); 

el valor predeterminado es JUSTIFICATION_MODE_NONE .

Escribo una base de widgets en textview nativo para hacerlo.

github

Encontré una manera de resolver este problema, pero puede que no sea muy elegante, pero el efecto no es malo.

Su principio es reemplazar los espacios de cada línea con el ImageSpan de ancho fijo (el color es transparente).

 public static void justify(final TextView textView) { final AtomicBoolean isJustify = new AtomicBoolean(false); final String textString = textView.getText().toString(); final TextPaint textPaint = textView.getPaint(); final SpannableStringBuilder builder = new SpannableStringBuilder(); textView.post(new Runnable() { @Override public void run() { if (!isJustify.get()) { final int lineCount = textView.getLineCount(); final int textViewWidth = textView.getWidth(); for (int i = 0; i < lineCount; i++) { int lineStart = textView.getLayout().getLineStart(i); int lineEnd = textView.getLayout().getLineEnd(i); String lineString = textString.substring(lineStart, lineEnd); if (i == lineCount - 1) { builder.append(new SpannableString(lineString)); break; } String trimSpaceText = lineString.trim(); String removeSpaceText = lineString.replaceAll(" ", ""); float removeSpaceWidth = textPaint.measureText(removeSpaceText); float spaceCount = trimSpaceText.length() - removeSpaceText.length(); float eachSpaceWidth = (textViewWidth - removeSpaceWidth) / spaceCount; SpannableString spannableString = new SpannableString(lineString); for (int j = 0; j < trimSpaceText.length(); j++) { char c = trimSpaceText.charAt(j); if (c == ' ') { Drawable drawable = new ColorDrawable(0x00ffffff); drawable.setBounds(0, 0, (int) eachSpaceWidth, 0); ImageSpan span = new ImageSpan(drawable); spannableString.setSpan(span, j, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } builder.append(spannableString); } textView.setText(builder); isJustify.set(true); } } }); } 

Puse el código en GitHub: https://github.com/twiceyuan/TextJustification

Visión de conjunto:

Visión de conjunto

Diseño XML: declara WebView en lugar de TextView

  

Código Java: establecer datos de texto en WebView

 WebView view = (WebView) findViewById(R.id.textContent); String text; text = "

"; text+= "This is the text will be justified when displayed!!!"; text+= "

"; view.loadData(text, "text/html", "utf-8");

Esto puede resolver su problema. Funcionó completamente para mí.

Así es como lo hice, creo que de la manera más elegante que pude. Con esta solución, las únicas cosas que debe hacer en sus diseños son:

  • agregar una statement xmlns adicional
  • cambie el espacio de nombres de texto fuente de su TextView desde android a su nuevo espacio de nombres
  • reemplace sus TextView con xyzJustifiedTextView

Aquí está el código. Funciona perfectamente bien en mis teléfonos (Galaxy Nexus Android 4.0.2, Galaxy Teos Android 2.1). Siéntete libre, por supuesto, para reemplazar mi nombre de paquete con el tuyo.

/assets/justified_textview.css :

 body { font-size: 1.0em; color: rgb(180,180,180); text-align: justify; } @media screen and (-webkit-device-pixel-ratio: 1.5) { /* CSS for high-density screens */ body { font-size: 1.05em; } } @media screen and (-webkit-device-pixel-ratio: 2.0) { /* CSS for extra high-density screens */ body { font-size: 1.1em; } } 

/res/values/attrs.xml :

 < ?xml version="1.0" encoding="utf-8"?>      

/res/layout/test.xml :

 < ?xml version="1.0" encoding="utf-8"?>      

/src/net/bicou/myapp/widget/JustifiedTextView.java :

 package net.bicou.myapp.widget; import net.bicou.myapp.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.webkit.WebView; public class JustifiedTextView extends WebView { public JustifiedTextView(final Context context) { this(context, null, 0); } public JustifiedTextView(final Context context, final AttributeSet attrs) { this(context, attrs, 0); } public JustifiedTextView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); if (attrs != null) { final TypedValue tv = new TypedValue(); final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.JustifiedTextView, defStyle, 0); if (ta != null) { ta.getValue(R.styleable.JustifiedTextView_text, tv); if (tv.resourceId > 0) { final String text = context.getString(tv.resourceId).replace("\n", "
"); loadDataWithBaseURL("file:///android_asset/", "" + "" + "" + text + "", "text/html", "UTF8", null); setTransparentBackground(); } } } } public void setTransparentBackground() { try { setLayerType(View.LAYER_TYPE_SOFTWARE, null); } catch (final NoSuchMethodError e) { } setBackgroundColor(Color.TRANSPARENT); setBackgroundDrawable(null); setBackgroundResource(0); } }

Necesitamos configurar el renderizado en el software para obtener un fondo transparente en Android 3+. De ahí el try-catch para versiones anteriores de Android.

¡Espero que esto ayude!

PD: por favor no sea útil agregar esto a toda tu actividad en Android 3+ para obtener el comportamiento esperado:
android:hardwareAccelerated="false"

Si bien aún no se ha completado el texto justificado, ahora puede equilibrar las longitudes de línea con android:breakStrategy="balanced" desde API 23 en adelante

http://developer.android.com/reference/android/widget/TextView.html#attr_android:breakStrategy

FILL_HORIZONTAL es equivalente a CENTER_HORIZONTAL . Puede ver este fragmento de código en el código fuente de textview:

 case Gravity.CENTER_HORIZONTAL: case Gravity.FILL_HORIZONTAL: return (mLayout.getLineWidth(0) - ((mRight - mLeft) - getCompoundPaddingLeft() - getCompoundPaddingRight())) / getHorizontalFadingEdgeLength(); 

Escribo mi propia clase para resolver este problema. Aquí solo tienes que llamar a la función de justificación estática que toma dos argumentos

  1. Objeto Text View
  2. Ancho del contenido (ancho total de su vista de texto)

//Actividad principal

 package com.fawad.textjustification; import android.app.Activity; import android.database.Cursor; import android.graphics.Point; import android.graphics.Typeface; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.Display; import android.view.Gravity; import android.view.Menu; import android.widget.TextView; public class MainActivity extends Activity { static Point size; static float density; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Display display = getWindowManager().getDefaultDisplay(); size=new Point(); DisplayMetrics dm=new DisplayMetrics(); display.getMetrics(dm); density=dm.density; display.getSize(size); TextView tv=(TextView)findViewById(R.id.textView1); Typeface typeface=Typeface.createFromAsset(this.getAssets(), "Roboto-Medium.ttf"); tv.setTypeface(typeface); tv.setLineSpacing(0f, 1.2f); tv.setTextSize(10*MainActivity.density); //some random long text String myText=getResources().getString(R.string.my_text); tv.setText(myText); TextJustification.justify(tv,size.x); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } } 

// TextJustificationClass

 package com.fawad.textjustification; import java.util.ArrayList; import android.graphics.Paint; import android.text.TextUtils; import android.widget.TextView; public class TextJustification { public static void justify(TextView textView,float contentWidth) { String text=textView.getText().toString(); Paint paint=textView.getPaint(); ArrayList lineList=lineBreak(text,paint,contentWidth); textView.setText(TextUtils.join(" ", lineList).replaceFirst("\\s", "")); } private static ArrayList lineBreak(String text,Paint paint,float contentWidth){ String [] wordArray=text.split("\\s"); ArrayList lineList = new ArrayList(); String myText=""; for(String word:wordArray){ if(paint.measureText(myText+" "+word)< =contentWidth) myText=myText+" "+word; else{ int totalSpacesToInsert=(int)((contentWidth-paint.measureText(myText))/paint.measureText(" ")); lineList.add(justifyLine(myText,totalSpacesToInsert)); myText=word; } } lineList.add(myText); return lineList; } private static String justifyLine(String text,int totalSpacesToInsert){ String[] wordArray=text.split("\\s"); String toAppend=" "; while((totalSpacesToInsert)>=(wordArray.length-1)){ toAppend=toAppend+" "; totalSpacesToInsert=totalSpacesToInsert-(wordArray.length-1); } int i=0; String justifiedText=""; for(String word:wordArray){ if(i 

// XML

         

Creo que hay dos opciones:

  • Usa algo como Pango que se especializa en esto a través del NDK y renderiza texto en un OpenGL u otra superficie.

  • Use Paint.measureText () y sus amigos para obtener las longitudes de las palabras y disponerlas manualmente en un canvas en una vista personalizada.

Hay un CustomView para este problema, esta vista de texto personalizada es compatible con la Vista de texto justificada.

Loot en esto: JustifiedTextView

 import java.util.ArrayList; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.text.TextPaint; import android.view.View; public class JustifiedTextView extends View { String text; ArrayList linesCollection = new ArrayList(); TextPaint textPaint; Typeface font; int textColor; float textSize = 42f, lineHeight = 57f, wordSpacing = 15f, lineSpacing = 15f; float onBirim, w, h; float leftPadding, rightPadding; public JustifiedTextView(Context context, String text) { super(context); this.text = text; init(); } private void init() { textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textColor = Color.BLACK; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (font != null) { font = Typeface.createFromAsset(getContext().getAssets(), "font/Trykker-Regular.ttf"); textPaint.setTypeface(font); } textPaint.setColor(textColor); int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); w = resolveSizeAndState(minw, widthMeasureSpec, 1); h = MeasureSpec.getSize(widthMeasureSpec); onBirim = 0.009259259f * w; lineHeight = textSize + lineSpacing; leftPadding = 3 * onBirim + getPaddingLeft(); rightPadding = 3 * onBirim + getPaddingRight(); textPaint.setTextSize(textSize); wordSpacing = 15f; Line lineBuffer = new Line(); this.linesCollection.clear(); String[] lines = text.split("\n"); for (String line : lines) { String[] words = line.split(" "); lineBuffer = new Line(); float lineWidth = leftPadding + rightPadding; float totalWordWidth = 0; for (String word : words) { float ww = textPaint.measureText(word) + wordSpacing; if (lineWidth + ww + (lineBuffer.getWords().size() * wordSpacing) > w) {// is lineBuffer.addWord(word); totalWordWidth += textPaint.measureText(word); lineBuffer.setSpacing((w - totalWordWidth - leftPadding - rightPadding) / (lineBuffer.getWords().size() - 1)); this.linesCollection.add(lineBuffer); lineBuffer = new Line(); totalWordWidth = 0; lineWidth = leftPadding + rightPadding; } else { lineBuffer.setSpacing(wordSpacing); lineBuffer.addWord(word); totalWordWidth += textPaint.measureText(word); lineWidth += ww; } } this.linesCollection.add(lineBuffer); } setMeasuredDimension((int) w, (int) ((this.linesCollection.size() + 1) * lineHeight + (10 * onBirim))); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawLine(0f, 10f, getMeasuredWidth(), 10f, textPaint); float x, y = lineHeight + onBirim; for (Line line : linesCollection) { x = leftPadding; for (String s : line.getWords()) { canvas.drawText(s, x, y, textPaint); x += textPaint.measureText(s) + line.spacing; } y += lineHeight; } } public String getText() { return text; } public void setText(String text) { this.text = text; } public Typeface getFont() { return font; } public void setFont(Typeface font) { this.font = font; } public float getLineHeight() { return lineHeight; } public void setLineHeight(float lineHeight) { this.lineHeight = lineHeight; } public float getLeftPadding() { return leftPadding; } public void setLeftPadding(float leftPadding) { this.leftPadding = leftPadding; } public float getRightPadding() { return rightPadding; } public void setRightPadding(float rightPadding) { this.rightPadding = rightPadding; } public void setWordSpacing(float wordSpacing) { this.wordSpacing = wordSpacing; } public float getWordSpacing() { return wordSpacing; } public float getLineSpacing() { return lineSpacing; } public void setLineSpacing(float lineSpacing) { this.lineSpacing = lineSpacing; } class Line { ArrayList words = new ArrayList(); float spacing = 15f; public Line() { } public Line(ArrayList words, float spacing) { this.words = words; this.spacing = spacing; } public void setSpacing(float spacing) { this.spacing = spacing; } public float getSpacing() { return spacing; } public void addWord(String s) { words.add(s); } public ArrayList getWords() { return words; } } } 

Agregue la clase anterior a su carpeta src y use este código de muestra para agregar a su diseño:

 JustifiedTextView jtv= new JustifiedTextView(getApplicationContext(), "Lorem ipsum dolor sit amet... "); LinearLayout place = (LinearLayout) findViewById(R.id.book_profile_content); place.addView(jtv); 

Pruebe esta solución en el enlace a continuación solo cree esa clase en la carpeta del proyecto y úsela. Funciona bien para mí 🙂

¿Justifica el texto en una aplicación de Android usando un WebView pero presentando una interfaz tipo TextView?

En android, justificar el texto a la izquierda y no truncar el color de fondo, prueba esto, funcionó para mí, produciendo resultados consistentes en android, ff, ie, y cromo, pero tienes que medir el espacio que queda entremedio para el texto al calcular el relleno.

 
		      	

Para formatear html no necesita llamar al Webkit, puede usar Html.fromHtml(text) para hacer el trabajo.

Fuente: http://developer.android.com/guide/topics/resources/string-resource.html

Android aún no admite una justificación completa. Podemos usar Webview y justificar HTML en lugar de usar textview. Funciona tan bien. Si no están claros, no duden en preguntarme 🙂

mira aquí en el github

Solo importe los dos archivos “TextJustifyUtils.java” y “TextViewEx.java” en su proyecto.

 public class TextJustifyUtils { // Please use run(...) instead public static void justify(TextView textView) { Paint paint = new Paint(); String[] blocks; float spaceOffset = 0; float textWrapWidth = 0; int spacesToSpread; float wrappedEdgeSpace; String block; String[] lineAsWords; String wrappedLine; String smb = ""; Object[] wrappedObj; // Pull widget properties paint.setColor(textView.getCurrentTextColor()); paint.setTypeface(textView.getTypeface()); paint.setTextSize(textView.getTextSize()); textWrapWidth = textView.getWidth(); spaceOffset = paint.measureText(" "); blocks = textView.getText().toString().split("((?< =\n)|(?=\n))"); if (textWrapWidth < 20) { return; } for (int i = 0; i < blocks.length; i++) { block = blocks[i]; if (block.length() == 0) { continue; } else if (block.equals("\n")) { smb += block; continue; } block = block.trim(); if (block.length() == 0) continue; wrappedObj = TextJustifyUtils.createWrappedLine(block, paint, spaceOffset, textWrapWidth); wrappedLine = ((String) wrappedObj[0]); wrappedEdgeSpace = (Float) wrappedObj[1]; lineAsWords = wrappedLine.split(" "); spacesToSpread = (int) (wrappedEdgeSpace != Float.MIN_VALUE ? wrappedEdgeSpace / spaceOffset : 0); for (String word : lineAsWords) { smb += word + " "; if (--spacesToSpread > 0) { smb += " "; } } smb = smb.trim(); if (blocks[i].length() > 0) { blocks[i] = blocks[i].substring(wrappedLine.length()); if (blocks[i].length() > 0) { smb += "\n"; } i--; } } textView.setGravity(Gravity.LEFT); textView.setText(smb); } protected static Object[] createWrappedLine(String block, Paint paint, float spaceOffset, float maxWidth) { float cacheWidth = maxWidth; float origMaxWidth = maxWidth; String line = ""; for (String word : block.split("\\s")) { cacheWidth = paint.measureText(word); maxWidth -= cacheWidth; if (maxWidth < = 0) { return new Object[] { line, maxWidth + cacheWidth + spaceOffset }; } line += word + " "; maxWidth -= spaceOffset; } if (paint.measureText(block) <= origMaxWidth) { return new Object[] { block, Float.MIN_VALUE }; } return new Object[] { line, maxWidth }; } final static String SYSTEM_NEWLINE = "\n"; final static float COMPLEXITY = 5.12f; // Reducing this will increase // efficiency but will decrease // effectiveness final static Paint p = new Paint(); public static void run(final TextView tv, float origWidth) { String s = tv.getText().toString(); p.setTypeface(tv.getTypeface()); String[] splits = s.split(SYSTEM_NEWLINE); float width = origWidth - 5; for (int x = 0; x < splits.length; x++) if (p.measureText(splits[x]) > width) { splits[x] = wrap(splits[x], width, p); String[] microSplits = splits[x].split(SYSTEM_NEWLINE); for (int y = 0; y < microSplits.length - 1; y++) microSplits[y] = justify(removeLast(microSplits[y], " "), width, p); StringBuilder smb_internal = new StringBuilder(); for (int z = 0; z < microSplits.length; z++) smb_internal.append(microSplits[z] + ((z + 1 < microSplits.length) ? SYSTEM_NEWLINE : "")); splits[x] = smb_internal.toString(); } final StringBuilder smb = new StringBuilder(); for (String cleaned : splits) smb.append(cleaned + SYSTEM_NEWLINE); tv.setGravity(Gravity.LEFT); tv.setText(smb); } private static String wrap(String s, float width, Paint p) { String[] str = s.split("\\s"); // regex StringBuilder smb = new StringBuilder(); // save memory smb.append(SYSTEM_NEWLINE); for (int x = 0; x < str.length; x++) { float length = p.measureText(str[x]); String[] pieces = smb.toString().split(SYSTEM_NEWLINE); try { if (p.measureText(pieces[pieces.length - 1]) + length > width) smb.append(SYSTEM_NEWLINE); } catch (Exception e) { } smb.append(str[x] + " "); } return smb.toString().replaceFirst(SYSTEM_NEWLINE, ""); } private static String removeLast(String s, String g) { if (s.contains(g)) { int index = s.lastIndexOf(g); int indexEnd = index + g.length(); if (index == 0) return s.substring(1); else if (index == s.length() - 1) return s.substring(0, index); else return s.substring(0, index) + s.substring(indexEnd); } return s; } private static String justifyOperation(String s, float width, Paint p) { float holder = (float) (COMPLEXITY * Math.random()); while (s.contains(Float.toString(holder))) holder = (float) (COMPLEXITY * Math.random()); String holder_string = Float.toString(holder); float lessThan = width; int timeOut = 100; int current = 0; while (p.measureText(s) < lessThan && current < timeOut) { s = s.replaceFirst(" ([^" + holder_string + "])", " " + holder_string + "$1"); lessThan = p.measureText(holder_string) + lessThan - p.measureText(" "); current++; } String cleaned = s.replaceAll(holder_string, " "); return cleaned; } private static String justify(String s, float width, Paint p) { while (p.measureText(s) < width) { s = justifyOperation(s, width, p); } return s; } } 

y

 public class TextViewEx extends TextView { private Paint paint = new Paint(); private String[] blocks; private float spaceOffset = 0; private float horizontalOffset = 0; private float verticalOffset = 0; private float horizontalFontOffset = 0; private float dirtyRegionWidth = 0; private boolean wrapEnabled = false; int left, top, right, bottom = 0; private Align _align = Align.LEFT; private float strecthOffset; private float wrappedEdgeSpace; private String block; private String wrappedLine; private String[] lineAsWords; private Object[] wrappedObj; private Bitmap cache = null; private boolean cacheEnabled = false; public TextViewEx(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // set a minimum of left and right padding so that the texts are not too // close to the side screen // this.setPadding(10, 0, 10, 0); } public TextViewEx(Context context, AttributeSet attrs) { super(context, attrs); // this.setPadding(10, 0, 10, 0); } public TextViewEx(Context context) { super(context); // this.setPadding(10, 0, 10, 0); } @Override public void setPadding(int left, int top, int right, int bottom) { // TODO Auto-generated method stub super.setPadding(left + 10, top, right + 10, bottom); } @Override public void setDrawingCacheEnabled(boolean cacheEnabled) { this.cacheEnabled = cacheEnabled; } public void setText(String st, boolean wrap) { wrapEnabled = wrap; super.setText(st); } public void setTextAlign(Align align) { _align = align; } @SuppressLint("NewApi") @Override protected void onDraw(Canvas canvas) { // If wrap is disabled then, // request original onDraw if (!wrapEnabled) { super.onDraw(canvas); return; } // Active canas needs to be set // based on cacheEnabled Canvas activeCanvas = null; // Set the active canvas based on // whether cache is enabled if (cacheEnabled) { if (cache != null) { // Draw to the OS provided canvas // if the cache is not empty canvas.drawBitmap(cache, 0, 0, paint); return; } else { // Create a bitmap and set the activeCanvas // to the one derived from the bitmap cache = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_4444); activeCanvas = new Canvas(cache); } } else { // Active canvas is the OS // provided canvas activeCanvas = canvas; } // Pull widget properties paint.setColor(getCurrentTextColor()); paint.setTypeface(getTypeface()); paint.setTextSize(getTextSize()); paint.setTextAlign(_align); paint.setFlags(Paint.ANTI_ALIAS_FLAG); // minus out the paddings pixel dirtyRegionWidth = getWidth() - getPaddingLeft() - getPaddingRight(); int maxLines = Integer.MAX_VALUE; int currentapiVersion = android.os.Build.VERSION.SDK_INT; if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) { maxLines = getMaxLines(); } int lines = 1; blocks = getText().toString().split("((?< =\n)|(?=\n))"); verticalOffset = horizontalFontOffset = getLineHeight() - 0.5f; // Temp // fix spaceOffset = paint.measureText(" "); for (int i = 0; i < blocks.length && lines <= maxLines; i++) { block = blocks[i]; horizontalOffset = 0; if (block.length() == 0) { continue; } else if (block.equals("\n")) { verticalOffset += horizontalFontOffset; continue; } block = block.trim(); if (block.length() == 0) { continue; } wrappedObj = TextJustifyUtils.createWrappedLine(block, paint, spaceOffset, dirtyRegionWidth); wrappedLine = ((String) wrappedObj[0]); wrappedEdgeSpace = (Float) wrappedObj[1]; lineAsWords = wrappedLine.split(" "); strecthOffset = wrappedEdgeSpace != Float.MIN_VALUE ? wrappedEdgeSpace / (lineAsWords.length - 1) : 0; for (int j = 0; j < lineAsWords.length; j++) { String word = lineAsWords[j]; if (lines == maxLines && j == lineAsWords.length - 1) { activeCanvas.drawText("...", horizontalOffset, verticalOffset, paint); } else if (j == 0) { // if it is the first word of the line, text will be drawn // starting from right edge of textview if (_align == Align.RIGHT) { activeCanvas.drawText(word, getWidth() - (getPaddingRight()), verticalOffset, paint); // add in the paddings to the horizontalOffset horizontalOffset += getWidth() - (getPaddingRight()); } else { activeCanvas.drawText(word, getPaddingLeft(), verticalOffset, paint); horizontalOffset += getPaddingLeft(); } } else { activeCanvas.drawText(word, horizontalOffset, verticalOffset, paint); } if (_align == Align.RIGHT) horizontalOffset -= paint.measureText(word) + spaceOffset + strecthOffset; else horizontalOffset += paint.measureText(word) + spaceOffset + strecthOffset; } lines++; if (blocks[i].length() > 0) { blocks[i] = blocks[i].substring(wrappedLine.length()); verticalOffset += blocks[i].length() > 0 ? horizontalFontOffset : 0; i--; } } if (cacheEnabled) { // Draw the cache onto the OS provided // canvas. canvas.drawBitmap(cache, 0, 0, paint); } } } 

Now, if you use normal textView like:

  

Simply use

  

Define a variable and set justify to be true,

 TextViewEx changed = (TextViewEx) findViewById(R.id.changed); changed.setText(getResources().getString(R.string.lorum_ipsum),true); 

You have to set

 android:layout_height="wrap_content" 

y

 android:layout_centerInParent="true" 

Try using < RelativeLayout > (making sure to fill_parent), then just add android:layout_alignParentLeft="true" and

android:layout_alignParentRight="true" to the elements you would like on the outside LEFT & RIGHT.

BLAM, justified!

This doesn’t really justify your text but

 android:gravity="center_horizontal" 

is the best choice you have.