图1 收缩状态 图2 展开状态
今天所讲述的是一个比较常见的一个功能模块,可伸缩的文本控件,可以用于显示商品的详情信息,但是为了节省空间,可以先让详情信息显示一部分,如果用户要看全部信息,只要点击展开,商品的详情信息就全部展示出来,如果用户看完之后,还可以点击收缩,把详情信息收缩起来,只展示一部分。其实这样的功能Android系统提供的TextView控件也是可以做到这一点的,TextView可以设置其显示的最大行数来收缩文本,但是它做不到的是将展开/收缩文本加到最后一行的后面,系统并没有提供此类的方法。下面我们就来自定义一个控件来实现此功能,本控件的实现原理是基于流式布局的,关于流式布局详解的博客:http://blog.csdn.net/cj_286/article/details/53082901
本控件的实现原理是遍历要显示的文本字符,计算每一个字符所占空间的宽度,不断的累加,如果超过一行显示的宽度时,就保存该行所显示的所有字符,再换行到下一行继续累计下一行显示的字符,以此类推,直到最后一行的最后一个字符。当保存了每一行所占的字符串后,遍历每行字符串的集合,对应每行创建一个TextView显示一行字符串,如果是收缩状态在后面加展开,并对其设置点击事件,点击后触发展开;如果是展开状态,在最后一行后面添加收缩,也对其添加点击事件,点击后触发收缩;在收缩或展开的最后一行添加收缩和展开TextView,因为其父容器是FlowLayout,会将收缩/展开view直接添加在其后面,如果显示不下,就会到下一行显示,不会出现显示在窗体外面的情况,完美的结合。
如何来计算每个字符所占宽度的大小呢,就是先创建一个TextView,将要计算宽度的字符设置到TextView上,然后得到它测量的宽度即可。
/**
* 临时文本控件,用于计算每个字符所占的空间大小,主要是宽度
* @param size
* @return
*/
private TextView getTextView(float size){
TextView textView = new TextView(mContext);
textView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
textView.setTextSize(size);
return textView;
}
在遍历每个字符的时候,都会调用此方法来计算字符所占的宽度
/**
* 计算每一个字符的宽度
* @param textView 初始化工作的文本控件,载体
* @param c 字符
* @return
*/
private float getCharWidth(TextView textView, char c) {
textView.setText(String.valueOf(c));
textView.measure(0, 0);
return textView.getMeasuredWidth();
}
解决了计算字符宽度的问题,下面就是得到回去要显示的字符串,遍历所有字符,计算每一行所能容纳的字符数,保存在行集合中。下面是遍历循环中的计算每行字符数的一段代码:
//得到字符的显示宽度
float charWidth = getCharWidth(mTextView,(char)mString.charAt(i));
//如果该行的显示的所有字符小于该行宽度,就继续添加
if(width + charWidth <= widthScreen){
width += charWidth;
if((char)mString.charAt(i) == ' '){//记住一行中最后一次出现的空格
isNullCharIndex = i;
}
//防止英文字符之间不是连续的
if(!isA_z((char)mString.charAt(i))){
isNullCharIndex = i;
}
if(isNullCharIndex + WORD_LENGTH < i ){//如果连续15个字符还是没有空格就直接截断
isNullCharIndex = i;
}
}else{//一行已显示不下
//如果下一个字符不是最后一个,且是英文字母
if(mString.length()-1 >= i + 1 && isA_z((char)mString.charAt(i))){
//就从记下的空格的位置继续循环,
i = isNullCharIndex ;
if(mString.length() != i){
//保存空格之前的字符串
strings.add(mString.substring(index, i + 1));
//记下下一行的开始位置
index = i + 1;
//下一行的宽度从0开始
width = 0;
//累加宽度
//width += charWidth;
//行数加1
rows ++;
}
continue;
}
//如果不是最后一行
if(mString.length() != i){
//记下该行的字符串
strings.add(mString.substring(index, i));
//记下下一行的开始位置
index = i;
//下一行的宽度从0开始
width = 0;
//累加宽度
width += charWidth;
//行数加1
rows ++;
}
}
}
在计算好每行要显示的字符数组成的字符串后,遍历每行的字符串,创建显示该字符串的文本控件TextView,设置相应的属性参数,然后将其添加到流式布局FlowLayout中去,流式布局会自动的去排版每行显示文本的控件。在收缩/展开的最后一行在创建TextView显示收缩/展开文本,并对其设置点击事件,点击收缩就出发收缩,点击展开就触发展开。布局每行字符串时,对应着三种情况,第一种情况为:要显示的字符太少,不够对其施以展开触发,第二种情况:展开状态,第三种情况:收缩状态,代码如下:
//第一种情况:在收缩文本行数以内 (展开和收缩都是一样的,所以就没有收缩和展开)
if(rows <= mZoomRows && !isExpand && mString.length()-1 == i){
strings.add(mString.substring(index, i+1));//加最后一行
for(int j=0;j<strings.size();j++){
TextView textView = new TextView(mContext);
textView.setTextSize(mCharSize);
textView.setTextColor(mDescColor);
textView.setText(strings.get(j));
//textView.setBackgroundColor(Color.RED);
mFlowLayout.addView(textView);
}
break;
}
//第二种情况:展开时,已经到最后一个字符
if(isExpand && mString.length()-1 == i){
strings.add(mString.substring(index, i+1));//加最后一行
for(int j=0;j<strings.size();j++){
TextView textView = new TextView(mContext);
textView.setTextSize(mCharSize);
textView.setText(strings.get(j));
textView.setTextColor(mDescColor);
//textView.setBackgroundColor(Color.GREEN);
mFlowLayout.addView(textView);
}
mTextViewExpand.setText(" "+mExpandTextClose);
setExpandTextDrawable(mCloseResId);
mTextViewExpand.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(mContext, "收缩", 0).show();
mFlowLayout.removeAllViews();
expandText(false);
}
});
mFlowLayout.addView(mTextViewExpand);
break;
}
//第三种情况:收缩时,但是文本已经超出了收缩的个数,所以只保留收缩的文本字数
if(rows == mZoomRows && index + mZoomChar == i && !isExpand){
strings.add(mString.substring(index, i+1));//加最后一行
strings.add("...");
for(int j=0;j<strings.size();j++){
TextView textView = new TextView(mContext);
textView.setTextSize(mCharSize);
textView.setText(strings.get(j));
textView.setTextColor(mDescColor);
//textView.setBackgroundColor(Color.BLUE);
mFlowLayout.addView(textView);
}
mTextViewExpand.setText(" "+mExpandTextOpen);
setExpandTextDrawable(mOpenResId);
mTextViewExpand.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(mContext, "展开", 0).show();
mFlowLayout.removeAllViews();
expandText(true);
}
});
mFlowLayout.addView(mTextViewExpand);
break;
}
到这里核心的代码就讲完了,下面就是给该控件自定义一些属性,让调用者在xml布局中就可以设置相应的数据,代码中也会提供相应的方法改变一些属性等。attrs.xml中定义相应的属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ExpandTextView">
<attr name="descZoomRows" format="integer" /><!-- 完全显示的行数 实际为descZoomRows + 1行 -->
<attr name="descText" format="string" /><!-- 显示文本 -->
<attr name="descSize" format="float" /><!-- 显示文本的字体大小 -->
<attr name="descColor" format="reference|color" /><!-- 显示文本的颜色 -->
<attr name="expandTextOpen" format="string" /><!-- 设置展开文本 -->
<attr name="expandTextClose" format="string" /><!-- 设置收缩文本 -->
<attr name="expandTextOpenDrawable" format="reference" /><!-- 设置展开文本右边的图标 -->
<attr name="expandTextCloseDrawable" format="reference" /><!-- 设置收缩文本右边的图标 -->
<attr name="expandTextColor" format="reference|color" /><!-- 展开/收缩的文本颜色 -->
<attr name="expandTextSize" format="float" /><!-- 展开/收缩的文本大小 -->
</declare-styleable>
</resources>
在该控件中获取相应的属性值
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ExpandTextView);
mZoomRows = a.getInt(R.styleable.ExpandTextView_descZoomRows, 2);
mString = a.getString(R.styleable.ExpandTextView_descText);
if(TextUtils.isEmpty(mString)){
mString = "";
}
mDescSize = a.getFloat(R.styleable.ExpandTextView_descSize, 14);
mCharSize = mDescSize;
mDescColor = a.getColor(R.styleable.ExpandTextView_descColor, 0);
mExpandTextOpen = a.getString(R.styleable.ExpandTextView_expandTextOpen);
mExpandTextClose = a.getString(R.styleable.ExpandTextView_expandTextClose);
mExpandTextColor = a.getColor(R.styleable.ExpandTextView_expandTextColor, 0);
mExpandTextSize = a.getFloat(R.styleable.ExpandTextView_expandTextSize, 14);
mOpenResId = a.getResourceId(R.styleable.ExpandTextView_expandTextOpenDrawable, 0);
mCloseResId = a.getResourceId(R.styleable.ExpandTextView_expandTextCloseDrawable, 0);
可伸缩的文本控件ExpandTextView到这里就讲解结束了,欢迎大家留言
源码下载地址:CSDN