一、问题在哪里?

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 自动换行 整齐排版的更多相关文章

  1. Android TextView自动换行文字排版参差不齐的原因

    今天项目没什么进展,公司后台出问题了.看了下刚刚学习Android时的笔记,发现TextView会自动换行,而且排版文字参差不齐.查了下资料,总结原因如下: 1.半角字符与全角字符混乱所致:这种情况一 ...

  2. Android TextView自动换行、排列错乱问题及解决

    解决之前层次不齐的排版截图,如下图:               解决之后的整齐排版截图,如下图:        今天忽然发现android项目中的文字排版参差不齐的情况非常严重,不得不想办法解决一下 ...

  3. Android:TextView 自动滚动(跑马灯) (转)

    Android:TextView 自动滚动(跑马灯)       TextView实现文字滚动需要以下几个要点: 1.文字长度长于可显示范围:android:singleLine="true ...

  4. Android TextView 添加下划线的几种方式

    总结起来大概有5种做法:  1. 将要处理的文字写到一个资源文件,如string.xml(使用html用法格式化)   2. 当文字中出现URL.E-mail.电话号码等的时候,可以将TextView ...

  5. Android TextView图文混合编排

    Android TextView图文混合编排 实现技术细节不难,两个要点:1.html代码的混合编写.2,重写ImageGetter.例如:布局: <?xml version="1.0 ...

  6. android Textview动态设置大小

    import android.app.Activity; //import com.travelzen.tdx.BaseActivity; //import com.travelzen.tdx.uti ...

  7. Android TextView内容过长加省略号,点击显示全部内容

    在Android TextView中有个内容过长加省略号的属性,即ellipsize,用法如下: 在xml中:android:ellipsize="end"    省略号在结尾an ...

  8. android TextView多行文本(超过3行)使用ellipsize属性无效问题的解决方法

    这篇文章介绍了android TextView多行文本(超过3行)使用ellipsize属性无效问题的解决方法,有需要的朋友可以参考一下 布局文件中的TextView属性 复制代码代码如下: < ...

  9. Android - TextView Ellipsize属性

    Android - TextView Ellipsize属性 本文地址: http://blog.csdn.net/caroline_wendy android:ellipsize属性: If set ...

随机推荐

  1. node(ActiveMq)

    简单尝试了node下的ActiveMQ 1.下载apache-activemq-5.9.0,执行bat文件: 2.登录http://localhost:8161/admin可查看其管理后台: 3.安装 ...

  2. Fedora 21 安装 Nvidia 驱动以及失败后的补救方法

    在 Linux 桌面系统下玩了这么久,大部分时间都是使用 Ubuntu,偶尔使用一下 Fedora.我的电脑中安装有多个 Linux 发行版,见这里<在同一个硬盘上安装多个Linux发行版及Fe ...

  3. 如何用 MEF 扩展应用程序

    最近在写一篇关于如何扩展 Visual Studio 编辑器的文章时,用到了 MEF,因此打算写一篇文章提一下这个技术点.本篇文章并不打算详细介绍 MEF,只是一个最简单的入门,相信您在阅读本篇文章后 ...

  4. Java 8函数编程轻松入门(五)并行化(parallel)

    1.并发与并行的区别 并发: 一个时间段内有几个程序都处于已启动到运行完毕之间,且这几个程序都是在同一个处理机上运行.但在任一个时刻点只有一个程序在处理机上运行 并行: 在同一个时刻,多核处理多个任务 ...

  5. 复化梯形求积分——用Python进行数值计算

    用程序来求积分的方法有很多,这篇文章主要是有关牛顿-科特斯公式. 学过插值算法的同学最容易想到的就是用插值函数代替被积分函数来求积分,但实际上在大部分场景下这是行不通的. 插值函数一般是一个不超过n次 ...

  6. 基于Cat的分布式调用追踪

    Cat是美团点评出的一款APM工具,同类的产品也有不少,知名的开源产品如zipkin和pinpoint:国内收费的产品如oneapm.考虑到Cat在互联网公司的应用比较广,因此被纳入选型队列,我也有幸 ...

  7. HTML5知识点总结

    HTML5知识点总结(一) 一.HTML新增元素 1.IE9版本以下支持HTML5的方法 <!--[if lt IE9]> <script src="http://cdn. ...

  8. salesforce 零基础学习(五十九)apex:param使用以及相关的疑惑

    做web项目难免要从一个页面传参数,解析参数中的值进行相关处理以后跳转到其他页面,VF中也不例外.使用传参的标签为apex:param. apex:param标签不可以单独使用,需要作为子标签嵌套在相 ...

  9. 【项目记录】-液化气配送app android版

    15年底参与过甲方呼叫中心平台开发.液化气配送app android版要求1个月开发完成.开发此项目以前我只有过一周android的开发经验.(与甲方签署过保密协议,遵循职业道德有些敏感信息不能写.) ...

  10. window.name实现的跨域数据传输

    这篇文章是对 JavaScript跨域总结与解决办法 的补充. 有三个页面: a.com/app.html:应用页面. a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件 ...