Android TextView实现点击显示全文与隐藏功能(附源码)
前言
相信大家在日常开发的时候,经常会遇到大段文本需要部分展示的场景,通常的做法是在隐藏的状态下文本末尾加上「显示全文」,在展开的状态下文本末尾加上「隐藏」来控制文本的展示状态。这个交互可能有很多种实现方法,本文则以一个简单的 textview 来实现这些交互,封装后的 collapsiabletextview 仅增加了不到 70 个额外的方法数。
参数定义
如上图效果,我们需要使用到几个可配置的参数:
<declare-styleablename="collapsibletextview"> <attrname="suffixcolor"format="color"/> <attrname="collapsedlines"format="integer"/> <attrname="collapsedtext"format="string"/> <attrname="expandedtext"format="string"/> <attrname="suffixtrigger"format="boolean"/> </declare-styleable>
这几个参数分别表示
- 后缀颜色,也就是「显示全文」,「隐藏」这几个字的颜色
- 折叠后显示几行文字
- 折叠后的后缀文字,也就是「显示全文」
- 展开后的后缀文字,也就是「隐藏」
- 隐藏与展示的触发事件是点击后缀还是整个 textview
主要的构造函数如:
publiccollapsibletextview(context context, attributeset attrs,intdefstyleattr){ super(context, attrs, defstyleattr); typedarray attributes = context.gettheme() .obtainstyledattributes(attrs, r.styleable.collapsibletextview, defstyleattr, 0); msuffixcolor = attributes.getcolor(r.styleable.collapsibletextview_suffixcolor, 0xff0000ff); mcollapsedlines = attributes.getint(r.styleable.collapsibletextview_collapsedlines, 1); mcollapsedtext = attributes.getstring(r.styleable.collapsibletextview_collapsedtext); if (textutils.isempty(mcollapsedtext)) mcollapsedtext = " show all"; mexpandedtext = attributes.getstring(r.styleable.collapsibletextview_expandedtext); if (textutils.isempty(mexpandedtext)) mexpandedtext = " hide"; msuffixtrigger = attributes.getboolean(r.styleable.collapsibletextview_suffixtrigger, false); this.mtext = gettext() == null ? null : gettext().tostring(); setmovementmethod(linkmovementmethod.getinstance()); super.setonclicklistener(mclicklistener); }
代理 onclick 事件
为了配置是否由后缀触发显示与隐藏操作,我们要在 collapsibletextview 中处理点击事件。所以在构造函数中设置 clicklistener 为 mclicklistener。同时在 mclicklistener 中处理点击事件:
private onclicklistener mclicklistener = new onclicklistener() { @override publicvoidonclick(view v){ if (!msuffixtrigger) { mexpanded = !mexpanded; applystate(mexpanded); } if (mcustomclicklistener != null) { mcustomclicklistener.onclick(v); } } };
为了用户仍可以设置 clicklistener 我们重写 setonclicklistener 方法,并保留 clicklistener 为 mcustomclicklistener:
@override publicvoidsetonclicklistener(onclicklistener l){ mcustomclicklistener = l; }
这样就将 click 事件代理到了 collapsibletextview 内部。
clickablespan 处理部分文本点击
为了能够监听后缀的点击事件,需要使用 clickablespan
str.setspan(mclickspanlistener, note.length(), note.length() + suffix.length(), spannablestring.span_exclusive_exclusive); // clickablespan private clickablespan mclickspanlistener = new clickablespan() { @override publicvoidonclick(view widget){ if (msuffixtrigger) { mexpanded = !mexpanded; applystate(mexpanded); } } @override publicvoidupdatedrawstate(textpaint ds){ super.updatedrawstate(ds); ds.setunderlinetext(false); } };
根据状态计算出 spannablestring
privatevoidapplystate(booleanexpanded){ if (textutils.isempty(mtext)) return; string note = mtext, suffix; if (expanded) { suffix = mexpandedtext; } else { if (mcollapsedlines - 1 < 0) { throw new runtimeexception("collapsedlines must equal or greater than 1"); } int lineend = getlayout().getlineend(mcollapsedlines - 1); suffix = mcollapsedtext; int newend = lineend - suffix.length() - 1; int end = newend > 0 ? newend : lineend; textpaint paint = getpaint(); int maxwidth = mcollapsedlines * (getmeasuredwidth() - getpaddingleft() - getpaddingright()); while (paint.measuretext(note.substring(0, end) + suffix) > maxwidth) end--; note = note.substring(0, end); } final spannablestring str = new spannablestring(note + suffix); if (msuffixtrigger) { str.setspan(mclickspanlistener, note.length(), note.length() + suffix.length(), spannablestring.span_exclusive_exclusive); } str.setspan(new foregroundcolorspan(msuffixcolor), note.length(), note.length() + suffix.length(), spannablestring.span_exclusive_exclusive); post(new runnable() { @override publicvoidrun(){ settext(str); } }); }
其中 paint.measuretext 可以测量出文本布局的宽度从而得只文本行数并与 mcollapsedlines 比较裁剪出合适的字符长度并添加上后缀与 span 赋予 textview 即可
由于 getlineend 等函数只有在 layout 过程之后值才有意义,所以要合理的选择 applystate 的时机:
@override protectedvoidonlayout(booleanchanged,intleft,inttop,intright,intbottom){ super.onlayout(changed, left, top, right, bottom); if (mshouldinitlayout && getlinecount() > mcollapsedlines) { mshouldinitlayout = false; applystate(mexpanded); } }
至此 collapsibletextview 的要点已经完成,添加上 getter,setter 函数与一些逻辑组织即可
源码下载:点击这里
总结
以上就是这篇文章的全部内容了,希望本文的内容对给我android开发者们能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。