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 ...
随机推荐
- .NET Core性能测试组件BenchmarkDotNet 支持.NET Framework Mono
.NET Core 超强性能测试组件BenchmarkDotNet 支持Full .NET Framework, .NET Core (RTM), Mono. BenchmarkDotNet支持 C# ...
- Angular2学习笔记——NgModule
在Angular2中一个Module指的是使用@NgModule修饰的class.@NgModule利用一个元数据对象来告诉Angular如何去编译和运行代码.一个模块内部可以包含组件.指令.管道,并 ...
- [译]基于GPU的体渲染高级技术之raycasting算法
[译]基于GPU的体渲染高级技术之raycasting算法 PS:我决定翻译一下<Advanced Illumination Techniques for GPU-Based Volume Ra ...
- ABP源码分析二十一:Feature
Feature是什么?Feature就是对function的分类方法,其与function的关系就比如Role和User的关系一样. ABP中Feature具有以下属性: 其中最重要的属性是name, ...
- Dapper:The member of type SeoTKD cannot be used as a parameter Value
异常汇总:http://www.cnblogs.com/dunitian/p/4523006.html#dapper 上次说了一下Dapper的扩展Dapper.Contrib http://www. ...
- SQL Server 复制系列(文章索引)
一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 前言(Introduction) 复制逻辑结构图(Construction) 系列文章索引(Catalog) 总结&am ...
- Sql Server系列:SQL语句查询数据库中表、视图、存储过程等组成
1. 查看用户表 select name from sys.tables select name from sys.objects where type='U' select name from sy ...
- SQL Server中TOP子句可能导致的问题以及解决办法
简介 在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性. 关系数据库中SQL语句只 ...
- 用原始方法解析复杂字符串,json一定要用JsonMapper么?
经常采集数据,肯定会碰到解析字符串,包括整个页面的html,或者json以及一些不标准的json格式... 以前用json序列化,有时候需要实体类,有的时候没有,比较麻烦,听说可以用JsonMappe ...
- webapi+Task并行请求不同接口实例
标题的名称定义不知道是否准确,不过我想表达的意思就是使用Task特性来同时请求多个不同的接口,然后合并数据:我想这种场景的开发对于对接过其他公司接口的人不会陌生,本人也是列属于之内,更多的是使用最原始 ...