圖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