8月8日,特斯拉汽車(Tesla Motors)首席執(zhí)行官埃隆馬斯克(Elon Musk)發(fā)布推文,提出將特斯拉私有化的想法,震驚了投資者。如果特斯拉真的私有化,這將是同類交易中最大的一筆。馬斯克在推特上說:“首先,最終的決定還沒有最終確定,但這樣做的原因是為了創(chuàng)造更好的環(huán)境,讓特斯拉更好地運(yùn)營?!?/p>
作者簡介
這個(gè)來自zzp2138的投稿分享了自定義可折疊TextView,大家來看看!希望大家喜歡。
zzp2138的博客地址:
https://www.jianshu.com/u/8b4e659ce82c
前言
當(dāng)文本內(nèi)容超過指定的行數(shù)時(shí),將顯示省略號和全文。
上圖效果可以在微博和bilibili上找到。這里我選擇繼承AppCompatTextView實(shí)現(xiàn)。
主體
實(shí)現(xiàn)理念
當(dāng)內(nèi)容超過指定行數(shù)后,計(jì)算最大行數(shù)第一個(gè)(start)和最后一個(gè)字符(end)在整個(gè)字符串里面的位置測量要拼接的內(nèi)容(demo中是... 全文)的寬度計(jì)算跟拼接內(nèi)容寬度相當(dāng)?shù)淖址麄€(gè)數(shù)(num)將整個(gè)字符串從0到(end-num)進(jìn)行截取拼接要顯示的內(nèi)容,設(shè)置點(diǎn)擊事件設(shè)置文字這里會(huì)有點(diǎn)小問題。提示內(nèi)容緊跟著原文,不在TextView的邊界上。點(diǎn)擊事件用點(diǎn)擊面板設(shè)置。如果文本視圖設(shè)置了點(diǎn)擊事件,它們將與文本視圖本身的點(diǎn)擊事件同時(shí)被觸發(fā)。如果沒有設(shè)置,點(diǎn)擊換句話說,點(diǎn)擊事件不會(huì)傳輸?shù)礁敢晥D,而是被TextView消費(fèi)掉。如下圖:
同時(shí)響應(yīng)點(diǎn)擊事件
單擊事件不能傳遞給父視圖
這是另一種思考方式
前幾步跟上面相同,都是對內(nèi)容進(jìn)行截取,設(shè)置文字文字繪制完畢后,手動(dòng)把提示的內(nèi)容繪制上去添加點(diǎn)擊事件這個(gè)方法可以保證提示在TextView的邊界上,但是點(diǎn)擊事件需要重寫onTouchEvent()的設(shè)置,有點(diǎn)麻煩。
先說具體實(shí)施的關(guān)鍵步驟:
內(nèi)容截取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-;
}
當(dāng)內(nèi)容行數(shù)超過最大行數(shù)時(shí),文本將被攔截。布局是TextView的布局,其中g(shù)etLineStart()和getLineEnd()分別獲取該行第一個(gè)和最后一個(gè)字符的位置(這里的位置是從第一個(gè)字符開始計(jì)算的)。breakText()方法計(jì)算要截取的字符數(shù)。
下面是breakText()方法的簡要描述:
參數(shù):
測量的字符串測量開始的位置測量結(jié)束的位置測量方向,true從前往后,false從后往前截取的字符串最大寬度截取字符串實(shí)際寬度最后,返回要攔截的字符數(shù)。截取內(nèi)容后,對提示文本進(jìn)行處理,并簡要說明以下兩種方法
方法1:直接拼接內(nèi)容,設(shè)置點(diǎn)擊事件
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(),跨區(qū)數(shù)。SPAN _ INCLUSIVE _ EXCLUSIVE
}
super.setText(span,type);
通過SpannableString將文本拼接在截取的內(nèi)容上,并設(shè)置相應(yīng)的點(diǎn)擊事件。
方法二:重寫onDraw()方法繪制提示文本,重寫onTouchEvent()設(shè)置click事件
折疊狀態(tài)
@覆蓋
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()方法計(jì)算坐標(biāo),繪制文本。
PS: minx、maxx、miny、maxy四個(gè)值用于后續(xù)的點(diǎn)擊事件,如果不需要可以忽略。這四個(gè)值分別對應(yīng)于提示左上角和右下角的坐標(biāo)。
擴(kuò)展?fàn)顟B(tài)
//文本擴(kuò)展
SpannableStringBuilder span enable = NewsAnnablestringbuilder(Moriginaltext);
if(IsShowTiptafterExpand){
Spannable.append("折疊全文");
span enable . set SPan(NewForeGroundColorSPan(MTipColor),span enable . length()-5,span enable . length(),跨區(qū)數(shù)。SPAN _ INCLUSIVE _ EXCLUSIVE
}
super . SetText(span enable,type);
這里的坐標(biāo)計(jì)算比上面稍微復(fù)雜一點(diǎn),這里的提示可能會(huì)換線。
所以要多加一個(gè)變量才能多記錄一個(gè)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:這里我選擇直接拼接的方法。如果展開狀態(tài)也需要貼邊界,請參考折疊狀態(tài)自行實(shí)現(xiàn)。
最后,重寫onTouchEvent()來設(shè)置點(diǎn)擊事件
@覆蓋
public booleanTouchEvent(MotionEvent事件){
if(mTipClickable) {
switch(event . Getactionmasked()){
caseMotionEvent。動(dòng)作_向下:
click time = system . current timemillis();
if(!isClickable()) {
if(isInRange(event.getX()、event . GetY()){
returntrue
}
}
打破;
caseMotionEvent。操作_取消:
caseMotionEvent。動(dòng)作_向上:
long deltime = system . current timemillis()-點(diǎn)擊time;
點(diǎn)擊時(shí)間= 0L
if(DelTiME & lt;view configuration . gettaptimeout()& amp;& ampis RanGe(event . GetX()、event . GetY()){
isExpand =!isExpand
setText(Moriginaltext);
returntrue
}
打破;
默認(rèn):
打破;
}
}
returnsuper.onTouchEvent(事件);
}
PS: action _ down在這里,我們需要判斷點(diǎn)擊事件是否是在TextView本身設(shè)置的。如果沒有,我們需要人工retrun true,否則點(diǎn)擊事件無法傳輸。如果點(diǎn)擊范圍太小,我們可以自行調(diào)整isInRange方法。
讓我們看看方法1中兩個(gè)問題的原因
我們先來看看TextView的onTouchEvent()方法
privateavotixficfocusableandclickablesettings(){
如果(移動(dòng)!= null|| (mEditor!= null & amp& ampmEditor.mKeyListener!= null)) {
setFocusable(FOCUSABLE);
setClickable(true);
setLongClickable(true);
} else{
setFocusable(FOCUSABLE _ AUTO);
setClickable(false);
setLongClickable(false);
}
}
發(fā)現(xiàn)這里的設(shè)置被更改了,導(dǎo)致了上面的一系列問題。因此,我們還需要在設(shè)置了ClickableSpan之后調(diào)用
setFocusable(false);
setClickable(false);
setLongClickable(false);
這樣,事件就可以傳遞給父視圖
最終效果
總結(jié)
最后附上演示地址。歡迎多交流!
java版本https://github.com/zzp2138/FoldText_Java
kotlin版本https://github.com/zzp2138/FoldText_Kotlin
1.《textview 自定義控件之可折疊TextView》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識,僅代表作者本人觀點(diǎn),與本網(wǎng)站無關(guān),侵刪請聯(lián)系頁腳下方聯(lián)系方式。
2.《textview 自定義控件之可折疊TextView》僅供讀者參考,本網(wǎng)站未對該內(nèi)容進(jìn)行證實(shí),對其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。
3.文章轉(zhuǎn)載時(shí)請保留本站內(nèi)容來源地址,http://f99ss.com/jiaoyu/1606223.html