Android實(shí)現(xiàn)可點(diǎn)擊展開的TextView
概述
Android開發(fā)過程中,經(jīng)常遇到 Textview 展示不完全的情況。
遇到此情況,通常的處理是:
方案一
Textview 添加 android:ellipsize 屬性,讓展示不完的部分使用省略號代替。
方案二
Textview 采用走馬燈效果,使其滾動(dòng)展示全部文本內(nèi)容。
對于方案一,如果想查看被省略后的內(nèi)容,如何實(shí)現(xiàn)?通常情況下是在 TextView 文本后面或下邊添加一個(gè)可點(diǎn)擊的圖標(biāo),來實(shí)現(xiàn) TextView 的展開與收縮。如下圖:
收縮狀態(tài)

展開狀態(tài)

實(shí)現(xiàn)原理
對于以上效果,大致的實(shí)現(xiàn)思路是:
- 對 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í)會使背景變色 ,填上這句則可不變色 )
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í)際顯示的文本長度 < 應(yīng)該顯示文本的長度(收縮狀態(tài))
if(ellipsizeStr.length() < desc.length()){
openFun(tv, ellipsizeStr, desc);//顯示收縮狀態(tài)的文本和圖標(biāo)
}
// TextView 實(shí)際顯示的文本長度 == 應(yīng)該顯示文本的長度(正常狀態(tài))
else if(ellipsizeStr.length() == desc.length()){
tv.setText(desc);//正常顯示Textview
}
// TextView 實(shí)際顯示的文本長度 > 應(yī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“
以上就是本文的全部內(nèi)容,希望對大家的學(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)識效果
這篇文章主要介紹了OpenGL Shader實(shí)例分析第3篇,等待標(biāo)識效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
Android用PopupWindow實(shí)現(xiàn)自定義overflow
這篇文章主要介紹了Android用PopupWindow實(shí)現(xiàn)自定義overflow的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Android 實(shí)現(xiàn)為點(diǎn)擊事件添加震動(dòng)效果
這篇文章主要介紹了Android 實(shí)現(xiàn)為點(diǎn)擊事件添加震動(dòng)效果,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android中的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é)到不少知識2023-12-12
Android 重寫ViewGroup 分析onMeasure()和onLayout()方法
這篇文章主要介紹了Android 重寫ViewGroup 分析onMeasure()和onLayout()方法的相關(guān)資料,需要的朋友可以參考下2017-06-06
android中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-12
Android自定義View圖片按Path運(yùn)動(dòng)和旋轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了Android自定義View圖片按Path運(yùn)動(dòng)和旋轉(zhuǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01

