欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android TextView实现点击显示全文与隐藏功能(附源码)

程序员文章站 2023-12-15 14:08:34
前言 相信大家在日常开发的时候,经常会遇到大段文本需要部分展示的场景,通常的做法是在隐藏的状态下文本末尾加上「显示全文」,在展开的状态下文本末尾加上「隐藏」来控制文本的展...

前言

相信大家在日常开发的时候,经常会遇到大段文本需要部分展示的场景,通常的做法是在隐藏的状态下文本末尾加上「显示全文」,在展开的状态下文本末尾加上「隐藏」来控制文本的展示状态。这个交互可能有很多种实现方法,本文则以一个简单的 textview 来实现这些交互,封装后的 collapsiabletextview 仅增加了不到 70 个额外的方法数。

Android TextView实现点击显示全文与隐藏功能(附源码)

参数定义

如上图效果,我们需要使用到几个可配置的参数:

<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>

这几个参数分别表示

  1. 后缀颜色,也就是「显示全文」,「隐藏」这几个字的颜色
  2. 折叠后显示几行文字
  3. 折叠后的后缀文字,也就是「显示全文」
  4. 展开后的后缀文字,也就是「隐藏」
  5. 隐藏与展示的触发事件是点击后缀还是整个 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开发者们能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

上一篇:

下一篇: