分享

Android TextView 自动换行,每行排满的问题

 gearss 2016-05-29

编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识、前端、后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过!

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

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

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

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

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

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

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

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

2)自定义View显示文本

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

自定义View的步骤:

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

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

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

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

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

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

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

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

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

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

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

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

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


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多