自定义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,运用一些原先的属性,偷偷懒:
以下是 attrs.xml 文件:
注意点一:自定义属性,包括左右 text、textSize、textColor
注意点二:用 StaticLayout 绘制能换行的文字
本来没考虑的,因为需求没有,但为了适配性更好,说不定有人用了特大字体呢 …
注意点三:先绘制右边 text
要先绘制右边 text,再根据右边 text 的位置来绘制左边 text,可以规定右边 text 所占宽度不超过多少,使实际效果更好看
注意点四:设置文本大小及行数
TextView能通过文本大小及多少来计算高度,但是这里是通过canvas直接绘制的,谁让我比较懒不自己计算高度(其实是水平有限),所以要通过TextView本身的setTextSize()来让TextView知道:
文本行数也是一样的道理:
注意点五:动态 set 的时候要刷新
效果:
实际应用中有很多问题:
问题一: TextView 有默认 TextSize,如果是先在 xml 中应用了,然后在代码中 set 修改文本大小,setXxxSize 时会有视差,肉眼看上去会有明显的抖动,所以可以在 xml 中运用 android:textSize=”xxsp” 设置成最大的文本 size,就没有抖动了
问题二:当文字超过三行时,可能出现后几个文字显示不全的问题,目前还不知道原因在哪,要捋一捋
问题三:如果左右文字加起来刚好占了一行,没有做间隔处理,后补
现在想一想,还有更好的方案能解决这个需求,且本方案还有很多需要改进的地方,但是呢,通过前些日子的修稿,确实感受良多,不断出现问题,刚开始总会想“这样就好了”,但又觉得改改还能更好,改改吧,不是最好,过程胜似最好 …
实践是检验真理的唯一标准,退堂!
参考文章:
http://hencoder.com/ui-1-3/
http://blog.csdn.net/xmxkf/article/details/51454685
工作中有这样一个需求:
打开时动态添加,不知道有多少行,那就开动脑经:在 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
相关文章推荐
- 关于在TextView中设置DrawableLeft,DrawableRight等不显示的问题
- TextView、Button的drawableLeft和drawableRight与文本一起居中显示
- 自定义控件让TextView、Button的drawableLeft和drawableRight与文本一起居中显示
- 给EditText左右两边设置图片与监听(drawableLeft/drawableRight)/弹出键盘
- 自定义控件让TextView(Radiobutton)、Button的drawableLeft和drawableRight与文本一起居中显示
- TextView显示带有自定义标签的HTML
- TextView的一些高级应用(自定义字体、显示多种颜色、添加阴影)
- android自定义Spinner下拉菜单和AutoCompleteTextView自动显示的(下拉列表框)样式
- Android自定义TextView实现drawableLeft内容居中
- android自定义Seekbar你还将你的拖动数值显示在旁边固定的TextView上么?
- android ExpandableTextView-自定义可以动态展开/收缩显示长文本的TextView
- EditText and TextView显示一行
- TextView 代码设置drawableLeft、drawableRight、drawableTop、drawableBottom
- 自定义TextView任意长度文本下自动改变文字大小以完全显示
- Android动态布局,并动态为TextView控件设置drawableLeft、drawableRight等属性添加图标
- android - TextView单行显示...或者文字左右滚动(走马灯效果)
- android自定义Seekbar你还将你的拖动数值显示在旁边固定的TextView上么?
- android AutoCompleteTextView中文搜索及自定义布局及固定显示条数
- android关于TextView的drawableLeft,drawableTop,drawableRight,drawableRight的drawable大小及位置问题
- IOS如何给VIEW设置2个圆角?set cornerRadius for only top-left and top-right corner of a UIVIEW