转载:
Android自定义TextView实现文字两端能够对齐的TextView,文字右端能够对齐的TextView - 简书
原生支持(需要Android 8.0及以上)
在 Android 中,TextView
控件的 justificationMode
属性用于设置文本内容的对齐模式。这个属性可以控制文本内容在水平方向上的对齐方式,包括两种选项:
JUSTIFICATION_MODE_NONE
:表示不进行文本内容的两端对齐(Justification),即不会调整文本行的宽度以使其填充整行。JUSTIFICATION_MODE_INTER_WORD
:表示启用单词之间的两端对齐,这样会调整文本行的宽度,使得每一行的最后一个单词紧贴右边界。
<TextView
android:id="@+id/yourTextViewId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Your Text"
android:justificationMode="inter_word" />
// 获取 TextView 对象
val textView = findViewById<TextView>(R.id.yourTextViewId)
// 设置 justificationMode 为 JUSTIFICATION_MODE_INTER_WORD
textView.justificationMode = LineBreaker.JUSTIFICATION_MODE_INTER_WORD
LineBreaker.JUSTIFICATION_MODE_INTER_WORD 需要 Android 8.0(API 级别 26)及更高版本才支持。即 Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
自定义实现
此内容来源于:Android自定义TextView实现文字两端能够对齐的TextView,文字右端能够对齐的TextView - 简书
Android原生的TextView在显示文字时,右端可能会因为 不够一个文字 或者 标点符号 的问题而参差不齐。这里自定义一个JustifyTextView,解决右端不能对齐的问题。如下图所示,上方的TextView为自定义的,下方为原生的。
原版是 java 代码,因项目需要,这里贴上 kotlin 代码 。任选其一即可。
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.util.AttributeSet
import android.widget.TextView
@SuppressLint("AppCompatCustomView")
class JustifyTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : TextView(context, attrs, defStyleAttr) {
private var mLineY = 0.0f //总行高
//TextView的总宽度
private var mViewWidth = 0
private var paint: TextPaint
init {
paint = getPaint()
paint.setColor(currentTextColor)
paint.drawableState = drawableState
}
override fun onDraw(canvas: Canvas) {
mLineY = 0.0f
mViewWidth = measuredWidth //获取textview的实际宽度
mLineY += textSize.toInt()
val text = text.toString()
val layout: Layout = layout
val lineCount: Int = layout.lineCount
for (i in 0 until lineCount) { //每行循环
val lineStart: Int = layout.getLineStart(i)
val lineEnd: Int = layout.getLineEnd(i)
val lineText = text.substring(lineStart, lineEnd) //获取TextView每行中的内容
if (needScale(lineText)) {
if (i == lineCount - 1) { //最后一行不需要重绘
canvas.drawText(lineText, 0.0f, mLineY, paint)
} else {
val width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, paint)
drawScaleText(canvas, lineText, width)
}
} else {
canvas.drawText(lineText, 0.0f, mLineY, paint)
}
mLineY += lineHeight //写完一行以后,高度增加一行的高度
}
}
/**
* 重绘此行.
*
* @param canvas 画布
* @param lineText 该行所有的文字
* @param lineWidth 该行每个文字的宽度的总和
*/
private fun drawScaleText(canvas: Canvas, lineText: String, lineWidth: Float) {
var lineText = lineText
var x = 0f
if (isFirstLineOfParagraph(lineText)) {
val blanks = " "
canvas.drawText(blanks, x, mLineY, paint)
val width = StaticLayout.getDesiredWidth(blanks, paint)
x += width
lineText = lineText.substring(3)
}
//比如说一共有5个字,中间有4个间隔,
//那就用整个TextView的宽度 - 5个字的宽度,
//然后除以4,填补到这4个空隙中
val interval = (mViewWidth - lineWidth) / (lineText.length - 1)
for (i in 0 until lineText.length) {
val character = lineText[i].toString()
val cw = StaticLayout.getDesiredWidth(character, paint)
canvas.drawText(character, x, mLineY, paint)
x += cw + interval
}
}
/**
* 判断是不是段落的第一行.
* 一个汉字相当于一个字符,此处判断是否为第一行的依据是:
* 字符长度大于3且前两个字符为空格
*
* @param lineText 该行所有的文字
*/
private fun isFirstLineOfParagraph(lineText: String): Boolean {
return lineText.length > 3 && lineText[0] == ' ' && lineText[1] == ' '
}
/**
* 判断需不需要缩放.
*
* @param lineText 该行所有的文字
* @return true 该行最后一个字符不是换行符 false 该行最后一个字符是换行符
*/
private fun needScale(lineText: String): Boolean {
return if (lineText.length == 0) {
false
} else {
lineText[lineText.length - 1] != '\n'
}
}
}
java 代码:
import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* Android自定义TextView实现文字两端能够对齐的TextView,文字右端能够对齐的TextView
* http://blog.csdn.net/kikitious_du/article/details/50561927
*/
public class JustifyTextView extends TextView {
private int mLineY = 0;//总行高
private int mViewWidth;//TextView的总宽度
private TextPaint paint;
public JustifyTextView(Context context) {
super(context);
init();
}
public JustifyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public JustifyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.drawableState = getDrawableState();
}
@Override
protected void onDraw(Canvas canvas) {
mLineY = 0;
mViewWidth = getMeasuredWidth();//获取textview的实际宽度
mLineY += getTextSize();
String text = getText().toString();
Layout layout = getLayout();
int lineCount = layout.getLineCount();
for (int i = 0; i < lineCount; i++) {//每行循环
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
String lineText = text.substring(lineStart, lineEnd);//获取TextView每行中的内容
if (needScale(lineText)) {
if (i == lineCount - 1) {//最后一行不需要重绘
canvas.drawText(lineText, 0, mLineY, paint);
} else {
float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, paint);
drawScaleText(canvas, lineText, width);
}
} else {
canvas.drawText(lineText, 0, mLineY, paint);
}
mLineY += getLineHeight();//写完一行以后,高度增加一行的高度
}
}
/**
* 重绘此行.
*
* @param canvas 画布
* @param lineText 该行所有的文字
* @param lineWidth 该行每个文字的宽度的总和
*/
private void drawScaleText(Canvas canvas, String lineText, float lineWidth) {
float x = 0;
if (isFirstLineOfParagraph(lineText)) {
String blanks = " ";
canvas.drawText(blanks, x, mLineY, paint);
float width = StaticLayout.getDesiredWidth(blanks, paint);
x += width;
lineText = lineText.substring(3);
}
//比如说一共有5个字,中间有4个间隔,
//那就用整个TextView的宽度 - 5个字的宽度,
//然后除以4,填补到这4个空隙中
float interval = (mViewWidth - lineWidth) / (lineText.length() - 1);
for (int i = 0; i < lineText.length(); i++) {
String character = String.valueOf(lineText.charAt(i));
float cw = StaticLayout.getDesiredWidth(character, paint);
canvas.drawText(character, x, mLineY, paint);
x += (cw + interval);
}
}
/**
* 判断是不是段落的第一行.
* 一个汉字相当于一个字符,此处判断是否为第一行的依据是:
* 字符长度大于3且前两个字符为空格
*
* @param lineText 该行所有的文字
*/
private boolean isFirstLineOfParagraph(String lineText) {
return lineText.length() > 3 && lineText.charAt(0) == ' ' && lineText.charAt(1) == ' ';
}
/**
* 判断需不需要缩放.
*
* @param lineText 该行所有的文字
* @return true 该行最后一个字符不是换行符 false 该行最后一个字符是换行符
*/
private boolean needScale(String lineText) {
if (lineText.length() == 0) {
return false;
} else {
return lineText.charAt(lineText.length() - 1) != '\n';
}
}
}