当前位置:首页 > 教育

textview 自定义控件之可折叠TextView

8月8日,特斯拉汽车(Tesla Motors)首席执行官埃隆马斯克(Elon Musk)发布推文,提出将特斯拉私有化的想法,震惊了投资者。如果特斯拉真的私有化,这将是同类交易中最大的一笔。马斯克在推特上说:“首先,最终的决定还没有最终确定,但这样做的原因是为了创造更好的环境,让特斯拉更好地运营。”

作者简介

这个来自zzp2138的投稿分享了自定义可折叠TextView,大家来看看!希望大家喜欢。

zzp2138的博客地址:

https://www.jianshu.com/u/8b4e659ce82c

前言

当文本内容超过指定的行数时,将显示省略号和全文。

上图效果可以在微博和bilibili上找到。这里我选择继承AppCompatTextView实现。

主体

实现理念

当内容超过指定行数后,计算最大行数第一个(start)和最后一个字符(end)在整个字符串里面的位置测量要拼接的内容(demo中是... 全文)的宽度计算跟拼接内容宽度相当的字符个数(num)将整个字符串从0到(end-num)进行截取拼接要显示的内容,设置点击事件设置文字

这里会有点小问题。提示内容紧跟着原文,不在TextView的边界上。点击事件用点击面板设置。如果文本视图设置了点击事件,它们将与文本视图本身的点击事件同时被触发。如果没有设置,点击换句话说,点击事件不会传输到父视图,而是被TextView消费掉。如下图:

同时响应点击事件

单击事件不能传递给父视图

这是另一种思考方式

前几步跟上面相同,都是对内容进行截取,设置文字文字绘制完毕后,手动把提示的内容绘制上去添加点击事件

这个方法可以保证提示在TextView的边界上,但是点击事件需要重写onTouchEvent()的设置,有点麻烦。

先说具体实施的关键步骤:

内容截取

SpannableStringBuilder span = NewsAnnablestringbuilder();

int start = layout . getlinestart(mShowMaxLine-1);

intent = layout . GetLineEnd(MsHowMaxLine-1);

if(mTipGravity == END) {

TextPaint paint = getPaint();

string builder builder = NewStringBuilder(ELLIPSize _ END)。追加(" ")。追加(MFoldText);

end-= paint . BreakText(Moriginaltext,start,end,false,paint . MeasureText(builder . ToString()),null);

} else{

end-;

}

当内容行数超过最大行数时,文本将被拦截。布局是TextView的布局,其中getLineStart()和getLineEnd()分别获取该行第一个和最后一个字符的位置(这里的位置是从第一个字符开始计算的)。breakText()方法计算要截取的字符数。

下面是breakText()方法的简要描述:

参数:

测量的字符串测量开始的位置测量结束的位置测量方向,true从前往后,false从后往前截取的字符串最大宽度截取字符串实际宽度

最后,返回要拦截的字符数。截取内容后,对提示文本进行处理,并简要说明以下两种方法

方法1:直接拼接内容,设置点击事件

CharSequence elli psize = Moriginaltext . subsequence(0,end);

span . append(ellipsize);

span . append(ELLIPSize _ END);

if(mTipGravity == END) {

span . append(" ");

} else{

span . append(" n ");

}

intlength

if(isExpand) {

span . append(MexpandText);

length = mexpandtext . length();

} else{

span . append(mFoldText);

length = mfoldtext . length();

}

if(mTipClickable) {

span.setSpan(mSpan,span.length() - length,span.length(),Spanned。SPAN _ INCLUSIVE _ EXCLUSIVE

setMovementMethod(Linkmovementmethod . GetInstance());

}

span . set span(NewForeGroundColorspan(MTIPColor),span.length() - length,span.length(),跨区数。SPAN _ INCLUSIVE _ EXCLUSIVE

}

super.setText(span,type);

通过SpannableString将文本拼接在截取的内容上,并设置相应的点击事件。

方法二:重写onDraw()方法绘制提示文本,重写onTouchEvent()设置click事件

折叠状态

@覆盖

protectedvoidonDraw(画布画布){

super . ondraw(canvas);

if(isOverMaxLine & amp;& amp!isExpand) {

//折叠

if(mTipGravity == END) {

minx = getwidth()-getpaddingslip()-getpaddinglight()-gettextwidth("全文");

maxX = getWidth()-getpadding left()-getpadding right();

minY = getHeight() - (getPaint()。getFontMetrics()。下降- getPaint()。getFontMetrics()。ascent)-getPaddingBottom();

maxY = getHeight()-getPaddingBottom();

Canvas.drawText("全文",minX,

getHeight() - getPaint()。getFontMetrics()。下降- getPaddingBottom(),mPaint);

} else{

minX = GetPaddingLeft();

MaxX = minX+getTextWidth("全文");

minY = getHeight() - (getPaint()。getFontMetrics()。下降- getPaint()。getFontMetrics()。ascent)-getPaddingBottom();

maxY = getHeight()-getPaddingBottom();

Canvas.drawText("全文",minx,getheight ()-getpaint()。getfontmetrics()。depression-getpadding bitmap(),MPa int);

}

}

}

文本截取后,重写onDraw()方法计算坐标,绘制文本。

PS: minx、maxx、miny、maxy四个值用于后续的点击事件,如果不需要可以忽略。这四个值分别对应于提示左上角和右下角的坐标。

扩展状态

//文本扩展

SpannableStringBuilder span enable = NewsAnnablestringbuilder(Moriginaltext);

if(IsShowTiptafterExpand){

Spannable.append("折叠全文");

span enable . set SPan(NewForeGroundColorSPan(MTipColor),span enable . length()-5,span enable . length(),跨区数。SPAN _ INCLUSIVE _ EXCLUSIVE

}

super . SetText(span enable,type);

这里的坐标计算比上面稍微复杂一点,这里的提示可能会换线。

所以要多加一个变量才能多记录一个y值。

intmLineCount = getLineCount();

layout layout = GetLayout();

minx = getpadding foot()+布局。get primary水平(spannable。tostring()。last index of(" closed ")-1);

maxx = getpaddingslip()+layout . getsecondariholizontal(span enable . tostring()。last indexof(" text ")+1);

rect bound = NewRect();

if(mLineCount & gt;originalinecount){

//不在一条线上

layout . getlinebounds(originalinecount-1,bound);

minY = getPaddingTop()+bound . top;

middleY = minY + getPaint()。getFontMetrics()。下降- getPaint()。getFontMetrics()。上升;

maxY = middleY + getPaint()。getFontMetrics()。下降- getPaint()。getFontMetrics()。上升;

} else{

//同一行

layout . getlinebounds(originalinecount-1,bound);

minY = getPaddingTop()+bound . top;

maxY = minY + getPaint()。getFontMetrics()。下降- getPaint()。getFontMetrics()。上升;

}

PS:这里我选择直接拼接的方法。如果展开状态也需要贴边界,请参考折叠状态自行实现。

最后,重写onTouchEvent()来设置点击事件

@覆盖

public booleanTouchEvent(MotionEvent事件){

if(mTipClickable) {

switch(event . Getactionmasked()){

caseMotionEvent。动作_向下:

click time = system . current timemillis();

if(!isClickable()) {

if(isInRange(event.getX()、event . GetY()){

returntrue

}

}

打破;

caseMotionEvent。操作_取消:

caseMotionEvent。动作_向上:

long deltime = system . current timemillis()-点击time;

点击时间= 0L

if(DelTiME & lt;view configuration . gettaptimeout()& amp;& ampis RanGe(event . GetX()、event . GetY()){

isExpand =!isExpand

setText(Moriginaltext);

returntrue

}

打破;

默认:

打破;

}

}

returnsuper.onTouchEvent(事件);

}

PS: action _ down在这里,我们需要判断点击事件是否是在TextView本身设置的。如果没有,我们需要人工retrun true,否则点击事件无法传输。如果点击范围太小,我们可以自行调整isInRange方法。

让我们看看方法1中两个问题的原因

我们先来看看TextView的onTouchEvent()方法

privateavotixficfocusableandclickablesettings(){

如果(移动!= null|| (mEditor!= null & amp& ampmEditor.mKeyListener!= null)) {

setFocusable(FOCUSABLE);

setClickable(true);

setLongClickable(true);

} else{

setFocusable(FOCUSABLE _ AUTO);

setClickable(false);

setLongClickable(false);

}

}

发现这里的设置被更改了,导致了上面的一系列问题。因此,我们还需要在设置了ClickableSpan之后调用

setFocusable(false);

setClickable(false);

setLongClickable(false);

这样,事件就可以传递给父视图

最终效果

总结

最后附上演示地址。欢迎多交流!

java版本

https://github.com/zzp2138/FoldText_Java

kotlin版本

https://github.com/zzp2138/FoldText_Kotlin

1.《textview 自定义控件之可折叠TextView》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《textview 自定义控件之可折叠TextView》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/jiaoyu/1606223.html

上一篇

卡路里和千焦 脂肪、卡路里、热量,到底啥关系?

下一篇

韩国综艺我们结婚了 安利5部韩国恋爱综艺,《我们结婚了》上榜,你看过哪几档?

巴西一托儿所发生持刀行凶事件致5人死亡 还原事发经过及背后真相!

巴西一托儿所发生持刀行凶事件致5人死亡 还原事发经过及背后真相!

巴西南部圣卡塔琳娜州一所托儿所当地时间5月4日上午发生一起持刀行凶事件,导致3名幼儿和2名教师死亡,犯罪嫌疑人已被逮捕。当地警方表示,圣卡塔琳娜州绍达迪斯市一所托儿所的员工和附近居民4日上午报警说,一名年轻男子闯入该托儿所并持刀行...

男子地铁上猥亵女乘客 事件详细经过!

男子地铁上猥亵女乘客 事件详细经过!

针对“深圳一男子被指在地铁车厢猥亵女乘客“一事,5月4日,广东深圳市公安局公交分局官方微博发布情况通报称,4月30日18时30分许,张某某在地铁11号线车厢内猥亵一名女乘客,被该女乘客发现后当场斥责,并用手机拍下视频后报警。列车停...

山东景区游客坠亡翻越护栏 事件的真相是什么?

山东景区游客坠亡翻越护栏 事件的真相是什么?

山东沂蒙山银座天蒙旅游区头条号5月4日发布情况通报称,5月2日13时许,天蒙景区发生一名男性游客坠落事件,景区第一时间启动应急预案进行救助。经查,该男性游客与家人在天蒙山景区游览时,其家人手机掉落游步道护栏外,该游客不顾景区安全提...

中欧投资协定 事件详情始末介绍!

  • 中欧投资协定 事件详情始末介绍!
  • 中欧投资协定 事件详情始末介绍!
  • 中欧投资协定 事件详情始末介绍!

发票虚开涉案金额 事件的真相是什么?

5月5日晚间,国家税务总局曝光了8起虚开发票违法典型案例。其中,北京查处了“8·27”增值税发票虚开案,涉案金额高达109亿元。为了维护正常的经济税收秩序,在国家税务总局统一部署下,各地税务部门联合公安等部门重拳出击,在开展打击虚...

疫苗加强针 事件详情始末介绍!

疫苗加强针 事件详情始末介绍!

当地时间5日,英国疫苗大臣扎哈维在接受当地媒体采访时表示,英国公共卫生系统可能将从9月份开始为民众注射疫苗加强针。但是现在还没有决定注射哪种疫苗,以及从哪个年龄段开始注射。此外,英国政府将再拨款约2930万英镑,建立新的病毒检测实...

韩国娱乐圈事件 2019韩国娱乐圈十大事件之首非它莫属,甚至是韩流发展史之最!

  • 韩国娱乐圈事件 2019韩国娱乐圈十大事件之首非它莫属,甚至是韩流发展史之最!
  • 韩国娱乐圈事件 2019韩国娱乐圈十大事件之首非它莫属,甚至是韩流发展史之最!
  • 韩国娱乐圈事件 2019韩国娱乐圈十大事件之首非它莫属,甚至是韩流发展史之最!

中国未解之谜诡异事件 中国未解之谜至今未解的有哪些

  • 中国未解之谜诡异事件 中国未解之谜至今未解的有哪些
  • 中国未解之谜诡异事件 中国未解之谜至今未解的有哪些
  • 中国未解之谜诡异事件 中国未解之谜至今未解的有哪些