近来天气渐冷啊!晚上加班加很晚回来的路上有点扛不住啊!好在桂花开了。还挺香的!
android:ellipsize="start"
android:singleLine="true"
android:ellipsize="middle"
android:singleLine="true"
android:ellipsize="end"
android:singleLine="true"
android:ellipsize="marquee"
android:singleLine="true"
可以看到elilipsize一共有4个可选值。看名称其实很容易理解,开始,中间,结尾和无限。实现的效果分别是如果文本内容超过textview 的宽度,那么分别显示…在开头中间,结尾和循环。注意一定要配合singleLine=true来使用,不然会失效。当然marquee这个属性再配合一些其他属性我们就可以实现跑马灯效果了。如下:
android:marqueeRepeatLimit="marquee_forever"
android:focusableInTouchMode="true"
android:ellipsize="marquee"
android:singleLine="true"
好了,那么现在我们要求是2行,那么singleline这个属性不能用。那么我们上面的属性当然也不能实现了。我们该怎么办呢。想了很多办法,其实挺难的。最终我的理解为:第一行显示最前面,中间…第二行显示最后的内容就行了。所以网上查找了一番,发现控件上能显示多少文字还是比较简单的。具体代码是如下:
TextView textView = (TextView) findViewById(R.id.test);
textView.setText(text);
int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
textView.measure(spec, spec);
// getMeasuredWidth 获得控件宽度
int measuredWidth = textView.getMeasuredWidth();
// textView getPaint measureText 获得控件的TextView的对象
TextPaint textPaint = textView.getPaint();
// 获得输入的text 的宽度
float textPaintWidth = textPaint.measureText(text);
根据网上这些基础的提示我的脑子就有了想法。既然控件宽度能获取到,对应的文本内容的宽度也能拿到,那么一行能显示多少文本就很容易得到了。
//获取我们需要展示的文本内容
String content=textView.getText().toString();
// getMeasuredWidth 获得控件宽度
int measuredWidth = textView.getMeasuredWidth();
// textView getPaint measureText 获得控件的TextView的对象
TextPaint textPaint = textView.getPaint();
// 获得输入的text 的宽度
float textPaintWidth = textPaint.measureText(content);
//先判断文本是否超过2行
if(textPaintWidth2 ){
return content;//能显示完全我们直接返回就行了。无需操作
}
//当前的textview 的textSize为15sp 其实很明显文字大小不同,每个字符占用的长度也是不同的,这里假设为15。我通过日志知道:".",0,"a","A","好",“ ” 等。这些分别占用的数值为:8,10,16,17,30,30。所以说其实挺麻烦的,因为区别很大。这里明显中文的显示是最大的为30。所以我们长度给一个最低范围-30。
// 首先计算一共能显示多少个字符:
num = (measuredWidth -30) ;
//统计文本总长度
int sumLenth=0;
//定义第一行应该显示的sb
StringBuilder sbfor = new StringBuilder();
//通过循环从content中找出第一行能显示的所有文本
for(int i=0;iif(sumLenth16){//继续缩小化一点
//获取每个位置的文本
String str=content.substring(i,i+1);
//计算长度并且统计
sumLenth=sumLenth+(int)textPaint.measureText(str);
if(sumLenth16){
//当第一行还能显示我们就拼接
sbfor.append(str);
}
}else{
//超出长度我们就退出循环并且拿到内容:sbfor
i=content.length();
}
}
//接下来计算第二行 的文本内容。也就是文本最后的内容。很好理解这次我们从后往前遍历截取。
num=measuredWidth-8;
sumLenth=0;
String sb="";
for(int i=content.length()-1;i>0;i--){
if(sumLenthstr="";
if(i==content.length()-1){//拿最后一位
str=content.substring(i);
}else{
str=content.substring(i,i+1);
}
sumLenth=sumLenth+(int) textPaint.measureText(str);
if(sumLenthstr+sb;
}
}else{
//退出循环
i=0;
}
}
//好了这里我们既获得了第一行文本以及第二行文本,那我们就可以拼接得到最终内容了.
return sbfor.toString()+" "+"..."+sb;
大家可以看到上图中已经实现了我们想要实现的效果。其实不然,我当时做完上面操作之后发现还有问题,可以看到上图被我圈红色的地方,很多做过小说app的朋友肯定知道,汉字和字符的编码方式不同,导致如果像上图中“天翼-V2.1.0-1310-9-7(2016092110934).apk”其中前面天翼是汉字用的utf-8编码,而后面的V以及一串数字编码采用的是unicode编码,所以,如果后面的一串数字如果一行不能完全显示,并且前面存在汉字,那么它会在汉字后面直接换行,选择和后面的靠在一起。不信大家可以试试。也就是会这么显示: 天翼-
V2.1.0-1310-9-7(2016092110934).apk这种方式,这明显不是我们想要的效果。所以得解决这个问题。网上搜了很多,有说都转成统一编码方式,或者选择自定义控件。自定义控件其实实现的效果是把“天翼-”这个两个字的间距拉宽,充满整行,让你看的不是那么生硬。其实这种并不能完全达到效果。后来我自己想了想,解决这个办法最好的方式就是在一行的最后加上一个空格,就能解决上面的问题。所以其实上面那个效果能实现,是因为我在
“天翼-V2.1.0-1310-9-7(201609211 “ 加了空格”0934).apk”
所以代码如下:
//计算一行显示数量
float num = (measuredWidth -30);
if(textPaintWidth <(2*measuredWidth -30)){
//对于大于一行,小于2行的文本
int sumLenth=0;
StringBuilder sbfor = new StringBuilder();
//依然遍历
for(int i=0;iif(sumLenth16){
String str=content.substring(i,i+1);
sumLenth=sumLenth+(int) textPaint.measureText(str);
if(sumLenth16){
sbfor.append(str);
}
}else{
//找到一行的位置,并且加上空格,并且再加上后面的文本
sbfor.append(" ").append(content.substring(i));
return sbfor.toString();
}
}
}
好了到达这里我们就解决问题了。其实这个问题目前很少有人去解决,最然我这个暂时解决了,但是效果不能说百分百。大家可以看一下小米自带的本地文件应用,也没有对这个进行解决。如图:
很明显大家看到。黑笔圈的地方。自动换行了。并且小米系统并没有做处理。所以说目前这个技术实现的人还是比较少的所以拿出来分享一下。
补充说明一下。如何正确获取控件的宽度:
TextPaint paint = mTvProgramName.getPaint();
paint.setTextSize(mTvProgramName.getTextSize());
// paint.measureText(mTvProgramName.getText().toString()); //这个方法能把文本所占宽度衡量出来.
Log.i(TAG, "getFocus paint.measureText(mTvProgramName.getText().toString())="
+ paint.measureText(mTvProgramName.getText().toString()));
mTvProgramName.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(final View v, final int left, final int top, final int right, final int bottom, final int oldLeft, final int oldTop, final int oldRight,
final int oldBottom) {
//在此,得到TextView控件的宽度
Log.i(TAG, "onLayoutChange mTvProgramName.getWidth()=" + v.getWidth());
}
});
我是这么获取的。
//获取屏幕宽度,高度dpi,分辨率
Map<String,Float> map=Constants.getScreenWidth(activity);
//由于明确知道textview 之外的宽度为110dp,那么通过dip和dp的转换可获得textview的实际dip
//屏幕宽度减去占用的dpi
measuredWidth =map.get("width")-map.get("density")*110;