一、問題在哪裡? textview顯示長文字時會進行自動折行,如果遇到一些特殊情況,自動折行會杯具成這個樣子: 上述特殊情況包括: 1)全形/半形符號混排(一般是數字、字母、漢字混排) 2)全形/半形標點符號出現在行首時,該標點符號會連同其前一個字元跳到下一行 3)英文單詞不能被折成兩行 4)...
一、問題在哪裡?
textview顯示長文字時會進行自動折行,如果遇到一些特殊情況,自動折行會杯具成這個樣子:
上述特殊情況包括:
1)全形/半形符號混排(一般是數字、字母、漢字混排)
2)全形/半形標點符號出現在行首時,該標點符號會連同其前一個字元跳到下一行
3)英文單詞不能被折成兩行
4)......
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5159125.html]
二、怎麼搞?
通常有兩類解決方案:
1)修改文本內容,將所有符號全形化、在標點符號前面加空格等等……
2)保持文本內容不變,在合適的位置將文本手動分成多行
本文采用第二種方案,更加通用,也最大限度的保留了原文本。
[轉載請保留本文地址:http://www.cnblogs.com/goagent/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("\r", "").split("\n"); 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("\n"); 23 lineWidth = 0; 24 --cnt; 25 } 26 } 27 } 28 sbNewText.append("\n"); 29 } 30 31 //把結尾多餘的\n去掉 32 if (!rawText.endsWith("\n")) { 33 sbNewText.deleteCharAt(sbNewText.length() - 1); 34 } 35 36 return sbNewText.toString(); 37 }
3.3 話不多說,效果如下:
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5159125.html]
四、更多玩法
4.1 可以封裝一個自定義的textview,直接包含自動排版換行的功能:
1 import android.content.Context; 2 import android.graphics.Paint; 3 import android.text.TextUtils; 4 import android.util.AttributeSet; 5 import android.widget.TextView; 6 7 public class AutoSplitTextView extends TextView { 8 private boolean mEnabled = true; 9 10 public AutoSplitTextView(Context context) { 11 super(context); 12 } 13 14 public AutoSplitTextView(Context context, AttributeSet attrs) { 15 super(context, attrs); 16 } 17 18 public AutoSplitTextView(Context context, AttributeSet attrs, int defStyle) { 19 super(context, attrs, defStyle); 20 } 21 22 public void setAutoSplitEnabled(boolean enabled) { 23 mEnabled = enabled; 24 } 25 26 @Override 27 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 28 if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY 29 && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY 30 && getWidth() > 0 31 && getHeight() > 0 32 && mEnabled) { 33 String newText = autoSplitText(this); 34 if (!TextUtils.isEmpty(newText)) { 35 setText(newText); 36 } 37 } 38 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 39 } 40 41 private String autoSplitText(final TextView tv) { 42 final String rawText = tv.getText().toString(); //原始文本 43 final Paint tvPaint = tv.getPaint(); //paint,包含字體等信息 44 final float tvWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight(); //控制項可用寬度 45 46 //將原始文本按行拆分 47 String [] rawTextLines = rawText.replaceAll("\r", "").split("\n"); 48 StringBuilder sbNewText = new StringBuilder(); 49 for (String rawTextLine : rawTextLines) { 50 if (tvPaint.measureText(rawTextLine) <= tvWidth) { 51 //如果整行寬度在控制項可用寬度之內,就不處理了 52 sbNewText.append(rawTextLine); 53 } else { 54 //如果整行寬度超過控制項可用寬度,則按字元測量,在超過可用寬度的前一個字元處手動換行 55 float lineWidth = 0; 56 for (int cnt = 0; cnt != rawTextLine.length(); ++cnt) { 57 char ch = rawTextLine.charAt(cnt); 58 lineWidth += tvPaint.measureText(String.valueOf(ch)); 59 if (lineWidth <= tvWidth) { 60 sbNewText.append(ch); 61 } else { 62 sbNewText.append("\n"); 63 lineWidth = 0; 64 --cnt; 65 } 66 } 67 } 68 sbNewText.append("\n"); 69 } 70 71 //把結尾多餘的\n去掉 72 if (!rawText.endsWith("\n")) { 73 sbNewText.deleteCharAt(sbNewText.length() - 1); 74 } 75 76 return sbNewText.toString(); 77 } 78 }View Code
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("\r", "").split("\n"); 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("\n"); 40 lineWidth = 0; 41 --cnt; 42 } 43 } 44 } 45 sbNewText.append("\n"); 46 } 47 48 //把結尾多餘的\n去掉 49 if (!rawText.endsWith("\n")) { 50 sbNewText.deleteCharAt(sbNewText.length() - 1); 51 } 52 53 return sbNewText.toString(); 54 }View Code
調用方式:
autoSplitText(tv, "1、");
懸掛縮進效果:
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5159125.html]