您的位置:首页 > 其它

自定义View:左右两边显示 text 的 TextView(RightAndLeftTextView)

2017-09-30 14:47 537 查看
简书:http://www.jianshu.com/p/1c51960b8771

工作中有这样一个需求:



打开时动态添加,不知道有多少行,那就开动脑经:在 xml 中写的话,又不确定有多少,那就在代码中 addView,但是呢,需求中又有很多处的 UI 布局也是这样的样式(左右 text 要根据情况修改),当然,一个LinearLayout 包两个 TextView 不难搞,但能不能自定义一个 TextView,简单复用 …

说干就干!

自定义view也有很多种解决方案,可以继承 View、LinearLayout、TextView 等等等等,这里本人继承 TextView,运用一些原先的属性,偷偷懒:

/**
* @author 小侨
* @desc 左右两边显示 text的 TextView
* <p>
* 参考:
* http://hencoder.com/ui-1-3/ * http://blog.csdn.net/xmxkf/article/details/51454685 */

public class RightAndLeftTextView extends android.support.v7.widget.AppCompatTextView {

/**
* PS:TextView有默认 TextSize,setXxxSize时会有视差,所以setXxxText前设置为左右之中最大的 TextSize
*      -- xml中:android:textSize="xxsp"
*      -- 代码中:setTextSize(TypedValue.COMPLEX_UNIT_SP, xx);
*/

// 文本
private String mLeftText;
private String mRightText;
// 文本颜色
private int mLeftTextColor;
private int mRightTextColor;
// 文本大小
private float mLeftTextSize;
private float mRightTextSize;
// Paint
private TextPaint mPaint;
// 宽高
private int mWidth;
private int mHalfWidth;
private int mRightTextWidth;
private int mLeftTextWidth;
private int mRightTextX;

public RightAndLeftTextView(Context context) {
this(context, null);
}

public RightAndLeftTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public RightAndLeftTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

// 获取自定义属性的值
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RightAndLeftTextView, defStyleAttr, 0);
mLeftText = a.getString(R.styleable.RightAndLeftTextView_left_text);
mLeftTextColor = a.getColor(R.styleable.RightAndLeftTextView_left_color, Color.BLACK);
mLeftTextSize = a.getDimension(R.styleable.RightAndLeftTextView_left_size, 100);
mRightText = a.getString(R.styleable.RightAndLeftTextView_right_text);
mRightTextColor = a.getColor(R.styleable.RightAndLeftTextView_right_color, Color.BLACK);
mRightTextSize = a.getDimension(R.styleable.RightAndLeftTextView_right_size, 100);
// 注意回收
a.recycle();

mPaint = new TextPaint();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHalfWidth = mWidth / 2;
}

@Override
protected void onDraw(Canvas canvas) {
drawRightText(canvas);
drawLeftText(canvas);

// 设置默认文本大小
if (mLeftTextSize < mRightTextSize) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mRightTextSize);
} else {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mLeftTextSize);
}
// 设置行数
if (mLeftTextWidth >= mHalfWidth || mRightTextWidth >= mHalfWidth) {
if (mLeftTextWidth < mRightTextWidth) {
if ((mRightTextWidth % mHalfWidth) > 0) {
setLines(mRightTextWidth / mHalfWidth + 1);
} else {
setLines(mRightTextWidth / mHalfWidth);
}
} else if (mRightTextWidth < mHalfWidth) {
if ((mLeftTextWidth % (mRightTextX)) > 0) {
setLines(mLeftTextWidth / mRightTextX + 1);
} else {
setLines(mLeftTextWidth / mRightTextX);
}
} else {
if ((mLeftTextWidth % mHalfWidth) > 0) {
setLines(mLeftTextWidth / mHalfWidth + 1);
} else {
setLines(mLeftTextWidth / mHalfWidth);
}
}
}
}

/**
* 左边 text
*/
private void drawLeftText(Canvas canvas) {
mPaint.setTextSize(mLeftTextSize);
mPaint.setColor(mLeftTextColor);
mLeftTextWidth = (int) mPaint.measureText(mLeftText);
// 绘制文字,不能换行
// canvas.drawText(mLeftText, 0, mPaint.getTextSize(), mPaint);
// 绘制文字,能换行
canvas.save();
canvas.translate(0, 0);
StaticLayout staticLayout = new StaticLayout(mLeftText, mPaint, mRightTextX, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
staticLayout.draw(canvas);
canvas.restore();
}

/**
* 右边 text
*/
private void drawRightText(Canvas canvas) {
mPaint.setTextSize(mRightTextSize);
mPaint.setColor(mRightTextColor);
mRightTextWidth = (int) mPaint.measureText(mRightText);
// 绘制文字,不能换行
// canvas.drawText(mRightText, mWidth - mRightTextWidth, mHeight, mPaint);
// 绘制文字,能换行
canvas.save();
StaticLayout staticLayout;
// 左右两边的 text长度如果超过一半 mWidth就换行
if (mRightTextWidth >= mHalfWidth) {
mRightTextX = mHalfWidth;
canvas.translate(mRightTextX, 0);
staticLayout = new StaticLayout(mRightText, mPaint, mHalfWidth, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
} else {
mRightTextX = mWidth - mRightTextWidth;
canvas.translate(mRightTextX, 0);
staticLayout = new StaticLayout(mRightText, mPaint, mRightTextWidth, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
}
staticLayout.draw(canvas);
canvas.restore();
}

public String getLeftText() {
return mLeftText;
}

public void setLeftText(String leftText) {
mLeftText = leftText;
invalidate();
}

public String getRightText() {
return mRightText;
}

public void setRightText(String rightText) {
mRightText = rightText;
invalidate();
}

public int getLeftTextColor() {
return mLeftTextColor;
}

public void setLeftTextColor(int leftTextColor) {
mLeftTextColor = leftTextColor;
invalidate();
}

public int getRightTextColor() {
return mRightTextColor;
}

public void setRightTextColor(int rightTextColor) {
mRightTextColor = rightTextColor;
invalidate();
}

public float getLeftTextSize() {
return mLeftTextSize;
}

public void setLeftTextSize(float leftTextSize) {
mLeftTextSize = leftTextSize;
invalidate();
}

public float getRightTextSize() {
return mRightTextSize;
}

public void setRightTextSize(float rightTextSize) {
mRightTextSize = rightTextSize;
invalidate();
}
}


以下是 attrs.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RightAndLeftTextView">
<attr name="left_text" format="string"/>
<attr name="left_color" format="color"/>
<attr name="left_size" format="dimension"/>
<attr name="right_text" format="string"/>
<attr name="right_color" format="color"/>
<attr name="right_size" format="dimension"/>
</declare-styleable>
</resources>


注意点一:自定义属性,包括左右 text、textSize、textColor

// 获取自定义属性的值
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RightAndLeftTextView, defStyleAttr, 0);
mLeftText = a.getString(R.styleable.RightAndLeftTextView_left_text);
mLeftTextColor = a.getColor(R.styleable.RightAndLeftTextView_left_color, Color.BLACK);
mLeftTextSize = a.getDimension(R.styleable.RightAndLeftTextView_left_size, 100);
mRightText = a.getString(R.styleable.RightAndLeftTextView_right_text);
mRightTextColor = a.getColor(R.styleable.RightAndLeftTextView_right_color, Color.BLACK);
mRightTextSize = a.getDimension(R.styleable.RightAndLeftTextView_right_size, 100);
// 注意回收
a.recycle();


注意点二:用 StaticLayout 绘制能换行的文字

本来没考虑的,因为需求没有,但为了适配性更好,说不定有人用了特大字体呢 …

// 绘制文字,不能换行
// canvas.drawText(mLeftText, 0, mPaint.getTextSize(), mPaint);
// 绘制文字,能换行
canvas.save();
canvas.translate(0, 0);
StaticLayout staticLayout = new StaticLayout(mLeftText, mPaint, mRightTextX, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
staticLayout.draw(canvas);
canvas.restore();


注意点三:先绘制右边 text

要先绘制右边 text,再根据右边 text 的位置来绘制左边 text,可以规定右边 text 所占宽度不超过多少,使实际效果更好看

// 左右两边的 text长度如果超过一半 mWidth就换行
if (mRightTextWidth >= mHalfWidth) {
mRightTextX = mHalfWidth;
canvas.translate(mRightTextX, 0);
staticLayout = new StaticLayout(mRightText, mPaint, mHalfWidth, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
} else {
mRightTextX = mWidth - mRightTextWidth;
canvas.translate(mRightTextX, 0);
staticLayout = new StaticLayout(mRightText, mPaint, mRightTextWidth, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
}


注意点四:设置文本大小及行数

TextView能通过文本大小及多少来计算高度,但是这里是通过canvas直接绘制的,谁让我比较懒不自己计算高度(其实是水平有限),所以要通过TextView本身的setTextSize()来让TextView知道:

// 设置默认文本大小
if (mLeftTextSize < mRightTextSize) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mRightTextSize);
} else {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mLeftTextSize);
}


文本行数也是一样的道理:

// 设置行数
if (mLeftTextWidth >= mHalfWidth || mRightTextWidth >= mHalfWidth) {
if (mLeftTextWidth < mRightTextWidth) {
if ((mRightTextWidth % mHalfWidth) > 0) {
setLines(mRightTextWidth / mHalfWidth + 1);
} else {
setLines(mRightTextWidth / mHalfWidth);
}
} else if (mRightTextWidth < mHalfWidth) {
if ((mLeftTextWidth % (mRightTextX)) > 0) {
setLines(mLeftTextWidth / mRightTextX + 1);
} else {
setLines(mLeftTextWidth / mRightTextX);
}
} else {
if ((mLeftTextWidth % mHalfWidth) > 0) {
setLines(mLeftTextWidth / mHalfWidth + 1);
} else {
setLines(mLeftTextWidth / mHalfWidth);
}
}
}


注意点五:动态 set 的时候要刷新

public void setLeftText(String leftText) {
mLeftText = leftText;
invalidate();
}


效果:



实际应用中有很多问题:

问题一: TextView 有默认 TextSize,如果是先在 xml 中应用了,然后在代码中 set 修改文本大小,setXxxSize 时会有视差,肉眼看上去会有明显的抖动,所以可以在 xml 中运用 android:textSize=”xxsp” 设置成最大的文本 size,就没有抖动了

问题二:当文字超过三行时,可能出现后几个文字显示不全的问题,目前还不知道原因在哪,要捋一捋

问题三:如果左右文字加起来刚好占了一行,没有做间隔处理,后补

现在想一想,还有更好的方案能解决这个需求,且本方案还有很多需要改进的地方,但是呢,通过前些日子的修稿,确实感受良多,不断出现问题,刚开始总会想“这样就好了”,但又觉得改改还能更好,改改吧,不是最好,过程胜似最好 …

实践是检验真理的唯一标准,退堂!

参考文章:

http://hencoder.com/ui-1-3/

http://blog.csdn.net/xmxkf/article/details/51454685
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐