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 ...
随机推荐
- Linux 安装Mono环境 运行ASP.NET(一)
1.先看一下Linux环境下面请求的过程,(画的不是很好,简单的了解一下原理.) .NET跨平台其实需要这三个关键:编译器.CLR和基础类库.在.NET下我们编写一个最简单的"Hello W ...
- Jexus服务器SSL二级证书安装指南
申请获得服务器证书有三张,一张服务器证书,二张中级CA证书.在Android微信中访问Https,如果服务器只有一张CA证书,就无法访问. 获取服务器证书中级CA证书: 为保障服务器证书在客户端的兼容 ...
- 轻量级通信引擎StriveEngine —— C/S通信demo(附源码)
前段时间,有几个研究ESFramework的朋友对我说,ESFramework有点庞大,对于他们目前的项目来说有点“杀鸡用牛刀”的意思,因为他们的项目不需要文件传送.不需要P2P.不存在好友关系.也不 ...
- 分布式存储 CentOS6.5虚拟机环境搭建FastDFS-5.0.5集群
前言: 由于公司项目需要,最近开始学习一下分布式存储相关知识,确定使用FastDFS这个开源工具.利用周末的时间在虚拟机上搭建了分布式存储系统,在搭建过程中,发现网上的资料说的并不是很全, ...
- ABP源码分析三十二:ABP.SignalR
Realtime Realtime是ABP底层模块提供的功能,用于管理在线用户.它是使用SignalR实现给在线用户发送通知的功能的前提 IOnlineClient/OnlineClient: 封装在 ...
- PagerSlidingTabStrip介绍及使用,让ViewPager更绚丽
转载请注明出处http://blog.csdn.net/harryweasley/article/details/42290595,谢谢. 以前一直想着,ViewPager中间的那个横线怎么跟着屏幕的 ...
- 【Win 10 应用开发】Sqlite 数据库的简单用法
如果老周没记错的话,园子里曾经有朋友写过如何在 UWP 项目中使用 Sqlite数据库的文章.目前我们都是使用第三方封装的库,将来,SDK会加入对 Sqlite 的支持. 尽管目前 UWP-RT 库中 ...
- js和jquery实现简单的选项卡
选项卡切换在做网页的时候经常会用到,以往都是用JQ来实现,代码简单易懂,今天用原生的js实现了一下,二者还是有很大不同的,可以对比一下代码来研究一下. <!DOCTYPE html> &l ...
- Android混合开发之WebView使用总结
前言: 今天修改项目中一个有关WebView使用的bug,激起了我总结WebView的动机,今天抽空做个总结. 混合开发相关博客: Android混合开发之WebView使用总结 Android混合开 ...
- Spring Bean的生命周期(非常详细)
Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...