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