概述
Android開發過程中,經常遇到 Textview 展示不完全的情況。
遇到此情況,通常的處理是:
	方案一
	Textview 添加 android:ellipsize 屬性,讓展示不完的部分使用省略號代替。
	方案二
	Textview 采用走馬燈效果,使其滾動展示全部文本內容。
	對于方案一,如果想查看被省略后的內容,如何實現?通常情況下是在 TextView 文本后面或下邊添加一個可點擊的圖標,來實現 TextView 的展開與收縮。如下圖:
收縮狀態
	
展開狀態
	
實現原理
對于以上效果,大致的實現思路是:
下面用代碼來詳細描述實現的過程:
給TextView添加視圖高度監聽
  /**   * 添加監聽   * @param tv  要實現伸縮效果的 TextView   * @param desc TextView 要展示的文字   */  public static void toggleEllipsize(final TextView tv,final String desc){    if(desc == null){      return;    }    //去除點擊圖片后的背景色( SpannableString 在點擊時會使背景變色 ,填上這句則可不變色 )    tv.setHighlightColor(Color.TRANSPARENT);    //添加 TextView 的高度監聽    tv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {      @SuppressWarnings("deprecation")      @SuppressLint("NewApi")      @Override      public void onGlobalLayout() {        int paddingLeft = tv.getPaddingLeft();        int paddingRight = tv.getPaddingRight();        TextPaint paint = tv.getPaint();        float moreText  = tv.getTextSize() * 3;        float availableTextWidth = (tv.getWidth() - paddingLeft - paddingRight) * 2 - moreText;        CharSequence ellipsizeStr = TextUtils.ellipsize(desc,paint,availableTextWidth,TextUtils.TruncateAt.END);        // TextView 實際顯示的文本長度 < 應該顯示文本的長度(收縮狀態)        if(ellipsizeStr.length() < desc.length()){          openFun(tv, ellipsizeStr, desc);//顯示收縮狀態的文本和圖標        }        // TextView 實際顯示的文本長度 == 應該顯示文本的長度(正常狀態)        else if(ellipsizeStr.length() == desc.length()){          tv.setText(desc);//正常顯示Textview        }        // TextView 實際顯示的文本長度 > 應該顯示文本的長度(展開狀態)        else{          closeFun(tv, ellipsizeStr, desc);//顯示展開狀態的文本和圖標        }        if(Build.VERSION.SDK_INT>=16){           tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);         }else{           tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);         }      }    });  }使用 SpannableString
在 SpannableString 中,我們可以通過設置 ImageSpan 來給 TextView 添加圖標,但是普通的 ImageSpan 是不能響應點擊事件的而且也不能設置圖片的位置,那么我們要如何實現一個可以響應點擊事件并且可以設置圖片位置的 ImageSpan 呢?
Step 1:
新建一個 ClickableImageSpan 類,使之具有 ImageSpan 所有屬性的,并且可以點擊,圖片垂直居中 。
/** * ClickableImageSpan 繼承自 ImageSpan,使其能響應點擊事件,并圖片垂直居中顯示 * @author lee * */public abstract class ClickableImageSpan extends ImageSpan {  public ClickableImageSpan(Drawable b) {    super(b);  }  /** 圖片垂直居中顯示 */  @Override  public int getSize(Paint paint, CharSequence text, int start, int end,       Paint.FontMetricsInt fontMetricsInt) {    Drawable drawable = getDrawable();     Rect rect = drawable.getBounds();     if (fontMetricsInt != null) {       Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();       int fontHeight = fmPaint.bottom - fmPaint.top;       int drHeight = rect.bottom - rect.top;       int top = drHeight / 2 - fontHeight / 4;       int bottom = drHeight / 2 + fontHeight / 4;       fontMetricsInt.ascent = -bottom;       fontMetricsInt.top = -bottom;       fontMetricsInt.bottom = top;       fontMetricsInt.descent = top;     }     return rect.right;   }   /** 圖片垂直居中顯示 */  @Override   public void draw(Canvas canvas, CharSequence text, int start, int end,       float x, int top, int y, int bottom, Paint paint) {    Drawable drawable = getDrawable();     canvas.save();     int transY = 0;     transY = ((bottom - top) - drawable.getBounds().bottom) / 2 + top;     canvas.translate(x, transY);     drawable.draw(canvas);     canvas.restore();   }  /** 添加點擊事件 */  public abstract void onClick(View view);}Step 2:
新建一個 ClickableMovementMethod (修改 LinkMovementMethod 的 onTouchEvent 方法), 使其支持 ClickableImageSpan 。
/** * ClickableMovementMethod 繼承自 LinkMovementMethod,使其能響應 ClickableImageSpan * @author lee * */public class ClickableMovementMethod extends LinkMovementMethod {  private static ClickableMovementMethod sInstance;  public static ClickableMovementMethod getInstance() {    if (sInstance == null) {      sInstance = new ClickableMovementMethod();    }    return sInstance;  }  public boolean onTouchEvent(TextView widget, Spannable buffer,      MotionEvent event) {    int action = event.getAction();    if (action == MotionEvent.ACTION_UP ||        action == MotionEvent.ACTION_DOWN) {      int x = (int) event.getX();      int y = (int) event.getY();      x -= widget.getTotalPaddingLeft();      y -= widget.getTotalPaddingTop();      x += widget.getScrollX();      y += widget.getScrollY();      Layout layout = widget.getLayout();      int line = layout.getLineForVertical(y);      int off = layout.getOffsetForHorizontal(line, x);      ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);      /** 修改位置【1】 START **/      ClickableImageSpan[] imageSpans = buffer.getSpans(off, off, ClickableImageSpan.class);      /******  END  ******/      if (link.length != 0) {        if (action == MotionEvent.ACTION_UP) {          link[0].onClick(widget);        } else if (action == MotionEvent.ACTION_DOWN) {          Selection.setSelection(buffer,              buffer.getSpanStart(link[0]),              buffer.getSpanEnd(link[0]));        }        return true;      }       /** 修改位置【2】START **/      else if (imageSpans.length != 0) {        if (action == MotionEvent.ACTION_UP) {          imageSpans[0].onClick(widget);        } else if (action == MotionEvent.ACTION_DOWN) {          Selection.setSelection(buffer,              buffer.getSpanStart(imageSpans[0]),              buffer.getSpanEnd(imageSpans[0]));        }        return true;      }       /******  END   ******/      else {        Selection.removeSelection(buffer);      }    }    return false;  }}將改好的 SpannableString 設置到 TextView 中
  // 顯示收縮狀態的文本,設置點擊圖標,并添加點擊事件  private static void openFun(final TextView tv,final CharSequence ellipsizeStr,final String desc){    CharSequence temp = ellipsizeStr+".";    SpannableStringBuilder ssb = new SpannableStringBuilder(temp);    Drawable dd = tv.getResources().getDrawable(R.drawable.ic_expand);    dd.setBounds(0, 0, dd.getIntrinsicWidth(), dd.getIntrinsicHeight());    ClickableImageSpan is = new ClickableImageSpan(dd) {      @Override      public void onClick(View view) {        closeFun(tv,ellipsizeStr,desc);      }    };    ssb.setSpan(is, temp.length()-1, temp.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);    tv.setText(ssb);    tv.setMovementMethod(ClickableMovementMethod.getInstance());  }  // 顯示展開狀態的文本,設置點擊圖標,并添加點擊事件  private static void closeFun(final TextView tv,final CharSequence ellipsizeStr,final String desc) {    SpannableStringBuilder ssb = new SpannableStringBuilder(desc);    Drawable dd = tv.getResources().getDrawable(R.drawable.ic_normal);    dd.setBounds(0, 0, dd.getIntrinsicWidth(), dd.getIntrinsicHeight());    ClickableImageSpan is = new ClickableImageSpan(dd) {      @Override      public void onClick(View view) {        openFun(tv,ellipsizeStr,desc);      }    };    ssb.setSpan(is, desc.length()-1, desc.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);    tv.setText(ssb);    tv.setMovementMethod(ClickableMovementMethod.getInstance());  }在Activity 中調用
public class MainActivity extends Activity {  private TextView mTv;  private String str = "我有一只小毛驢,我從來也不騎~ "      + "有一天我心血來潮騎它去趕集,我手里拿著小皮鞭,我心里正得意~ "      + "不知怎么嘩啦啦啦啦,我摔了一身泥~";  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    mTv = (TextView) findViewById(R.id.tv_test);    //調用 toggleEllipsize 方法來設置 mTv    Utils.toggleEllipsize(mTv,str);  }}完整Demo鏈接:ExpandableTextView
還有一些使用其他方法實現可伸縮的 TextView(使用 setMaxLines 方法),傳送門:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答