可伸縮的文本控件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

發佈了43 篇原創文章 · 獲贊 17 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章