在res文件加下添加attrs.xml文件,用于设置styleable
<?xml version="1.0" encoding="utf-8"?>
<resources >
<declare-styleable name ="AlignTextView" >
<attr name ="align" format ="enum" >
<enum name ="left" value ="0" />
<enum name ="center" value ="1" />
<enum name ="right" value ="2" />
</attr >
</declare-styleable >
</resources >
自定义AlignTextView.java类,继承自TextView
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* 两端对齐的text view,可以设置最后一行靠左,靠右,居中对齐
*
* @author YD
*/
public class AlignTextView extends TextView {
private float textHeight;
private float textLineSpaceExtra = 0 ;
private int width;
private List<String> lines = new ArrayList<String>();
private List<Integer> tailLines = new ArrayList<Integer>();
private Align align = Align.ALIGN_LEFT;
private boolean firstCalc = true ;
private float lineSpacingMultiplier = 1.0 f;
private float lineSpacingAdd = 0.0 f;
private int originalHeight = 0 ;
private int originalLineCount = 0 ;
private int originalPaddingBottom = 0 ;
private boolean setPaddingFromMe = false ;
public enum Align {
ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT
}
public AlignTextView (Context context) {
super (context);
setTextIsSelectable(false );
}
public AlignTextView (Context context, AttributeSet attrs) {
super (context, attrs);
setTextIsSelectable(false );
lineSpacingMultiplier = attrs.getAttributeFloatValue("http://schemas.android" + "" +
".com/apk/res/android" , "lineSpacingMultiplier" , 1.0 f);
int [] attributes = new int []{android.R.attr.lineSpacingExtra};
TypedArray arr = context.obtainStyledAttributes(attrs, attributes);
lineSpacingAdd = arr.getDimensionPixelSize(0 , 0 );
originalPaddingBottom = getPaddingBottom();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.AlignTextView);
int alignStyle = ta.getInt(R.styleable.AlignTextView_align, 0 );
switch (alignStyle) {
case 1 :
align = Align.ALIGN_CENTER;
break ;
case 2 :
align = Align.ALIGN_RIGHT;
break ;
default :
align = Align.ALIGN_LEFT;
break ;
}
ta.recycle();
}
@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
super .onLayout(changed, left, top, right, bottom);
if (firstCalc) {
width = getMeasuredWidth();
String text = getText().toString();
TextPaint paint = getPaint();
lines.clear();
tailLines.clear();
String[] items = text.split("\\n" );
for (String item : items) {
calc(paint, item);
}
measureTextViewHeight(text, paint.getTextSize(), getMeasuredWidth() -
getPaddingLeft() - getPaddingRight());
textHeight = 1.0 f * originalHeight / originalLineCount;
textLineSpaceExtra = textHeight * (lineSpacingMultiplier - 1 ) + lineSpacingAdd;
int heightGap = (int ) ((textLineSpaceExtra + textHeight) * (lines.size() -
originalLineCount));
setPaddingFromMe = true ;
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
originalPaddingBottom + heightGap);
firstCalc = false ;
}
}
@Override
protected void onDraw (Canvas canvas) {
TextPaint paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.drawableState = getDrawableState();
width = getMeasuredWidth();
Paint.FontMetrics fm = paint.getFontMetrics();
float firstHeight = getTextSize() - (fm.bottom - fm.descent + fm.ascent - fm.top);
int gravity = getGravity();
if ((gravity & 0x1000 ) == 0 ) {
firstHeight = firstHeight + (textHeight - firstHeight) / 2 ;
}
int paddingTop = getPaddingTop();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
width = width - paddingLeft - paddingRight;
for (int i = 0 ; i < lines.size(); i++) {
float drawY = i * textHeight + firstHeight;
String line = lines.get(i);
float drawSpacingX = paddingLeft;
float gap = (width - paint.measureText(line));
float interval = gap / (line.length() - 1 );
if (tailLines.contains(i)) {
interval = 0 ;
if (align == Align.ALIGN_CENTER) {
drawSpacingX += gap / 2 ;
} else if (align == Align.ALIGN_RIGHT) {
drawSpacingX += gap;
}
}
for (int j = 0 ; j < line.length(); j++) {
float drawX = paint.measureText(line.substring(0 , j)) + interval * j;
canvas.drawText(line.substring(j, j + 1 ), drawX + drawSpacingX, drawY +
paddingTop + textLineSpaceExtra * i, paint);
}
}
}
/**
* 设置尾行对齐方式
*
* @param align 对齐方式
*/
public void setAlign (Align align) {
this .align = align;
invalidate();
}
/**
* 计算每行应显示的文本数
*
* @param text 要计算的文本
*/
private void calc (Paint paint, String text) {
if (text.length() == 0 ) {
lines.add("\n" );
return ;
}
int startPosition = 0 ;
float oneChineseWidth = paint.measureText("中" );
int ignoreCalcLength = (int ) (width / oneChineseWidth);
StringBuilder sb = new StringBuilder(text.substring(0 , Math.min(ignoreCalcLength + 1 ,
text.length())));
for (int i = ignoreCalcLength + 1 ; i < text.length(); i++) {
if (paint.measureText(text.substring(startPosition, i + 1 )) > width) {
startPosition = i;
lines.add(sb.toString());
sb = new StringBuilder();
if ((text.length() - startPosition) > ignoreCalcLength) {
sb.append(text.substring(startPosition, startPosition + ignoreCalcLength));
} else {
lines.add(text.substring(startPosition));
break ;
}
i = i + ignoreCalcLength - 1 ;
} else {
sb.append(text.charAt(i));
}
}
if (sb.length() > 0 ) {
lines.add(sb.toString());
}
tailLines.add(lines.size() - 1 );
}
@Override
public void setText (CharSequence text, BufferType type) {
firstCalc = true ;
super .setText(text, type);
}
@Override
public void setPadding (int left, int top, int right, int bottom) {
if (!setPaddingFromMe) {
originalPaddingBottom = bottom;
}
setPaddingFromMe = false ;
super .setPadding(left, top, right, bottom);
}
/**
* 获取文本实际所占高度,辅助用于计算行高,行数
*
* @param text 文本
* @param textSize 字体大小
* @param deviceWidth 屏幕宽度
*/
private void measureTextViewHeight (String text, float textSize, int deviceWidth) {
TextView textView = new TextView(getContext());
textView.setText(text);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(deviceWidth, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0 , MeasureSpec.UNSPECIFIED);
textView.measure(widthMeasureSpec, heightMeasureSpec);
originalLineCount = textView.getLineCount();
originalHeight = textView.getMeasuredHeight();
}
}
activity_main.xml布局中添加一个普通的TextView,添加一个AlignTextView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"
xmlns:tools ="http://schemas.android.com/tools"
android:layout_width ="match_parent"
android:layout_height ="match_parent"
android:paddingBottom ="@dimen/activity_vertical_margin"
android:paddingLeft ="@dimen/activity_horizontal_margin"
android:paddingRight ="@dimen/activity_horizontal_margin"
android:paddingTop ="@dimen/activity_vertical_margin"
android:orientation ="vertical"
tools:context ="com.zcn.demo.aligntextview.MainActivity" >
<TextView
android:id ="@+id/text"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content" />
<com.zcn.demo.aligntextview.AlignTextView
android:id ="@+id/text1"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content" />
</LinearLayout >
AlignTextViews的使用和正常的TextView使用一样
public class MainActivity extends AppCompatActivity {
private AlignTextView alignText;
private TextView text;
@Override
protected void onCreate (Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.text);
text.setText("这个是测试文本,用于观察现象,做对比使用,this is test text,for to see the different in different Controls" );
alignText = (AlignTextView)findViewById(R.id.text1);
alignText.setText("这个是测试文本,用于观察现象,做对比使用,this is test text,for to see the different in different Controls" );
}
}