前言
不久之前,本菜雞遇到了一個需求缩功。android中文本展示需要兩邊對其的效果晴及。于是乎,我就想這種常用的屬性嫡锌,我們的textView居虑稼!然!沒势木!有动雹!無奈,只能吭哧吭哧的谷歌跟压,百度胰蝠。最終,尋得一方,來治我的蠢病茸塞。
正文
網(wǎng)上的各位大神為了實現(xiàn)兩邊對其躲庄,大家也算是腦洞大開,大致上為使用空格填充钾虐,或者將所有字符統(tǒng)一大小來實現(xiàn)兩邊對其噪窘。這種做法雖然很簡單,但是只能應(yīng)付一些單純的場景效扫。一旦場景復(fù)雜起來就會顯露弊端倔监。例如文字間距忽大忽小,句末文字沒有貼邊等一些問題菌仁。俗話說得好浩习,撿最貴的飲料瓶,裝最精致的逼济丘。這么粗糙的方法當(dāng)然不能忍受谱秽。那么有沒有方法來解決以上問題呢?當(dāng)然是有的:
兩邊對其原理
其實原理很簡單摹迷,首先先得到我們要顯示文本的文本框的寬度疟赊。隨后計算出一行最多能夠容納多少個文字,然后讀取文本峡碉,將文本繪制在TextView上近哟,在繪制之前計算出當(dāng)前行的長度減去本行最大容納文字的總長度,最后將這些長度均攤到這一行所有的文字間隔中鲫寄,這樣就可以達(dá)到吉执,收尾貼邊,并且整行文字間隔相等的狀態(tài)塔拳。詳細(xì)的內(nèi)容都寫在代碼注釋里鼠证,直接看代碼即可峡竣。
代碼
package com.flyme.systemuitools.gameassiant.gamemode.view;
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;
public class DroidAlignTextView extends TextView {
private int mLineY = 0;//總行高
private int mViewWidth;//TextView的總寬度
private TextPaint paint;
private boolean isEnter;
public DroidAlignTextView(Context context) {
super(context);
init();
}
public DroidAlignTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DroidAlignTextView(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) {
mViewWidth = getMeasuredWidth() - 5;//獲取textview的實際寬度
mLineY = 0;
mLineY += getTextSize();
String text = getText().toString();
Layout layout = getLayout();
int lineCount = layout.getLineCount();
for (int i = 0; i < lineCount; i++) {//每行循環(huán)
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
String lineText = text.substring(lineStart, lineEnd);//獲取TextView每行中的內(nèi)容
if (needScale(lineText)) {
if(isEnter){
lineStart += 1;
lineText = lineText.substring(1, 1 + lineText.length() - 1);
isEnter = false;
}
if (i == lineCount - 1) {//最后一行不需要重繪
canvas.drawText(lineText, 0, mLineY, paint);
} else {
float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, paint);
drawScaleText(canvas, lineText, width);
}
} else {
if (isEnter && !lineText.equals("\n")) {
lineText = lineText.substring(1, 1 + lineText.length() - 1);
isEnter = false;
}
if (lineText.charAt(lineText.length() - 1) == '\n') {
isEnter = true;
}
canvas.drawText(lineText, 0, mLineY, paint);
if(lineText.equals("\n")){
mLineY -= 8*3;
}
}
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(1, 1+2);
}
//比如說一共有5個字,中間有4個間隔适掰,
//那就用整個TextView的寬度 - 5個字的寬度颂碧,
//然后除以4,填補(bǔ)到這4個空隙中
float interval = (mViewWidth - lineWidth) / (lineText.length() - 1);
for (int i = 0; i < lineText.length(); ) {
String character = "";
if (!isEmojiCharacter(lineText.charAt(i))) {
// 是emoji表情
character = String.valueOf(lineText.substring(i, i += 2));
} else {
character = String.valueOf(lineText.charAt(i++));
}
float cw = StaticLayout.getDesiredWidth(character, paint);
canvas.drawText(character, x, mLineY, paint);
x += (cw + interval);
}
}
/**
* 判斷是不是段落的第一行.
* 一個漢字相當(dāng)于一個字符类浪,此處判斷是否為第一行的依據(jù)是:
* 字符長度大于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 || lineText.charAt(lineText.length() - 1) == '\n') {
return false;
} else {
return true;
}
}
/**
* 判斷是否是Emoji
*
* @param codePoint 比較的單個字符
* @return
*/
private boolean isEmojiCharacter(char codePoint) {
return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) ||
(codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000)
&& (codePoint <= 0x10FFFF));
}
}