Android(安卓)TextView自动换行文字排版参差不齐的原因及解决办法
今天项目没什么进展,公司后台出问题了。看了下刚刚学习Android时的笔记,发现TextView会自动换行,而且排版文字参差不齐。查了下资料,总结原因如下:
1、半角字符与全角字符混乱所致:这种情况一般就是汉字与数字、英文字母混用
解决方法一:
将textview中的字符全角化。即将所有的数字、字母及标点全部转为全角字符,使它们与汉字同占两个字节,这样就可以避免由于占位导致的排版混乱问题了。半角转为全角的代码如下,只需调用即可。
- publicstaticStringToDBC(Stringinput){
- char[]c=input.toCharArray();
- for(inti=0;i<c.length;i++){
- if(c[i]==12288){
- c[i]=(char)32;
- continue;
- }if(c[i]>65280&&c[i]<65375)
- c[i]=(char)(c[i]-65248);
- }
- returnnewString(c);
- }
解决方法二:
去除特殊字符或将所有中文标号替换为英文标号。利用正则表达式将所有特殊字符过滤,或利用replaceAll()将中文标号替换为英文标号。则转化之后,则可解决排版混乱问题。
Java代码
- //替换、过滤特殊字符
- publicstaticStringStringFilter(Stringstr)throwsPatternSyntaxException{
- str=str.replaceAll("【","[").replaceAll("】","]").replaceAll("!","!");//替换中文标号
- StringregEx="[『』]";//清除掉特殊字符
- Patternp=Pattern.compile(regEx);
- Matcherm=p.matcher(str);
- returnm.replaceAll("").trim();
- }
2、TextView在显示中文的时候标点符号不能显示在一行的行首和行尾,如果一个标点符号刚好在一行的行尾,该标点符号就会连同前一个字符跳到下一行显示。
解决方法:在标点符号后加一个空格。
3、一个英文单词不能被显示在两行中( TextView在显示英文时,标点符号是可以放在行尾的,但英文单词也不能分开 )。
4、如果要两行对其的显示效果:有两种方法
方法一:
修改Android源代码;将frameworks/base/core/java/android/text下的StaticLayout.java文件中的如下代码:
Java代码
- 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;
- }
去掉就可以了。去掉后标点符号可以显示在行首和行尾,英文单词也可以被分开在两行中显示。
方法二:
自定义View显示文本
网上就有达人采用自定义View来解决这个问题,我做了实验并总结了一下:
自定义View的步骤:
1)继承View类或其子类,例子继承了TextView类;
2)写构造函数,通过XML获取属性(这一步中可以自定义属性,见例程);
3)重写父类的某些函数,一般都是以on开头的函数,例子中重写了onDraw()和onMeasure()函数;
=========================CYTextView.java=============================
Java代码
- publicclassCYTextViewextendsTextView{
- publicstaticintm_iTextHeight;//文本的高度
- publicstaticintm_iTextWidth;//文本的宽度
- privatePaintmPaint=null;
- privateStringstring="";
- privatefloatLineSpace=0;//行间距
- publicCYTextView(Contextcontext,AttributeSetset)
- {
- super(context,set);
- TypedArraytypedArray=context.obtainStyledAttributes(set,R.styleable.CYTextView);
- intwidth=typedArray.getInt(R.styleable.CYTextView_textwidth,320);
- floattextsize=typedArray.getDimension(R.styleable.CYTextView_textSize,24);
- inttextcolor=typedArray.getColor(R.styleable.CYTextView_textColor,-1442840576);
- floatlinespace=typedArray.getDimension(R.styleable.CYTextView_lineSpacingExtra,15);
- inttypeface=typedArray.getColor(R.styleable.CYTextView_typeface,0);
- typedArray.recycle();
- //设置CYTextView的宽度和行间距www.linuxidc.com
- m_iTextWidth=width;
- LineSpace=linespace;
- //构建paint对象
- mPaint=newPaint();
- mPaint.setAntiAlias(true);
- mPaint.setColor(textcolor);
- mPaint.setTextSize(textsize);
- switch(typeface){
- case0:
- mPaint.setTypeface(Typeface.DEFAULT);
- break;
- case1:
- mPaint.setTypeface(Typeface.SANS_SERIF);
- break;
- case2:
- mPaint.setTypeface(Typeface.SERIF);
- break;
- case3:
- mPaint.setTypeface(Typeface.MONOSPACE);
- break;
- default:
- mPaint.setTypeface(Typeface.DEFAULT);
- break;
- }
- }
- @Override
- protectedvoidonDraw(Canvascanvas)
- {
- super.onDraw(canvas);
- charch;
- intw=0;
- intistart=0;
- intm_iFontHeight;
- intm_iRealLine=0;
- intx=2;
- inty=30;
- Vectorm_String=newVector();
- FontMetricsfm=mPaint.getFontMetrics();
- m_iFontHeight=(int)Math.ceil(fm.descent-fm.top)+(int)LineSpace;//计算字体高度(字体高度+行间距)
- for(inti=0;i<string.length();i++)
- {
- ch=string.charAt(i);
- float[]widths=newfloat[1];
- Stringsrt=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(inti=0,j=0;i<m_iRealLine;i++,j++)
- {
- canvas.drawText((String)(m_String.elementAt(i)),x,y+m_iFontHeight*j,mPaint);
- }
- }
- protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)
- {
- intmeasuredHeight=measureHeight(heightMeasureSpec);
- intmeasuredWidth=measureWidth(widthMeasureSpec);
- this.setMeasuredDimension(measuredWidth,measuredHeight);
- this.setLayoutParams(newLinearLayout.LayoutParams(measuredWidth,measuredHeight));
- super.onMeasure(widthMeasureSpec,heightMeasureSpec);
- }
- privateintmeasureHeight(intmeasureSpec)
- {
- intspecMode=MeasureSpec.getMode(measureSpec);
- intspecSize=MeasureSpec.getSize(measureSpec);
- //Defaultsizeifnolimitsarespecified.
- initHeight();
- intresult=m_iTextHeight;
- if(specMode==MeasureSpec.AT_MOST){
- //Calculatetheidealsizeofyour
- //controlwithinthismaximumsize.
- //Ifyourcontrolfillstheavailable
- //spacereturntheouterbound.
- result=specSize;
- }elseif(specMode==MeasureSpec.EXACTLY){
- //Ifyourcontrolcanfitwithintheseboundsreturnthatvalue.
- result=specSize;
- }
- returnresult;
- }
- privatevoidinitHeight()
- {
- //设置CYTextView的初始高度为0
- m_iTextHeight=0;
- //大概计算CYTextView所需高度
- FontMetricsfm=mPaint.getFontMetrics();
- intm_iFontHeight=(int)Math.ceil(fm.descent-fm.top)+(int)LineSpace;
- intline=0;
- intistart=0;
- intw=0;
- for(inti=0;i<string.length();i++)
- {
- charch=string.charAt(i);
- float[]widths=newfloat[1];
- Stringsrt=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;
- }
- privateintmeasureWidth(intmeasureSpec)
- {
- intspecMode=MeasureSpec.getMode(measureSpec);
- intspecSize=MeasureSpec.getSize(measureSpec);
- //Defaultsizeifnolimitsarespecified.
- intresult=500;
- if(specMode==MeasureSpec.AT_MOST){
- //Calculatetheidealsizeofyourcontrol
- //withinthismaximumsize.
- //Ifyourcontrolfillstheavailablespace
- //returntheouterbound.
- result=specSize;
- }elseif(specMode==MeasureSpec.EXACTLY){
- //Ifyourcontrolcanfitwithintheseboundsreturnthatvalue.
- result=specSize;
- }
- returnresult;
- }
- publicvoidSetText(Stringtext)(//注:此函数目前只有在UI线程中调用才可以把文本画出来,在其它线程中<p>//无法画文本,找了好久找不到原因,求高手解答)
- {
- string=text;
- //requestLayout();
- //invalidate();
- }
- }</p>
=======================attrs.xml===============================
该文件是自定义的属性,放在工程的res/values下
Java代码
- <resources>
- <attrname="textwidth"format="integer"/>
- <attrname="typeface">
- <enumname="normal"value="0"/>
- <enumname="sans"value="1"/>
- <enumname="serif"value="2"/>
- <enumname="monospace"value="3"/>
- </attr>
- <declare-styleablename="CYTextView">
- <attrname="textwidth"/>
- <attrname="textSize"format="dimension"/>
- <attrname="textColor"format="reference|color"/>
- <attrname="lineSpacingExtra"format="dimension"/>
- <attrname="typeface"/>
- </declare-styleable>
- </resources>
=======================main.xml==========================
Java代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <ScrollView
- xmlns:Android="http://schemas.android.com/apk/res/android"
- Android:layout_width="320px"
- Android:layout_height="320px"
- Android:background="#ffffffff"
- >
- <LinearLayout
- xmlns:Android="http://schemas.android.com/apk/res/android"
- 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=============================
Java代码
- publicclassMainextendsActivity{
- CYTextViewmCYTextView;
- Stringtext="Android提供了精巧和有力的组件化模型构建用户的UI部分。主要是基于布局类:View和ViewGroup。在此基础上,android平台提供了大量的预制的View和xxxViewGroup子类,即布局(layout)和窗口小部件(widget)。可以用它们构建自己的UI。";
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- this.setContentView(R.layout.main);
- mCYTextView=(CYTextView)findViewById(R.id.mv);
- mCYTextView.SetText(text);
- }
- }
更多相关文章
- 【Bugly干货分享】手把手教你逆向分析 Android(安卓)程序
- Flutter调用Android和iOS原生代码的方法示例
- Android(安卓)sdk 简单的客户端源代码
- 破解某国外收费的RTMP Client并成功在Android和Java上调用
- Android(安卓)Studio 和 Unity 之间实现代码交互aar模式
- Android(安卓)弹无虚发之第二弹:Android(安卓)ActionBar 的其它用
- android 代码设置、打开wifi热点及热点的连接
- Android项目源码质量检测
- Android逆向之旅---静态分析技术来破解Apk