TextView的自动换行问题

Android的TextView在显示文字的时候有个问题就是一行还没显示满就跳到下一行,原因是:

1) TextView在显示中文的时候 标点符号不能显示在一行的行首和行尾,如果一个标点符号刚好在一行的行尾,该标点符号就会连同前一个字符跳到下一行显示;

2)一个英文单词不能被显示在两行中( TextView在显示英文时,标点符号是可以放在行尾的,但英文单词也不能分开 );

如果只是想让标点符号可以显示在行尾,有一个简单的方法就是在标点符号后加一个空格,则该标点符号就可以显示在行尾了;

如果想要两端对齐的显示效果,有两种方法:

1)修改Android源代码;将frameworks/base/core/Java/android/text下的StaticLayout.java文件中的如下代码:

[java]  view plain  copy
  1. if (c == ' ' || c == '/t' ||  
  2. ((c == '.' || c == ',' || c == ':' || c == ';') &&  
  3. (j - 1 < here || !Character.isDigit(chs[j - 1 - start])) &&  
  4. (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||  
  5. ((c == '/' || c == '-') &&  
  6. (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||  
  7. (c >= FIRST_CJK && isIdeographic(c, true) &&  
  8. j + 1 < next && isIdeographic(chs[j + 1 - start], false))) {  
  9. okwidth = w;  
  10. ok = j + 1;  
  11.   
  12. if (fittop < oktop)  
  13. oktop = fittop;  
  14. if (fitascent < okascent)  
  15. okascent = fitascent;  
  16. if (fitdescent > okdescent)  
  17. okdescent = fitdescent;  
  18. if (fitbottom > okbottom)  
  19. okbottom = fitbottom;  
  20. }   


 

去掉就可以了。去掉后标点符号可以显示在行首和行尾,英文单词也可以被分开在两行中显示。

2)自定义View显示文本

网上就有达人采用自定义View来解决这个问题,我做了实验并总结了一下:

自定义View的步骤:

1)继承View类或其子类,例子继承了TextView类;

2)写构造函数,通过XML获取属性(这一步中可以自定义属性,见例程);

3)重写父类的某些函数,一般都是以on开头的函数,例子中重写了onDraw()和onMeasure()函数;

=========================StartCustomTextView.java=============================

[java]  view plain  copy
  1. public class StartCustomTextView extends TextView {  
  2.     public  static  int m_iTextHeight; //文本的高度  
  3.     public  static  int m_iTextWidth;//文本的宽度  
  4.       
  5.     private Paint mPaint = null;  
  6.     private String string="";  
  7.     private float LineSpace = 0;//行间距  
  8.     private int left_Margin;  
  9.     private int right_Margin;  
  10.     private int bottom_Margin;  
  11.           
  12.     public StartCustomTextView(Context context, AttributeSet set)   
  13.     {         
  14.         super(context,set);    
  15.         DisplayMetrics displayMetrics = getResources().getDisplayMetrics();  
  16.         TypedArray typedArray = context.obtainStyledAttributes(set, R.styleable.CYTextView);  
  17.         int width = displayMetrics.widthPixels;  
  18.         left_Margin = 29;  
  19.         right_Margin = 29;  
  20.         bottom_Margin = 29;  
  21.         width = width - left_Margin -right_Margin;  
  22.         float textsize = typedArray.getDimension(R.styleable.CYTextView_textSize, 34);  
  23.         int textcolor = typedArray.getColor(R.styleable.CYTextView_textColor, getResources().getColor(R.color.white));  
  24.         float linespace = typedArray.getDimension(R.styleable.CYTextView_lineSpacingExtra, 15);  
  25.         int typeface = typedArray.getColor(R.styleable.CYTextView_typeface, 0);  
  26.           
  27.         typedArray.recycle();  
  28.           
  29.         //设置 CY TextView的宽度和行间距www.linuxidc.com  
  30.         m_iTextWidth=width;  
  31.         LineSpace=linespace;  
  32.           
  33.         // 构建paint对象        
  34.         mPaint = new Paint();  
  35.         mPaint.setAntiAlias(true);  
  36.         mPaint.setColor(textcolor);  
  37.         mPaint.setTextSize(textsize);  
  38.         switch(typeface){  
  39.         case 0:  
  40.             mPaint.setTypeface(Typeface.DEFAULT);  
  41.             break;  
  42.         case 1:  
  43.             mPaint.setTypeface(Typeface.SANS_SERIF);  
  44.             break;  
  45.         case 2:  
  46.             mPaint.setTypeface(Typeface.SERIF);  
  47.             break;  
  48.         case 3:  
  49.             mPaint.setTypeface(Typeface.MONOSPACE);  
  50.             break;  
  51.         default:  
  52.             mPaint.setTypeface(Typeface.DEFAULT);      
  53.             break;  
  54.         }  
  55.           
  56.     }  
  57.     
  58.     @Override  
  59.     protected void onDraw(Canvas canvas)   
  60.     {    
  61.        super.onDraw(canvas);         
  62.           
  63.         char ch;  
  64.         int w = 0;  
  65.         int istart = 0;  
  66.         int m_iFontHeight;  
  67.         int m_iRealLine=0;  
  68.         int x=2;  
  69.         int y=30;  
  70.           
  71.         Vector   m_String=new Vector();  
  72.           
  73.         FontMetrics fm = mPaint.getFontMetrics();          
  74.         m_iFontHeight = (int) Math.ceil(fm.descent - fm.top) + (int)LineSpace;//计算字体高度(字体高度+行间距)  
  75.   
  76.         for (int i = 0; i < string.length(); i++)  
  77.         {  
  78.             ch = string.charAt(i);  
  79.             float[] widths = new float[1];  
  80.             String srt = String.valueOf(ch);  
  81.             mPaint.getTextWidths(srt, widths);  
  82.   
  83.             if (ch == '\n'){  
  84.                 m_iRealLine++;  
  85.                 m_String.addElement(string.substring(istart, i));  
  86.                 istart = i + 1;  
  87.                 w = 0;  
  88.             }else{  
  89.                 w += (int) (Math.ceil(widths[0]));  
  90.                 if (w > m_iTextWidth){  
  91.                     m_iRealLine++;  
  92.                     m_String.addElement(string.substring(istart, i));  
  93.                     istart = i;  
  94.                     i--;  
  95.                     w = 0;  
  96.                 }else{  
  97.                     if (i == (string.length() - 1)){  
  98.                         m_iRealLine++;  
  99.                         m_String.addElement(string.substring(istart, string.length()));  
  100.                     }  
  101.                 }  
  102.             }  
  103.         }  
  104.         m_iTextHeight=m_iRealLine*m_iFontHeight+2;  
  105.         canvas.setViewport(m_iTextWidth, m_iTextWidth);  
  106.         for (int i = 0, j = 0; i < m_iRealLine; i++, j++)  
  107.         {  
  108.             canvas.drawText((String)(m_String.elementAt(i)), x,  y+m_iFontHeight * j, mPaint);  
  109.         }  
  110.     }    
  111.      
  112.       
  113.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)   
  114.     {            
  115.         int measuredHeight = measureHeight(heightMeasureSpec);            
  116.         int measuredWidth = measureWidth(widthMeasureSpec);             
  117.         this.setMeasuredDimension(measuredWidth, measuredHeight);  
  118.         LayoutParams layout = new LinearLayout.LayoutParams(measuredWidth,measuredHeight);  
  119.         layout.leftMargin= left_Margin;  
  120.         layout.rightMargin= right_Margin;  
  121.         layout.bottomMargin= bottom_Margin;  
  122.         this.setLayoutParams(layout);  
  123.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  124.     }    
  125.                    
  126.     private int measureHeight(int measureSpec)   
  127.     {    
  128.         int specMode = MeasureSpec.getMode(measureSpec);            
  129.         int specSize = MeasureSpec.getSize(measureSpec);                     
  130.         // Default size if no limits are specified.    
  131.         initHeight();  
  132.         int result = m_iTextHeight;            
  133.         if (specMode == MeasureSpec.AT_MOST){           
  134.             // Calculate the ideal size of your            
  135.             // control within this maximum size.            
  136.             // If your control fills the available             
  137.             // space return the outer bound.            
  138.             result = specSize;             
  139.         }else if (specMode == MeasureSpec.EXACTLY){             
  140.             // If your control can fit within these bounds return that value.              
  141. //            result = specSize;             
  142.         }       
  143.         return result;              
  144.     }  
  145.       
  146.     private void initHeight()  
  147.     {  
  148.         //设置 CY TextView的初始高度为0  
  149.         m_iTextHeight=0;  
  150.           
  151.         //大概计算 CY TextView所需高度  
  152.         FontMetrics fm = mPaint.getFontMetrics();          
  153.         int m_iFontHeight = (int) Math.ceil(fm.descent - fm.top) + (int)LineSpace;  
  154.         int line=0;  
  155.         int istart=0;  
  156.           
  157.         int w=0;  
  158.         for (int i = 0; i < string.length(); i++)  
  159.         {  
  160.             char ch = string.charAt(i);  
  161.             float[] widths = new float[1];  
  162.             String srt = String.valueOf(ch);  
  163.             mPaint.getTextWidths(srt, widths);  
  164.   
  165.             if (ch == '\n'){  
  166.                 line++;  
  167.                 istart = i + 1;  
  168.                 w = 0;  
  169.             }else{  
  170.                 w += (int) (Math.ceil(widths[0]));  
  171.                 if (w > m_iTextWidth){  
  172.                     line++;  
  173.                     istart = i;  
  174.                     i--;  
  175.                     w = 0;  
  176.                 }else{  
  177.                     if (i == (string.length() - 1)){  
  178.                         line++;  
  179.                     }  
  180.                 }  
  181.             }  
  182.         }  
  183.         m_iTextHeight=(line)*m_iFontHeight+2;  
  184.     }  
  185.                    
  186.     private int measureWidth(int measureSpec)   
  187.     {    
  188.         int specMode = MeasureSpec.getMode(measureSpec);             
  189.         int specSize = MeasureSpec.getSize(measureSpec);               
  190.   
  191.         // Default size if no limits are specified.            
  192.         int result = 500;            
  193.         if (specMode == MeasureSpec.AT_MOST){            
  194.             // Calculate the ideal size of your control             
  195.             // within this maximum size.           
  196.             // If your control fills the available space           
  197.             // return the outer bound.           
  198.             result = specSize;            
  199.         }else if (specMode == MeasureSpec.EXACTLY){             
  200.             // If your control can fit within these bounds return that value.             
  201.             result = specSize;              
  202.         }             
  203.         return result;            
  204.     }  
  205.       
  206.     public void SetText(String text)  
  207.     {  
  208.         string = text;  
  209.        // requestLayout();  
  210.        // invalidate();  
  211.     }     
  212. }   


 

=======================attrs.xml===============================

该文件是自定义的属性,放在工程的res/values下

[html]  view plain  copy
  1. <resources>  
  2. <attr name="textwidth" format="integer"/>  
  3. <attr name="typeface">  
  4. <enum name="normal" value="0"/>  
  5. <enum name="sans" value="1"/>  
  6. <enum name="serif" value="2"/>  
  7. <enum name="monospace" value="3"/>  
  8. </attr>  
  9.   
  10. <declare-styleable name="CYTextView">   
  11. <attr name="textwidth" />   
  12. <attr name="textSize" format="dimension"/>  
  13. <attr name="textColor" format="reference|color"/>  
  14. <attr name="lineSpacingExtra" format="dimension"/>  
  15. <attr name="typeface" />  
  16. </declare-styleable>  
  17. </resources>   


=======================main.xml==========================

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ScrollView  
  3. xmlns:Android="http://schemas.android.com/apk/res/android"  
  4. Android:layout_width="320px"  
  5. Android:layout_height="320px"  
  6. Android:background="#ffffffff"  
  7. >  
  8. <LinearLayout   
  9. xmlns:Android="http://schemas.android.com/apk/res/android"  
  10. Android:orientation="vertical"  
  11. Android:layout_width="fill_parent"  
  12. Android:layout_height="fill_parent">  
  13. <com.cy.CYTextView.CYTextView   
  14. xmlns:cy="http://schemas.Android.com/apk/res/ com.cy.CYTextView "  
  15. Android:id="@+id/mv"  
  16. Android:layout_height="wrap_content"  
  17. Android:layout_width="wrap_content"   
  18. cy :textwidth="320"   
  19. cy :textSize="24sp"  
  20. cy :textColor="#aa000000"  
  21. cy :lineSpacingExtra="15sp"  
  22. cy :typeface="serif">  
  23. </com. cy .CYTextView.CYTextView>   
  24. </LinearLayout>  
  25. </ScrollView>  

蓝色代码即为自定义View,其中以cy命名空间开头的属性是自定义属性;

=======================Main.java=============================

[java]  view plain  copy
  1. public class Main extends Activity {  
  2. CYTextView mCYTextView;  
  3. String text = "Android提供了精巧和有力的组件化模型构建用户的UI部分。主要是基于布局类:View和 ViewGroup。在此基础上,android平台提供了大量的预制的View和xxxViewGroup子 类,即布局(layout)和窗口小部件(widget)。可以用它们构建自己的UI。";  
  4.   
  5. /** Called when the activity is first created. */  
  6. @Override  
  7. public void onCreate(Bundle savedInstanceState) {  
  8. super.onCreate(savedInstanceState);  
  9. this.setContentView(R.layout.main);  
  10.   
  11. mCYTextView = (CYTextView)findViewById(R.id.mv);  
  12. mCYTextView.SetText(text);  
  13. }  
  14.   
  15. }   


 

转自:http://hi.baidu.com/java_rose/blog/item/2940a030d1ec7f3e96ddd847.html



另外一个人的自定义TextView,学习一下


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("\r", "").split("\n");
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("\n");
65                         lineWidth = 0;
66                         --cnt;
67                     }
68                 }
69             }
70             sbNewText.append("\n");
71         }
72         
73         //把结尾多余的\n去掉
74         if (!rawText.endsWith("\n")) {
75             sbNewText.deleteCharAt(sbNewText.length() - 1);
76         }
77         
78         return sbNewText.toString();
79     }
80 }
复制代码
复制代码
 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 }
复制代码
复制代码
 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>
复制代码

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     }
复制代码

调用方式:

 autoSplitText(tv, "1、"); 

悬挂缩进效果:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值