Android實(shí)現(xiàn)可點(diǎn)擊展開的TextView
概述
Android開發(fā)過程中,經(jīng)常遇到 Textview 展示不完全的情況。
遇到此情況,通常的處理是:
方案一
Textview 添加 android:ellipsize 屬性,讓展示不完的部分使用省略號(hào)代替。
方案二
Textview 采用走馬燈效果,使其滾動(dòng)展示全部文本內(nèi)容。
對(duì)于方案一,如果想查看被省略后的內(nèi)容,如何實(shí)現(xiàn)?通常情況下是在 TextView 文本后面或下邊添加一個(gè)可點(diǎn)擊的圖標(biāo),來實(shí)現(xiàn) TextView 的展開與收縮。如下圖:
收縮狀態(tài)
展開狀態(tài)
實(shí)現(xiàn)原理
對(duì)于以上效果,大致的實(shí)現(xiàn)思路是:
- 對(duì) TextView 添加視圖高度監(jiān)聽 (addOnGlobalLayoutListener),監(jiān)控 TextView 的狀態(tài)。
- 利用 SpannableString 在 TextView 文本的后面添加一個(gè)圖標(biāo)。
- 實(shí)現(xiàn)圖標(biāo)的點(diǎn)擊效果(收縮或展開 TextView)。
下面用代碼來詳細(xì)描述實(shí)現(xiàn)的過程:
給TextView添加視圖高度監(jiān)聽
/** * 添加監(jiān)聽 * @param tv 要實(shí)現(xiàn)伸縮效果的 TextView * @param desc TextView 要展示的文字 */ public static void toggleEllipsize(final TextView tv,final String desc){ if(desc == null){ return; } //去除點(diǎn)擊圖片后的背景色( SpannableString 在點(diǎn)擊時(shí)會(huì)使背景變色 ,填上這句則可不變色 ) tv.setHighlightColor(Color.TRANSPARENT); //添加 TextView 的高度監(jiān)聽 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 實(shí)際顯示的文本長(zhǎng)度 < 應(yīng)該顯示文本的長(zhǎng)度(收縮狀態(tài)) if(ellipsizeStr.length() < desc.length()){ openFun(tv, ellipsizeStr, desc);//顯示收縮狀態(tài)的文本和圖標(biāo) } // TextView 實(shí)際顯示的文本長(zhǎng)度 == 應(yīng)該顯示文本的長(zhǎng)度(正常狀態(tài)) else if(ellipsizeStr.length() == desc.length()){ tv.setText(desc);//正常顯示Textview } // TextView 實(shí)際顯示的文本長(zhǎng)度 > 應(yīng)該顯示文本的長(zhǎng)度(展開狀態(tài)) else{ closeFun(tv, ellipsizeStr, desc);//顯示展開狀態(tài)的文本和圖標(biāo) } if(Build.VERSION.SDK_INT>=16){ tv.getViewTreeObserver().removeOnGlobalLayoutListener(this); }else{ tv.getViewTreeObserver().removeGlobalOnLayoutListener(this); } } }); }
使用 SpannableString
在 SpannableString 中,我們可以通過設(shè)置 ImageSpan 來給 TextView 添加圖標(biāo),但是普通的 ImageSpan 是不能響應(yīng)點(diǎn)擊事件的而且也不能設(shè)置圖片的位置,那么我們要如何實(shí)現(xiàn)一個(gè)可以響應(yīng)點(diǎn)擊事件并且可以設(shè)置圖片位置的 ImageSpan 呢?
Step 1:
新建一個(gè) ClickableImageSpan 類,使之具有 ImageSpan 所有屬性的,并且可以點(diǎn)擊,圖片垂直居中 。
/** * ClickableImageSpan 繼承自 ImageSpan,使其能響應(yīng)點(diǎn)擊事件,并圖片垂直居中顯示 * @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(); } /** 添加點(diǎn)擊事件 */ public abstract void onClick(View view); }
Step 2:
新建一個(gè) ClickableMovementMethod (修改 LinkMovementMethod 的 onTouchEvent 方法), 使其支持 ClickableImageSpan 。
/** * ClickableMovementMethod 繼承自 LinkMovementMethod,使其能響應(yīng) 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 設(shè)置到 TextView 中
// 顯示收縮狀態(tài)的文本,設(shè)置點(diǎn)擊圖標(biāo),并添加點(diǎn)擊事件 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()); } // 顯示展開狀態(tài)的文本,設(shè)置點(diǎn)擊圖標(biāo),并添加點(diǎn)擊事件 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 中調(diào)用
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); //調(diào)用 toggleEllipsize 方法來設(shè)置 mTv Utils.toggleEllipsize(mTv,str); } }
完整Demo鏈接:ExpandableTextView
還有一些使用其他方法實(shí)現(xiàn)可伸縮的 TextView(使用 setMaxLines 方法),傳送門:
如何寫一個(gè)可以展開的TextView
android Textview 使用之一:伸縮效果
參考文章:
用SpannableString和ImageSpan在textview中插入圖片
自定義可點(diǎn)擊的ImageSpan并在TextView中內(nèi)置“View“
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android中TextView實(shí)現(xiàn)超過固定行數(shù)顯示“...展開全部”
- Android實(shí)現(xiàn)可以展開的TextView
- Android TextView實(shí)現(xiàn)多文本折疊、展開效果
- Android TextView多文本折疊展開效果
- Android UI實(shí)現(xiàn)多行文本折疊展開效果
- Android編程實(shí)現(xiàn)Listview點(diǎn)擊展開和隱藏的方法
- Android ExpandableListView展開列表控件使用實(shí)例
- Android自定義TextView仿微信朋友圈文字展開全文功能
相關(guān)文章
OpenGL Shader實(shí)例分析(3)等待標(biāo)識(shí)效果
這篇文章主要介紹了OpenGL Shader實(shí)例分析第3篇,等待標(biāo)識(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02Android用PopupWindow實(shí)現(xiàn)自定義overflow
這篇文章主要介紹了Android用PopupWindow實(shí)現(xiàn)自定義overflow的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android 實(shí)現(xiàn)為點(diǎn)擊事件添加震動(dòng)效果
這篇文章主要介紹了Android 實(shí)現(xiàn)為點(diǎn)擊事件添加震動(dòng)效果,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android中的HTextView庫實(shí)現(xiàn)TextView動(dòng)畫效果
HTextView是一個(gè)用來給TextView里的文字做各種轉(zhuǎn)換動(dòng)畫的開源庫,不僅提供了多種動(dòng)畫選擇,而且還有重復(fù)字符的位移動(dòng)畫,雖然并沒有多么復(fù)雜,但是它使用的這些典型的設(shè)計(jì)模式以及各種動(dòng)畫的實(shí)現(xiàn)確實(shí)可以從中讓我們學(xué)到不少知識(shí)2023-12-12Android 重寫ViewGroup 分析onMeasure()和onLayout()方法
這篇文章主要介紹了Android 重寫ViewGroup 分析onMeasure()和onLayout()方法的相關(guān)資料,需要的朋友可以參考下2017-06-06android中NFC讀寫功能的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了android中NFC讀寫功能的實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09利用Kotlin如何實(shí)現(xiàn)Android開發(fā)中的Parcelable詳解
這篇文章主要給大家介紹了關(guān)于利用Kotlin如何實(shí)現(xiàn)Android開發(fā)中的Parcelable的相關(guān)資料,并且給大家介紹了關(guān)于Kotlin使用parcelable出現(xiàn):BadParcelableException: Parcelable protocol requires a Parcelable.Creator...問題的解決方法,需要的朋友可以參考下。2017-12-12Android自定義View圖片按Path運(yùn)動(dòng)和旋轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了Android自定義View圖片按Path運(yùn)動(dòng)和旋轉(zhuǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01