动态自定义textview居中显示文字居中什么方法

2015年7月 移动开发大版内专家分月排行榜第二2015年5月 移动开发大版内专家分月排行榜第二
2015年7月 扩充话题大版内专家分月排行榜第三2015年5月 扩充话题大版内专家分月排行榜第三
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
图1.1&系统TextView的绘制结果
图1.2&期望TextView的绘制结果(重写后的效果)
系统TextView中文换行的大致原理为:
文本绘制绘制到某一行的行尾,并且剩余空间不足一个字符的空间时,将当前要绘制的字符放在下一行进行绘制(因此上一行结尾与该行的右边界有一小段空白)。
文本绘制到某一行的行尾,并且最后一个字符为标点符号时,那么将标点符号连同标点符号的上一个字符放在下一行进行绘制(因此上一行结尾与该行的右边界有稍微大的一段空白)。
所以这种绘制方式,会使TextView右侧的显示文本参差不齐。
二、重写TextView算法的原理
(1)、如何绘制:
对此TextView中的所有字符串使用“canvas.drawText(str, x,y,mPaint)”方法进行一个字、一个字的绘制。
(2)、目标:
主要的目标是解决标点带来的换行参差不齐的问题。
(3)、标点的说明:
这里要说明自己命名的几种标点名称:
单独出现的标点:“,”&“。”&“.”等
左侧标点:“《”“&”“{”等
右侧标点:“》”“&”“}”等
(4)、最初思考要处理的情况和解决方式:
对一行结尾的字符或者下一行开始的字符进行判断。根据标点的不同,分别进行字体间距的拉伸和压缩。
字间距压缩的情况:
正常绘制时(没有字间距的变化),如果一行绘制结束,但是在下一行的开始时,“单独出现的标点符号”被绘制到了该行的开头。即,“,”或者“。”出现在了行首的位置。
这种情况下,将上一行每两个字符之间的字间距进行压缩,使其刚好可以将本行首的标点放在上一行的行首。如图2.1所示。
图2.1&字间距进行压缩
字间距的拉伸的情况:
这种情况下即,“《”、“&”等标点符号出现在一行的行尾位置。那么将该行字符的字间距进行拉伸,使这些标点出现在下一行的行首。如图2.2所示。
图2.2&字间距进行拉伸
(5)、最终的解决方案:
后来发现,只是简单的对“行尾字符”、“下一行的开始字符”进行标点的判断还不够。有的时候,几个标点会连续同时出现。下面举例,如图2.3所示。
图2.3&字间距进行拉伸
因此在进行标点判断时,要对结尾的三个字符进行判断。即,一行的结尾字符、下一行的开始字符、下一行的第二个字符。如图2.4所示。
&图2.4&要进行标点判断的三个字符位置
(6)、实现:
整个算法的实现,就是判断(5)中所介绍的三个字符与标点的对应情况。这种判断方式的实现,如图2.5所示。
&图2.5标点判断的算法实现
三、简单代码的讲解
因为要解决的问题为文本的绘制问题,所以这里只重写了onDraw方法。onDraw方法中:
首先,通过this.getText().toString()方法,获取要绘制的文本字符串;
其次,对字符串进行第一次循环判断,第一次判断是将字符串分行(即获取整个文本中,每一行文本开始的Index和结束Index、该行字体间距拉伸了多少和压缩了多少);
再次,对字符串进行第二次循环,这次循环要做的工作就是根据第一次循环获取的信息进行一个字符一个字符的绘制了。
四、引入的问题
由于自定义TextView中的文本全部都是由“canvas.drawText(str, x,y,mPaint)”方法,一个字符一个字符的进行绘制的。所以当文本数量较大时,对与系统资源的占用较为严重,会造成界面中其他事件的反映较慢。
五、编码是否有影响??
(1)、代码中对于中文标点的判断:
&* Chinese punctuation
public static boolean isPunctuation(char c) {
&&&&&& Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
&&&&&& if (ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
&&&&&&&&&&&&&&&&&&&& || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
&&&&&&&&&&&&&&&&&&&& || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
&&&&&&&&&&&&&
对于中文标点的判断来自于网络
(2)、其他英文标点的判断:
&* English punctuation
public static boolean isHalfPunctuation(char c) {
&&&&&& int count = (int)
&&&&&& if (count &= 33 && count &= 47) {
&&&&&&&&&&&&& // !~/
&&&&&&&&&&&&&
&&&&&& } else if (count &= 58 && count &= 64) {
&&&&&&&&&&&&& // :~@
&&&&&&&&&&&&&
&&&&&& } else if (count &= 91 && count &= 96) {
&&&&&&&&&&&&& // [~
&&&&&&&&&&&&&
&&&&&& } else if (count &= 123 && count &= 126) {
&&&&&&&&&&&&& // {~~
&&&&&&&&&&&&&
这里全部的英文标点是根据“ASCII编码表”&确定的,在不同的编码环境下,这些标点所对应的十进制数值是不变的。
(3)、成对出现标点的判断:
&* the left half of the punctuation . For example:" ( & [ { "
public static boolean isLeftPunctuation(char c) {
&&&&&& int count = (int)
&&&&&& if (count == 8220 || count == 12298 || count == 65288 || count == 12304
&&&&&&&&&&&&&&&&&&&& || count == 40 || count == 60 || count == 91 || count == 123) {
&&&&&&&&&&&&&
&* the right half of the punctuation . For example:" ) & ] } "
public static boolean isRightPunctuation(char c) {
&&&&&& int count = (int)
&&&&&& if (count == 8221 || count == 12299 || count == 65289 || count == 12305
&&&&&&&&&&&&&&&&&&&& || count == 41 || count == 62 || count == 93 || count == 125) {
&&&&&&&&&&&&&
这些成对出现的标点中,编码在0~255之间的是根据“ASCII编码表”&确定;其他的则为相应标点,转化为相应的十进制数值来确定。
当编码发生变化时,对于这些“相应标点,转化为相应十进制数值来确定的标点”是否会有影响?????????
BaikeTextView.java
package com.example.android_test01;
import java.util.ArrayL
import android.content.C
import android.graphics.C
import android.graphics.P
import android.util.AttributeS
import android.util.L
import android.widget.TextV
&* @author xxl
public class BaikeTextView extends TextView {
private String TAG = "BaikeTextView";
public Context mContext =
public Paint mPaint =
public int mTextHeight = 1080;
public int mBaikeTextHeight = 0;
public int mTextWidth = 1920;
public String mText = "";
public float mLineSpace = 15;
public int mOffset = -2;
public float mTextSize = 0;
public int mTextColor = 0
public int mFontHeight = 0;
public int mPaddingLeft = 0;
public int mPaddingRight = 0;
public BaikeTextView(Context context, AttributeSet set) {
super(context, set);
this.mContext =
mPaint = new Paint();
mPaint.setAntiAlias(true);
protected void onDraw(Canvas canvas) {
mTextWidth = this.getWidth();
setmTextHeight(this.getHeight());
mText = this.getText().toString().trim();
mText = getTextString(mContext, mText);
if (mText == null || mText.equals("") == true) {
Log.i(TAG, "mTextStr: " + mText + "");
mTextSize = this.getTextSize();
mFontHeight = (int) mTextS
mPaddingLeft = this.getPaddingLeft();
mPaddingRight = this.getPaddingRight();
mTextColor = this.getCurrentTextColor();
Log.i(TAG, "mTextSize: " + mTextSize + "");
Log.i(TAG, "mFontHeight: " + mFontHeight + "");
Log.i(TAG, "mPaddingLeft: " + mPaddingLeft + "");
Log.i(TAG, "mPaddingRight: " + mPaddingRight + "");
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
ArrayList&LinePar& tempLineArray = getLineParList(mText);
drawText(tempLineArray, mText, canvas);
&* Obtain the information of each row
public ArrayList&LinePar& getLineParList(String mTextStr) {
if (mTextStr == null || mTextStr.isEmpty() == true) {
int tempStart = 0;
int tempLineWidth = 0;
int tempLineCount = 0;
ArrayList&LinePar& tempLineArray = new ArrayList&LinePar&();
//对字符串进行一次循环
for (int i = 0; i & mTextStr.length(); i++) {
char ch = mTextStr.charAt(i);
String str = String.valueOf(ch);
float strWidth = 0;
if (str != null && str.isEmpty() == false) {
strWidth = BaikeConstant.getWidthofString(str, mPaint);
&* 如果是换行符,将这一行的信息存入列表中
if (ch == '\n' && tempStart != i) {
tempLineCount++;
addLinePar(tempStart, i, tempLineCount, 0, tempLineArray);
if (i == (mTextStr.length() - 1)) {
tempStart = i + 1;
tempLineWidth = 0;
tempLineWidth += Math.ceil(strWidth);
if (tempLineWidth &= mTextWidth - mPaddingRight) {
tempLineCount++;
&* 对正常绘制时的“下一行第一个字符”进行判断,如果是“成对出现标点”的左侧半个,
&* 对上一行的字符间距进行拉伸,或者不处理
if (BaikeConstant.isLeftPunctuation(ch) == true) {
Log.i(TAG, "i: " + i + "");
Log.i(TAG,
"the char is the left half of the punctuation");
Log.i(TAG, "str: " + str + " ");
&* if the char is the left half of the punctuation. Go
&* into the next line of the current character
float tempWordSpaceOffset = (float) (tempLineWidth
- Math.ceil(strWidth) - mTextWidth)
/ (float) (i - tempStart);
addLinePar(tempStart, i, tempLineCount,
tempWordSpaceOffset, tempLineArray);
} else if (BaikeConstant.isRightPunctuation(ch) == true) {
&* 对正常绘制时的“下一行第一个字符”进行判断,如果是“成对出现标点”的右侧半个
Log.i(TAG,
"the char is the right half of the punctuation");
Log.i(TAG, "str: " + str + " ");
if (i == (mTextStr.length() - 1)) {
addLinePar(tempStart, i, tempLineCount, 0,
tempLineArray);
char nextChar = mTextStr.charAt(i + 1);
if ((BaikeConstant.isHalfPunctuation(nextChar) == true || BaikeConstant
.isPunctuation(nextChar) == true)
&& BaikeConstant
.isLeftPunctuation(nextChar) == false) {
&* 对正常绘制时的“下一行第一个字符”进行判断,如果是“成对出现标点”的右侧半个
&* 并且,“再下一个字符”是“英文标点”、“中文标点”、“右侧标点”
&* 处理:讲这两个标点都放在上一行进行绘制
String nextStr = String.valueOf(nextChar);
float nextStrWidth = 0;
if (nextStr != null
&& nextStr.isEmpty() == false) {
nextStrWidth = BaikeConstant
.getWidthofString(nextStr, mPaint);
float tempWordSpaceOffset = (float) (tempLineWidth
+ Math.ceil(nextStrWidth) - mTextWidth)
/ (float) (i - tempStart);
addLinePar(tempStart, i, tempLineCount,
tempWordSpaceOffset, tempLineArray);
&* 对正常绘制时的“下一行第一个字符”进行判断,如果是“成对出现标点”的右侧半个
&* 并且,“再下一个字符”是“左侧标点”、非标点的字符
&* 处理:只将右侧标点放在上一行进行绘制
float tempWordSpaceOffset = (float) (tempLineWidth - mTextWidth)
/ (float) (i - tempStart);
addLinePar(tempStart, i, tempLineCount,
tempWordSpaceOffset, tempLineArray);
&* 如果下一行的第一个字符是“单个出现的标点”和“非标点字符”
&* if the char is not the left And Right half of the
&* punctuation.
if (BaikeConstant.isHalfPunctuation(ch) == true
|| BaikeConstant.isPunctuation(ch) == true) {
&* 如果下一行的第一个字符是“单个出现的标点”
&* 放在上一行进行绘制
&* If the current character is a punctuation mark,
&* on the end of the Bank
float tempWordSpaceOffset = (float) (tempLineWidth - mTextWidth)
/ (float) (i - tempStart);
addLinePar(tempStart, i, tempLineCount,
tempWordSpaceOffset, tempLineArray);
&* 如果下一行的第一个字符是“非标点”
&* If the current character is not a punctuation
if (i &= 1) {
char preChar = mTextStr.charAt(i - 1);
if (BaikeConstant.isLeftPunctuation(preChar) == true) {
&* 如果下一行的第一个字符是“非标点”
&* 上一个字符(即结尾的字符),是左侧标点
&* 处理:两个字符全都放在下一行进行绘制
String preStr = String.valueOf(preChar);
float preStrWidth = 0;
if (preStr != null
&& preStr.isEmpty() == false) {
preStrWidth = BaikeConstant
.getWidthofString(preStr,
Log.i(TAG,
"the char is the left half of the punctuation");
Log.i(TAG, "preChar: " + preChar + " ");
i = i - 2;
float tempWordSpaceOffset = (float) (tempLineWidth
- Math.ceil(strWidth)
- Math.ceil(preStrWidth) - mTextWidth)
/ (float) (i - tempStart);
addLinePar(tempStart, i, tempLineCount,
tempWordSpaceOffset, tempLineArray);
&* 如果下一行的第一个字符是“非标点”
&* 上一个字符(即结尾的字符),是“非左侧标点”
&* 处理:下一行的第一个字符放在下一行(即,不处理)
float tempWordSpaceOffset = (float) (tempLineWidth
- Math.ceil(strWidth) - mTextWidth)
/ (float) (i - tempStart);
addLinePar(tempStart, i, tempLineCount,
tempWordSpaceOffset, tempLineArray);
if (i == (mTextStr.length() - 1)) {
tempStart = i + 1;
tempLineWidth = 0;
if (i == (mTextStr.length() - 1)) {
tempLineCount++;
addLinePar(tempStart, i, tempLineCount, 0,
tempLineArray);
return tempLineA
public void addLinePar(int start, int end, int lineCount,
float wordSpaceOffset, ArrayList&LinePar& lineList) {
if (lineList != null) {
LinePar linePar = new LinePar();
linePar.setLineCount(lineCount);
linePar.setStart(start);
linePar.setEnd(end);
linePar.setWordSpaceOffset(wordSpaceOffset);
lineList.add(linePar);
public void drawText(ArrayList&LinePar& tempLineArray, String mTextStr,
Canvas canvas) {
if (tempLineArray == null || canvas == null || mTextStr == null
|| mTextStr.equals("") == true) {
for (int lineNum = 0; lineNum & tempLineArray.size(); lineNum++) {
LinePar linePar = tempLineArray.get(lineNum);
int start = linePar.getStart();
int end = linePar.getEnd();
float width = linePar.getWordSpaceOffset();
int lineCount = linePar.getLineCount();
if (lineNum & 0 && lineNum == tempLineArray.size() - 1) {
mBaikeTextHeight = (int) (lineCount * (mLineSpace + mTextSize));
if (start & end || end & mTextStr.length() - 1) {
float lineWidth = 0;
for (int strNum = strNum &= strNum++) {
char ch = mTextStr.charAt(strNum);
String str = String.valueOf(ch);
if (str == null || str.equals("") == true) {
if (ch == '\n') {
if (strNum & end) {
if (strNum &= start && strNum &= end && lineCount &= 1) {
canvas.drawText(str, mPaddingLeft + lineWidth, lineCount
* mFontHeight - mOffset + (lineCount - 1)
* mLineSpace, mPaint);
lineWidth += BaikeConstant.getWidthofString(str, mPaint);
lineWidth = lineWidth -
public int getBaikeTextHeight() {
return mBaikeTextH
public String getTextString(Context mContext, String mText) {
if (mContext != null && mText != null && mText.equals("") == false) {
return BaikeConstant.replaceTABToSpace(mText);
return "";
public void setmTextHeight(int mTextHeight) {
this.mTextHeight = mTextH
public int getmTextHeight() {
return mTextH
public class LinePar {
private int mS
private int mE
private int mLineC
private float mWordSpaceO
public void setStart(int mStart) {
this.mStart = mS
public void setEnd(int mEnd) {
this.mEnd = mE
public void setLineCount(int count) {
this.mLineCount =
public void setWordSpaceOffset(float mWordSpaceOffset) {
this.mWordSpaceOffset = mWordSpaceO
public int getStart() {
public int getEnd() {
public int getLineCount() {
return mLineC
public float getWordSpaceOffset() {
return mWordSpaceO
BaikeConstant.java
package com.example.android_test01;
import android.graphics.P
&* @author xxl
public class BaikeConstant {
&* replace TAB
public static String replaceTABToSpace(String str) {
str = str.replaceAll(" ", " & & &");
public static String replaceBreakLineToSpace(String str) {
str = str.replaceAll("\n", " ");
&* Letters and numbers
public static boolean isLetterOfEnglish(char c) {
int count = (int)
if (count &= 65 && count &= 90) {
} else if (count &= 97 && count &= 122) {
} else if (count &= 48 && count &= 57) {
&* English punctuation
public static boolean isHalfPunctuation(char c) {
int count = (int)
if (count &= 33 && count &= 47) {
} else if (count &= 58 && count &= 64) {
} else if (count &= 91 && count &= 96) {
} else if (count &= 123 && count &= 126) {
&* Chinese punctuation
public static boolean isPunctuation(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
&* Get the pixel length of the string value
public static int getWidthofString(String str, Paint paint) {
if (str != null && str.equals("") == false && paint != null) {
int strLength = str.length();
int result = 0;
float[] widths = new float[strLength];
paint.getTextWidths(str, widths);
for (int i = 0; i & strL i++) {
result += widths[i];
return (int)
&* the left half of the punctuation . For example:" ( & [ { "
public static boolean isLeftPunctuation(char c) {
int count = (int)
if (count == 8220 || count == 12298 || count == 65288 || count == 12304
|| count == 40 || count == 60 || count == 91 || count == 123) {
&* the right half of the punctuation . For example:" ) & ] } "
public static boolean isRightPunctuation(char c) {
int count = (int)
if (count == 8221 || count == 12299 || count == 65289 || count == 12305
|| count == 41 || count == 62 || count == 93 || count == 125) {
public static String ToDBC(String input) {
char[] c = input.toCharArray();
for (int i = 0; i & c. i++) {
if (isPunctuation(c[i])) {
if (c[i] == 12288) {
c[i] = (char) 32;
if (c[i] & 65280 && c[i] & 65375) {
c[i] = (char) (c[i] - 65248);
return new String(c);
阅读(6936)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'16、重写TextView的onDraw方法',
blogAbstract:'\r\nAndroid重写系统TextView\r\n一、目的',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:9,
publishTime:1,
permalink:'blog/static/',
commentCount:7,
mainCommentCount:3,
recommendCount:2,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}TableLayout中动态生成的Textview,怎么让它能居中显示?  Java code  
TableLayout tbl=(TableLayout) findViewById(R.id.TableLayout1);
String Str=randomString(130);
for(int i=0; i&10;i++){
TableRow row=new TableRow(this);
for(int j=0;j&13;j++){
TextView txt=new TextView(this);
txt.setText(Str.substring(j+13* i,j+13* i+1));
txt.setTextSize(20);
row.addView(txt,j);
tbl.addView(row);
回答1:你要哪个居中,textview居中?text居中?
试试 TableRow.setGravity(int gravity)
txt.setGravity(Gravity.CENTER);如何实现使用TextView的DrawableLeft使图片和文字居中显示呢???代码如下: 1.首先自定义一个类,继承TextViewpackage com.test.signcalendar.import android.content.Cimport android.graphics.Cimport android.graphics.drawable.Dimport android.util.AttributeSimport android.widget.TextV/** * 自定义TextView,实现drawableLeft可以和文字一起居中 * @author HLQ * @createtime 日04:14:36 * */public class DrawableCenterTextView extends TextView {
public DrawableCenterTextView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
public DrawableCenterTextView(Context context, AttributeSet attrs) {
super(context, attrs);
public DrawableCenterTextView(Context context) {
super(context);
protected void onDraw(Canvas canvas) {
Drawable[] drawables = getCompoundDrawables();
if (drawables != null) {
Drawable drawableLeft = drawables[0];
if (drawableLeft != null) {
float textWidth = getPaint().measureText(getText().toString());
int drawablePadding = getCompoundDrawablePadding();
int drawableWidth = 0;
drawableWidth = drawableLeft.getIntrinsicWidth();
float bodyWidth = textWidth + drawableWidth + drawableP
canvas.translate((getWidth() - bodyWidth) / 2, 0);
super.onDraw(canvas);
}}2.之后在xml布局文件中直接引用即可。。。&com.test.signcalendar.weight.DrawableCenterTextView
android:id="@+id/textView1111"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableLeft="@drawable/fmhp_mine_health_plan_signcalendar_finish_status_bg"
android:drawablePadding="5dp"//给图片和文字之间设置填充
android:text="都完成"
android:textColor="#333333"
android:textSize="12sp" /&3。ok实现效果 如下。。。真是会者不难,,,难者不会。。。
iOS之解决崩溃Collection &_
最新教程周点击榜
5iOS之解决崩溃Collection &__NSArrayM: 0xb55
微信扫一扫}

我要回帖

更多关于 textview居中显示 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信