android textview 自动换行 整齐排版
一、问题在哪里?
textview显示长文字时会进行自动折行,如果遇到一些特殊情况,自动折行会杯具成这个样子:

上述特殊情况包括:
1)全角/半角符号混排(一般是数字、字母、汉字混排)
2)全角/半角标点符号出现在行首时,该标点符号会连同其前一个字符跳到下一行
3)英文单词不能被折成两行
4)......
二、怎么搞?
通常有两类解决方案:
1)修改文本内容,将所有符号全角化、在标点符号前面加空格等等……
2)保持文本内容不变,在合适的位置将文本手动分成多行
本文采用第二种方案,更加通用,也最大限度的保留了原文本。
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5159125.html]
三、开始干活
3.1 “在合适的位置将文本手动分成多行”需要知道textview的实际宽度、字体大小等信息,框架如下:
public class TestCActivity extends Activity {
private TextView mText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testc);
mText = (TextView)findViewById(R.id.txt);
mText.setText("本文地址http://www.cnblogs.com/goagent/p/5159125.html本文地址啊本文。地址。啊http://www.cnblogs.com/goagent/p/5159125.html");
mText.getViewTreeObserver().addOnGlobalLayoutListener(new OnTvGlobalLayoutListener());
}
private class OnTvGlobalLayoutListener implements OnGlobalLayoutListener {
@Override
public void onGlobalLayout() {
mText.getViewTreeObserver().removeOnGlobalLayoutListener(this);
final String newText = autoSplitText(mText);
if (!TextUtils.isEmpty(newText)) {
mText.setText(newText);
}
}
}
private String autoSplitText(final TextView tv) {
final String rawText = tv.getText().toString();
final Paint tvPaint = tv.getPaint();
final int tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight();
//autoSplitText begin....
String newText = rawText;
//autoSplitText end....
return newText;
}
}
3.2 实现自动分割文本,简单来说就是用textview的paint逐字符测量,如果发现当前行绘制不下了,就手动加入一个换行符:
private String autoSplitText(final TextView tv) {
final String rawText = tv.getText().toString(); //原始文本
final Paint tvPaint = tv.getPaint(); //paint,包含字体等信息
final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控件可用宽度
//将原始文本按行拆分
String [] rawTextLines = rawText.replaceAll("\r", "").split("\n");
StringBuilder sbNewText = new StringBuilder();
for (String rawTextLine : rawTextLines) {
if (tvPaint.measureText(rawTextLine) <= tvWidth) {
//如果整行宽度在控件可用宽度之内,就不处理了
sbNewText.append(rawTextLine);
} else {
//如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
float lineWidth = 0;
for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) {
char ch = rawTextLine.charAt(cnt);
lineWidth += tvPaint.measureText(String.valueOf(ch));
if (lineWidth <= tvWidth) {
sbNewText.append(ch);
} else {
sbNewText.append("\n");
lineWidth = 0;
--cnt;
}
}
}
sbNewText.append("\n");
}
//把结尾多余的\n去掉
if (!rawText.endsWith("\n")) {
sbNewText.deleteCharAt(sbNewText.length() - 1);
}
return sbNewText.toString();
}
3.3 话不多说,效果如下:

[转载请保留本文地址:http://www.cnblogs.com/snser/p/5159125.html]
四、更多玩法
4.1 可以封装一个自定义的textview,直接包含自动排版换行的功能:
package cc.snser.test; import android.content.Context;
import android.graphics.Paint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView; public class AutoSplitTextView extends TextView {
private boolean mEnabled = true; public AutoSplitTextView(Context context) {
super(context);
} public AutoSplitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
} public AutoSplitTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} public void setAutoSplitEnabled(boolean enabled) {
mEnabled = enabled;
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
&& getWidth() > 0
&& getHeight() > 0
&& mEnabled) {
String newText = autoSplitText(this);
if (!TextUtils.isEmpty(newText)) {
setText(newText);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} private String autoSplitText(final TextView tv) {
final String rawText = tv.getText().toString(); //原始文本
final Paint tvPaint = tv.getPaint(); //paint,包含字体等信息
final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控件可用宽度 //将原始文本按行拆分
String [] rawTextLines = rawText.replaceAll("\r", "").split("\n");
StringBuilder sbNewText = new StringBuilder();
for (String rawTextLine : rawTextLines) {
if (tvPaint.measureText(rawTextLine) <= tvWidth) {
//如果整行宽度在控件可用宽度之内,就不处理了
sbNewText.append(rawTextLine);
} else {
//如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
float lineWidth = 0;
for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) {
char ch = rawTextLine.charAt(cnt);
lineWidth += tvPaint.measureText(String.valueOf(ch));
if (lineWidth <= tvWidth) {
sbNewText.append(ch);
} else {
sbNewText.append("\n");
lineWidth = 0;
--cnt;
}
}
}
sbNewText.append("\n");
} //把结尾多余的\n去掉
if (!rawText.endsWith("\n")) {
sbNewText.deleteCharAt(sbNewText.length() - 1);
} return sbNewText.toString();
}
}
View AutoSplitTextView.java
package cc.snser.test; import android.app.Activity;
import android.os.Bundle; public class TestCActivity extends Activity {
private AutoSplitTextView mText; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.testc); mText = (AutoSplitTextView)findViewById(R.id.txt);
mText.setText("本文地址http://www.cnblogs.com/goagent/p/5159125.html本文地址啊本文。地址。啊http://www.cnblogs.com/goagent/p/5159125.html");
}
}
View TestCActivity.java
<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:background="@android:color/white"
android:orientation="vertical" > <cc.snser.test.AutoSplitTextView
android:id="@+id/txt"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="11dp"
android:layout_marginLeft="11dp"
android:layout_marginRight="11dp"
android:background="@android:color/holo_blue_light"
android:textSize="20sp"
android:textColor="@android:color/black" /> </LinearLayout>
View testc.xml
4.2 实现悬挂缩进
private String autoSplitText(final TextView tv, final String indent) {
final String rawText = tv.getText().toString(); //原始文本
final Paint tvPaint = tv.getPaint(); //paint,包含字体等信息
final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控件可用宽度
//将缩进处理成空格
String indentSpace = "";
float indentWidth = 0;
if (!TextUtils.isEmpty(indent)) {
float rawIndentWidth = tvPaint.measureText(indent);
if (rawIndentWidth < tvWidth) {
while ((indentWidth = tvPaint.measureText(indentSpace)) < rawIndentWidth) {
indentSpace += " ";
}
}
}
//将原始文本按行拆分
String [] rawTextLines = rawText.replaceAll("\r", "").split("\n");
StringBuilder sbNewText = new StringBuilder();
for (String rawTextLine : rawTextLines) {
if (tvPaint.measureText(rawTextLine) <= tvWidth) {
//如果整行宽度在控件可用宽度之内,就不处理了
sbNewText.append(rawTextLine);
} else {
//如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
float lineWidth = 0;
for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) {
char ch = rawTextLine.charAt(cnt);
//从手动换行的第二行开始,加上悬挂缩进
if (lineWidth < 0.1f && cnt != 0) {
sbNewText.append(indentSpace);
lineWidth += indentWidth;
}
lineWidth += tvPaint.measureText(String.valueOf(ch));
if (lineWidth <= tvWidth) {
sbNewText.append(ch);
} else {
sbNewText.append("\n");
lineWidth = 0;
--cnt;
}
}
}
sbNewText.append("\n");
}
//把结尾多余的\n去掉
if (!rawText.endsWith("\n")) {
sbNewText.deleteCharAt(sbNewText.length() - 1);
}
return sbNewText.toString();
}
调用方式:
autoSplitText(tv, "1、");
悬挂缩进效果:

[转载请保留本文地址:http://www.cnblogs.com/snser/p/5159125.html]
android textview 自动换行 整齐排版的更多相关文章
- Android TextView自动换行文字排版参差不齐的原因
今天项目没什么进展,公司后台出问题了.看了下刚刚学习Android时的笔记,发现TextView会自动换行,而且排版文字参差不齐.查了下资料,总结原因如下: 1.半角字符与全角字符混乱所致:这种情况一 ...
- Android TextView自动换行、排列错乱问题及解决
解决之前层次不齐的排版截图,如下图: 解决之后的整齐排版截图,如下图: 今天忽然发现android项目中的文字排版参差不齐的情况非常严重,不得不想办法解决一下 ...
- Android:TextView 自动滚动(跑马灯) (转)
Android:TextView 自动滚动(跑马灯) TextView实现文字滚动需要以下几个要点: 1.文字长度长于可显示范围:android:singleLine="true ...
- Android TextView 添加下划线的几种方式
总结起来大概有5种做法: 1. 将要处理的文字写到一个资源文件,如string.xml(使用html用法格式化) 2. 当文字中出现URL.E-mail.电话号码等的时候,可以将TextView ...
- Android TextView图文混合编排
Android TextView图文混合编排 实现技术细节不难,两个要点:1.html代码的混合编写.2,重写ImageGetter.例如:布局: <?xml version="1.0 ...
- android Textview动态设置大小
import android.app.Activity; //import com.travelzen.tdx.BaseActivity; //import com.travelzen.tdx.uti ...
- Android TextView内容过长加省略号,点击显示全部内容
在Android TextView中有个内容过长加省略号的属性,即ellipsize,用法如下: 在xml中:android:ellipsize="end" 省略号在结尾an ...
- android TextView多行文本(超过3行)使用ellipsize属性无效问题的解决方法
这篇文章介绍了android TextView多行文本(超过3行)使用ellipsize属性无效问题的解决方法,有需要的朋友可以参考一下 布局文件中的TextView属性 复制代码代码如下: < ...
- Android - TextView Ellipsize属性
Android - TextView Ellipsize属性 本文地址: http://blog.csdn.net/caroline_wendy android:ellipsize属性: If set ...
随机推荐
- Spark笔记:复杂RDD的API的理解(下)
本篇接着谈谈那些稍微复杂的API. 1) flatMapValues:针对Pair RDD中的每个值应用一个返回迭代器的函数,然后对返回的每个元素都生成一个对应原键的键值对记录 这个方法我最开始接 ...
- xamarin android ListView手动分组
xamarin的listview控件其实自带有分组方法,关于xamarin listview的自带分组方法请自行参考官方文档,我这里只写自己写的分组方法.xamarin自带的分组好是好,功能多,但是加 ...
- 我们为什么使用Node
引言:Node 已经迅速成为一个可行并且真正高效的web 开发平台.在Node 诞生之前,在服务端运行JavasScript 是件不可思议的事情,并且对其他的脚本语言来说,要实现非阻塞I/O 通常需要 ...
- 给 Android 研发的一些的建议
作为应用程序开发人员,我们需要注意在开发应用程序时的一些问题. 这些问题的安全级别是取决于应用程序的类型和使用域. 在这里列举了一些我们在开发中需要注意的一些问题: 开发日志输出相关: 1. 不要在 ...
- 前端学HTTP之数据传输
× 目录 [1]客户机处理 [2]集线器处理 [3]路由器1处理[4]路由器2处理[5]交换机处理[6]服务器处理[7]反向传输 前面的话 上一篇中,介绍了网络基础.本文将详细介绍客户机在浏览网页ab ...
- Java开发中的23种设计模式详解(转)
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- SQLCMD备忘录:执行文件夹所有Sql文件
在做性能测试的时候最希望的一件事情是数据自动导入. 一般做法就是写很多SQL文件,通过Bat自动执行所有Sql文件. Bat代码: @ECHO OFF SET SQLCMD="C:\Prog ...
- jquery dataTable汉化(插件形式)
1.jquery dataTable.js 官网:http://datatables.net/ 中文:http://dt.thxopen.com/ 2.汉化提示信息(放到xx.js中,引入即可) 注: ...
- C#面试题汇总(未完成)
泛型的好处: 1.可以保证类型安全以及避免装箱和拆箱操作,泛型类会在编译时由具体的类型去取代. 2.我们就拿一个ArrayList来说吧,ArrayList要进行拆箱操作,也就是ArrayList传入 ...
- context:component-scan" 的前缀 "context" 未绑定。
SpElUtilTest.testSpELLiteralExpressiontestSpELLiteralExpression(cn.zr.spring.spel.SpElUtilTest)org.s ...