可伸缩的文本控件ExpandTextView

   

     图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

                       GitHub

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值