Android-TextView换行排版混乱-允许设置SpannableString
2016-01-14 15:10
1796 查看
首先感谢两个网站提供的灵感:
http://www.it165.net/pro/html/201405/14606.html
另一个忘了地址了,哪天找出来加上
1.概述
TextView在换行时遇到首字符为字母、符号、汉字等时会产生提前换行、排版混乱等,看图
带ta看房处”,当第一个有字母开头时,提前换行
![](https://oscdn.geek-share.com/Uploads/Images/Content/202006/04/99394c2bd60d9b25868778fa1c59ba93)
2.解决方案:1).半圆角转换 2).字符过滤成全英文字符
3).前两种解决都不够理想——自定义TextView才是王道
3.分析自定义步骤:
1).文字大小统一时,每当每行排满进行换行,然后绘制每行的内容
2).SpannableString设置了RelativeSizeSpan、ForegroundSpan等,甚至设置了多个,
需要逐字绘制,并在排满时进行换行
效果图:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202006/04/d8ac4f4fe6abc9224a488001083cc1b9)
前两个橙色背景是正常textview,黄色背景是自定义的效果
解决了允许以字母为首且不会提前换行的情况
重写setText(Charsequence text, BufferType type) 方法,并获取SpannableString设置
逐个字符绘制
主Activity里面进行设置
此自定义的TextView不仅可以处理SpannableString属性,而且不用重新设置attr.xml文件,直接重写setText方法。
关于其它效果的设置,参考里面对SpannableString的处理,可以自行扩展,此处未进行高度设置,有简单方法的大牛等待共享
全部资源下载处:
http://download.csdn.net/detail/xiaoweiguoyuan/9404182
http://www.it165.net/pro/html/201405/14606.html
另一个忘了地址了,哪天找出来加上
1.概述
TextView在换行时遇到首字符为字母、符号、汉字等时会产生提前换行、排版混乱等,看图
带ta看房处”,当第一个有字母开头时,提前换行
2.解决方案:1).半圆角转换 2).字符过滤成全英文字符
3).前两种解决都不够理想——自定义TextView才是王道
3.分析自定义步骤:
1).文字大小统一时,每当每行排满进行换行,然后绘制每行的内容
2).SpannableString设置了RelativeSizeSpan、ForegroundSpan等,甚至设置了多个,
需要逐字绘制,并在排满时进行换行
效果图:
前两个橙色背景是正常textview,黄色背景是自定义的效果
解决了允许以字母为首且不会提前换行的情况
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); paint = new Paint(); /* 设置宽度 */ int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); //父控件允许子控件的最大宽度,包括子控件设置的具体值或者fill_parent if(specMode == MeasureSpec.EXACTLY) { //fill_parent,也相当于设置了exactly mWidth = specSize; } else { //子控件需要的宽度 int desiredSize = (int) (getPaddingLeft() + getPaddingRight() + mContent.length() * getTextSize()); if(specMode == MeasureSpec.AT_MOST) { //wrap_content //两者取最小 desiredSize = Math.min(specSize, desiredSize); } mWidth = desiredSize; //TextView的宽度 } // /* // 根据文本内容设置高度 // */ // Rect textBound = new Rect(); // paint.setTextSize(getTextSize()); // paint.getTextBounds(mContent,0,mContent.length(),textBound); // mHeight = textBound.height() + getPaddingBottom() + getPaddingTop(); mContentWidth = mWidth - getPaddingLeft() - getPaddingRight(); //View里面内容的宽度 setMeasuredDimension(mWidth, heightMeasureSpec); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); }
重写setText(Charsequence text, BufferType type) 方法,并获取SpannableString设置
@Override public void setText(CharSequence text, BufferType type) { //该行不能删掉,否则出错 super.setText("", type); if(text instanceof SpannableString) { SpannableString sStr = (SpannableString) text; //获取所有的ForegroundSpan ForegroundColorSpan[] spans = sStr.getSpans(0, sStr.length(), ForegroundColorSpan.class); for(ForegroundColorSpan span:spans) { FGSpan fgSpan = new FGSpan(); fgSpan.fgColor = span.getForegroundColor(); fgSpan.start = sStr.getSpanStart(span); fgSpan.end = sStr.getSpanEnd(span); fgSpans.add(fgSpan); } //获取所有的RelativeSizeSpan RelativeSizeSpan[] spans2 = sStr.getSpans(0, sStr.length(), RelativeSizeSpan.class); for(RelativeSizeSpan span:spans2) { RLSpan rlSpan = new RLSpan(); rlSpan.propertion = span.getSizeChange(); rlSpan.start = sStr.getSpanStart(span); rlSpan.end = sStr.getSpanEnd(span); rlSpans.add(rlSpan); } } //内容赋值 mContent = text.toString(); //如果mContent不是static的话,xml里面text无法同步 }
逐个字符绘制
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //逐个字符画出 int fgSpanIndex = 0; //标记fgSpans下标 int rlSpanIndex = 0; //标记rlSpans下标 paint.setColor(getCurrentTextColor()); paint.setTextSize(getTextSize()); float x = 0,y = 0; //绘制字符时的x,y坐标 Paint.FontMetrics fm = paint.getFontMetrics(); float baseLine = fm.descent - fm.ascent; y = baseLine; for(int i = 0; i < mContent.length(); i++) { /* 画当前字符 */ //处理RelativeSizeSpan——若当前字符有RelativeSizeSpan设置,则进行设置 if(rlSpans != null && rlSpans.size() > 0) { if(rlSpanIndex < rlSpans.size() && i >= rlSpans.get(rlSpanIndex).start && i < rlSpans.get(rlSpanIndex).end) { paint.setTextSize(rlSpans.get(rlSpanIndex).propertion*getTextSize()); } else if(rlSpanIndex < rlSpans.size() && i >= rlSpans.get(rlSpanIndex).end) { rlSpanIndex++; } } //处理ForegroundSpan——若当前字符有ForegroundSpan设置,则进行设置 if(fgSpans != null && fgSpans.size() > 0) { if(fgSpanIndex < fgSpans.size() && i >= fgSpans.get(fgSpanIndex).start && i < fgSpans.get(fgSpanIndex).end) { paint.setColor(fgSpans.get(fgSpanIndex).fgColor); } else if(fgSpanIndex < fgSpans.size() && i >= fgSpans.get(fgSpanIndex).end) { fgSpanIndex++; } } if(i +2 <=mContent.length()/*主要是防止后面i+1,i+2越界*/ && x >= mContentWidth - paint.measureText(mContent.substring(i+1,i+2))) { //换行 x = 0; y += baseLine + fm.leading; } canvas.drawText(mContent.substring(i,i+1),x,y,paint); //计算下个字符水平位置 x += paint.measureText(mContent.substring(i,i+1)); paint.setTextSize(getTextSize()); paint.setColor(getCurrentTextColor()); } }
主Activity里面进行设置
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyTextView myTextView = (MyTextView) findViewById(R.id.my); SpannableString sp1 = new SpannableString("是解决得快的健康减肥大力度dkfjkajdvb是解决得快的健康减肥大力度dkfjkajd是解决得快的健康减肥大力度dkfjkajd"); sp1.setSpan(new ForegroundColorSpan(getResources() .getColor(R.color.blue)), 5, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sp1.setSpan(new ForegroundColorSpan(getResources() .getColor(R.color.red)), 5, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sp1.setSpan(new RelativeSizeSpan((float) 2.0), 5, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sp1.setSpan(new ForegroundColorSpan(getResources(). getColor(R.color.green_blue)),10, 15,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sp1.setSpan(new RelativeSizeSpan((float) 2.0), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText(sp1); }
此自定义的TextView不仅可以处理SpannableString属性,而且不用重新设置attr.xml文件,直接重写setText方法。
关于其它效果的设置,参考里面对SpannableString的处理,可以自行扩展,此处未进行高度设置,有简单方法的大牛等待共享
全部资源下载处:
http://download.csdn.net/detail/xiaoweiguoyuan/9404182
相关文章推荐
- Android Navigation Bar
- 【Android】错误:Installation error: INSTALL_FAILED_VERSION_DOWNGRADE
- AlertDialog crashes with BadTokenException
- Android Studio 快捷键
- Android长按imageview把图片保存到本地
- android实现多界面左右滑动切换
- android studio1.0创建模拟器不显示右边的虚拟键盘
- Android LRU算法中LinkedHashMap补充 <11>
- Android系统GPS分析
- Android:Field can be converted to a local varible.
- Android字符串资源及其格式化
- Android:Field can be converted to a local varible.
- 自定义圆形头像
- Android DrawingCache超詳細解析,解決getDrawingCache方法回傳null
- 解决Android SDK Manager的Download interrupted: URL not found问题
- android开机动画启动流程
- Android 剪贴板操作方法在不同版本API下的使用
- Android Context完全解析
- android实现qq侧滑
- Android 仿QQ联系人,分组悬浮,展开停至特定项