zoukankan      html  css  js  c++  java
  • android textview 自动换行 整齐排版

    一、问题在哪里?

    textview显示长文字时会进行自动折行,如果遇到一些特殊情况,自动折行会杯具成这个样子:

    上述特殊情况包括:

    1)全角/半角符号混排(一般是数字、字母、汉字混排)

    2)全角/半角标点符号出现在行首时,该标点符号会连同其前一个字符跳到下一行

    3)英文单词不能被折成两行

    4)......

    二、怎么搞?

    通常有两类解决方案:

    1)修改文本内容,将所有符号全角化、在标点符号前面加空格等等……

    2)保持文本内容不变,在合适的位置将文本手动分成多行

    本文采用第二种方案,更加通用,也最大限度的保留了原文本。

     [转载请保留本文地址:http://www.cnblogs.com/snser/p/5159125.html]

    三、开始干活

    3.1  “在合适的位置将文本手动分成多行”需要知道textview的实际宽度、字体大小等信息,框架如下:

     1 public class TestCActivity extends Activity {
     2     private TextView mText;
     3     
     4     @Override
     5     protected void onCreate(Bundle savedInstanceState) {
     6         super.onCreate(savedInstanceState);
     7         
     8         setContentView(R.layout.testc);
     9         
    10         mText = (TextView)findViewById(R.id.txt);
    11         mText.setText("本文地址http://www.cnblogs.com/goagent/p/5159125.html本文地址啊本文。地址。啊http://www.cnblogs.com/goagent/p/5159125.html");
    12         mText.getViewTreeObserver().addOnGlobalLayoutListener(new OnTvGlobalLayoutListener());
    13     }
    14 
    15     private class OnTvGlobalLayoutListener implements OnGlobalLayoutListener {
    16         @Override
    17         public void onGlobalLayout() {
    18             mText.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    19             final String newText = autoSplitText(mText);
    20             if (!TextUtils.isEmpty(newText)) {
    21                 mText.setText(newText);
    22             }
    23         }
    24     }
    25     
    26     private String autoSplitText(final TextView tv) {
    27         final String rawText = tv.getText().toString();
    28         final Paint tvPaint = tv.getPaint();
    29         final int tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight();
    30         
    31         //autoSplitText begin....
    32         String newText = rawText;
    33         //autoSplitText end....
    34         
    35         return newText;
    36     }
    37 }

    3.2  实现自动分割文本,简单来说就是用textview的paint逐字符测量,如果发现当前行绘制不下了,就手动加入一个换行符: 

     1     private String autoSplitText(final TextView tv) {
     2         final String rawText = tv.getText().toString(); //原始文本
     3         final Paint tvPaint = tv.getPaint(); //paint,包含字体等信息
     4         final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控件可用宽度
     5         
     6         //将原始文本按行拆分
     7         String [] rawTextLines = rawText.replaceAll("
    ", "").split("
    ");
     8         StringBuilder sbNewText = new StringBuilder();
     9         for (String rawTextLine : rawTextLines) {
    10             if (tvPaint.measureText(rawTextLine) <= tvWidth) {
    11                 //如果整行宽度在控件可用宽度之内,就不处理了
    12                 sbNewText.append(rawTextLine);
    13             } else {
    14                 //如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
    15                 float lineWidth = 0;
    16                 for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) {
    17                     char ch = rawTextLine.charAt(cnt);
    18                     lineWidth += tvPaint.measureText(String.valueOf(ch));
    19                     if (lineWidth <= tvWidth) {
    20                         sbNewText.append(ch);
    21                     } else {
    22                         sbNewText.append("
    ");
    23                         lineWidth = 0;
    24                         --cnt;
    25                     }
    26                 }
    27             }
    28             sbNewText.append("
    ");
    29         }
    30         
    31         //把结尾多余的
    去掉
    32         if (!rawText.endsWith("
    ")) {
    33             sbNewText.deleteCharAt(sbNewText.length() - 1);
    34         }
    35         
    36         return sbNewText.toString();
    37     }

    3.3  话不多说,效果如下:

     [转载请保留本文地址:http://www.cnblogs.com/snser/p/5159125.html]

    四、更多玩法

    4.1  可以封装一个自定义的textview,直接包含自动排版换行的功能:

     1 package cc.snser.test;
     2 
     3 import android.content.Context;
     4 import android.graphics.Paint;
     5 import android.text.TextUtils;
     6 import android.util.AttributeSet;
     7 import android.widget.TextView;
     8 
     9 public class AutoSplitTextView extends TextView {
    10     private boolean mEnabled = true;
    11 
    12     public AutoSplitTextView(Context context) {
    13         super(context);
    14     }
    15 
    16     public AutoSplitTextView(Context context, AttributeSet attrs) {
    17         super(context, attrs);
    18     }
    19 
    20     public AutoSplitTextView(Context context, AttributeSet attrs, int defStyle) {
    21         super(context, attrs, defStyle);
    22     }
    23     
    24     public void setAutoSplitEnabled(boolean enabled) {
    25         mEnabled = enabled;
    26     }
    27     
    28     @Override
    29     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    30         if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY 
    31             && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
    32             && getWidth() > 0 
    33             && getHeight() > 0
    34             && mEnabled) {
    35             String newText = autoSplitText(this);
    36             if (!TextUtils.isEmpty(newText)) {
    37                 setText(newText);
    38             }
    39         }
    40         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    41     }
    42     
    43     private String autoSplitText(final TextView tv) {
    44         final String rawText = tv.getText().toString(); //原始文本
    45         final Paint tvPaint = tv.getPaint(); //paint,包含字体等信息
    46         final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控件可用宽度
    47         
    48         //将原始文本按行拆分
    49         String [] rawTextLines = rawText.replaceAll("
    ", "").split("
    ");
    50         StringBuilder sbNewText = new StringBuilder();
    51         for (String rawTextLine : rawTextLines) {
    52             if (tvPaint.measureText(rawTextLine) <= tvWidth) {
    53                 //如果整行宽度在控件可用宽度之内,就不处理了
    54                 sbNewText.append(rawTextLine);
    55             } else {
    56                 //如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
    57                 float lineWidth = 0;
    58                 for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) {
    59                     char ch = rawTextLine.charAt(cnt);
    60                     lineWidth += tvPaint.measureText(String.valueOf(ch));
    61                     if (lineWidth <= tvWidth) {
    62                         sbNewText.append(ch);
    63                     } else {
    64                         sbNewText.append("
    ");
    65                         lineWidth = 0;
    66                         --cnt;
    67                     }
    68                 }
    69             }
    70             sbNewText.append("
    ");
    71         }
    72         
    73         //把结尾多余的
    去掉
    74         if (!rawText.endsWith("
    ")) {
    75             sbNewText.deleteCharAt(sbNewText.length() - 1);
    76         }
    77         
    78         return sbNewText.toString();
    79     }
    80 }
    View AutoSplitTextView.java
     1 package cc.snser.test;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 
     6 public class TestCActivity extends Activity {
     7     private AutoSplitTextView mText;
     8     
     9     @Override
    10     protected void onCreate(Bundle savedInstanceState) {
    11         super.onCreate(savedInstanceState);
    12         
    13         setContentView(R.layout.testc);
    14         
    15         mText = (AutoSplitTextView)findViewById(R.id.txt);
    16         mText.setText("本文地址http://www.cnblogs.com/goagent/p/5159125.html本文地址啊本文。地址。啊http://www.cnblogs.com/goagent/p/5159125.html");
    17     }
    18 }
    View TestCActivity.java
     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:background="@android:color/white"
     6     android:orientation="vertical" >
     7 
     8     <cc.snser.test.AutoSplitTextView
     9         android:id="@+id/txt"
    10         android:layout_width="match_parent"
    11         android:layout_height="200dp"
    12         android:layout_marginTop="11dp"
    13         android:layout_marginLeft="11dp"
    14         android:layout_marginRight="11dp"
    15         android:background="@android:color/holo_blue_light"
    16         android:textSize="20sp"
    17         android:textColor="@android:color/black" />
    18     
    19 </LinearLayout>
    View testc.xml

    4.2  实现悬挂缩进 

     1     private String autoSplitText(final TextView tv, final String indent) {
     2         final String rawText = tv.getText().toString(); //原始文本
     3         final Paint tvPaint = tv.getPaint(); //paint,包含字体等信息
     4         final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控件可用宽度
     5         
     6         //将缩进处理成空格
     7         String indentSpace = "";
     8         float indentWidth = 0;
     9         if (!TextUtils.isEmpty(indent)) {
    10             float rawIndentWidth = tvPaint.measureText(indent);
    11             if (rawIndentWidth < tvWidth) {
    12                 while ((indentWidth = tvPaint.measureText(indentSpace)) < rawIndentWidth) {
    13                     indentSpace += " ";
    14                 }
    15             }
    16         }
    17         
    18         //将原始文本按行拆分
    19         String [] rawTextLines = rawText.replaceAll("
    ", "").split("
    ");
    20         StringBuilder sbNewText = new StringBuilder();
    21         for (String rawTextLine : rawTextLines) {
    22             if (tvPaint.measureText(rawTextLine) <= tvWidth) {
    23                 //如果整行宽度在控件可用宽度之内,就不处理了
    24                 sbNewText.append(rawTextLine);
    25             } else {
    26                 //如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
    27                 float lineWidth = 0;
    28                 for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) {
    29                     char ch = rawTextLine.charAt(cnt);
    30                     //从手动换行的第二行开始,加上悬挂缩进
    31                     if (lineWidth < 0.1f && cnt != 0) {
    32                         sbNewText.append(indentSpace);
    33                         lineWidth += indentWidth;
    34                     }
    35                     lineWidth += tvPaint.measureText(String.valueOf(ch));
    36                     if (lineWidth <= tvWidth) {
    37                         sbNewText.append(ch);
    38                     } else {
    39                         sbNewText.append("
    ");
    40                         lineWidth = 0;
    41                         --cnt;
    42                     }
    43                 }
    44             }
    45             sbNewText.append("
    ");
    46         }
    47         
    48         //把结尾多余的
    去掉
    49         if (!rawText.endsWith("
    ")) {
    50             sbNewText.deleteCharAt(sbNewText.length() - 1);
    51         }
    52         
    53         return sbNewText.toString();
    54     }
    View Code

    调用方式:

     autoSplitText(tv, "1、"); 

    悬挂缩进效果:

     [转载请保留本文地址:http://www.cnblogs.com/snser/p/5159125.html]

  • 相关阅读:
    git使用总结
    [置顶] 生成学习算法、高斯判别分析、朴素贝叶斯、Laplace平滑——斯坦福ML公开课笔记5
    Java调用cmd压缩文件
    Linux下精确控制时间的函数
    ZTESoft 持续集成 编年史 之 持续集成探索---平台选择
    graylog2+syslog-ng+mongodb构建集中管理日志服务器 --转载
    Active Object 并发模式在 Java 中的应用--转载
    分布式计算的模式语言读后感--基线架构
    Spring 中JCA CCI分析--转载
    Getting started with Apache Camel--转载
  • 原文地址:https://www.cnblogs.com/snser/p/5159125.html
Copyright © 2011-2022 走看看