先说具体实施的关键步骤:
内容截取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);
这样,事件就可以传递给父视图