Android TextView超過指定限制在文末顯示 "...全文" 并且可以點擊

最近有個需求就是文字顯示指定行數(shù)比如3行榆鼠,超過3行時就在第三行的末尾顯示省略號加“全文”勤庐。
效果類似今日頭條和微博:

image.png

實現(xiàn)如下:
1、ShowAllTextView 繼承TextView類

public class ShowAllTextView extends TextView {

/**全文按鈕點擊事件*/
private ShowAllSpan.OnAllSpanClickListener onAllSpanClickListener;
private int maxShowLines = 0;  //最大顯示行數(shù)

public ShowAllTextView(Context context) {
    super(context);
}

public ShowAllTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

/**調(diào)用此方法才有效果*/
public void setMyText(CharSequence text) {
    super.setText(text);
    post(new Runnable() {
            @Override
            public void run() {
                addEllipsisAndAllAtEnd();
            }
     });
}

/**調(diào)用此方法才有效果*/
public void setMyText(int resId){
    setMyText(getContext().getResources().getText(resId));
}

/**超過規(guī)定行數(shù)時, 在文末添加 "...全文"*/
private void addEllipsisAndAllAtEnd(){
    if (maxShowLines > 0 && maxShowLines < getLineCount()) {
        try {
            int moreWidth = PaintUtils.getTheTextNeedWidth(getPaint(), "...全文");
            /**加上...全文 長度超過了textView的寬度, 則多減去5個字符*/
            if (getLayout().getLineRight(maxShowLines - 1) + moreWidth >= getLayout().getWidth()){
                this.setText(getText().subSequence(0, getLayout().getLineEnd(maxShowLines - 1) - 5));
                /**避免減5個字符后還是長度還是超出了,這里再減4個字符*/
                if (getLayout().getLineRight(maxShowLines - 1) + moreWidth >= getLayout().getWidth()){
                    this.setText(getText().subSequence(0, getLayout().getLineEnd(maxShowLines - 1) - 4));
                }
            }else {
                this.setText(getText().subSequence(0, getLayout().getLineEnd(maxShowLines - 1)));
            }
            if (getText().toString().endsWith("\n") && getText().length() >= 1){
                this.setText(getText().subSequence(0, getText().length() - 1));
            }
            this.append("...");
            SpannableString sb = new SpannableString("全文");
            sb.setSpan(new ShowAllSpan(getContext(), onAllSpanClickListener), 0, sb.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            this.append(sb);
        }catch (Exception e){}
    }
}

public void setOnAllSpanClickListener(ShowAllSpan.OnAllSpanClickListener onAllSpanClickListener) {
    this.onAllSpanClickListener = onAllSpanClickListener;
}

public int getMaxShowLines() {
    return maxShowLines;
}

public void setMaxShowLines(int maxShowLines) {
    this.maxShowLines = maxShowLines;
}

//實現(xiàn)span的點擊
private ClickableSpan mPressedSpan = null;
private boolean result = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
    CharSequence text = getText();
    Spannable spannable = Spannable.Factory.getInstance().newSpannable(text);
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            mPressedSpan = getPressedSpan(this, spannable, event);
            if (mPressedSpan != null){
                if (mPressedSpan instanceof ShowAllSpan){
                    ((ShowAllSpan) mPressedSpan).setPressed(true);
                }
                Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan), spannable.getSpanEnd(mPressedSpan));
                result = true;
            }else {
                result = false;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            ClickableSpan mClickSpan = getPressedSpan(this, spannable, event);
            if (mPressedSpan != null && mPressedSpan != mClickSpan){
                if (mPressedSpan instanceof ShowAllSpan){
                    ((ShowAllSpan) mPressedSpan).setPressed(false);
                }
                mPressedSpan = null;
                Selection.removeSelection(spannable);
            }
            break;
        case MotionEvent.ACTION_UP:
            if (mPressedSpan != null){
                if (mPressedSpan instanceof ShowAllSpan){
                    ((ShowAllSpan) mPressedSpan).setPressed(false);
                }
                mPressedSpan.onClick(this);
            }
            mPressedSpan = null;
            Selection.removeSelection(spannable);
            break;
    }
    return result;

}

private ClickableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) {

    ClickableSpan mTouchSpan = null;

    int x = (int) event.getX();
    int y = (int) event.getY();
    x -= textView.getTotalPaddingLeft();
    x += textView.getScrollX();
    y -= textView.getTotalPaddingTop();
    y += textView.getScrollY();
    Layout layout = getLayout();
    int line = layout.getLineForVertical(y);
    int off = layout.getOffsetForHorizontal(line, x);

    ShowAllSpan[] spans = spannable.getSpans(off, off, ShowAllSpan.class);
    if (spans != null && spans.length > 0){
        mTouchSpan = spans[0];
    }
    return mTouchSpan;
}
}

2筝蚕、ShowAllSpan 可點擊的“全文”的span類

public class ShowAllSpan extends ClickableSpan {

private OnAllSpanClickListener clickListener;
private boolean isPressed = false;
private Context context;

public ShowAllSpan(Context context, OnAllSpanClickListener clickListener){
    this.context = context;
    this.clickListener = clickListener;
}

@Override
public void onClick(View widget) {
    if (clickListener != null){
        clickListener.onClick(widget);
    }
}

public void setPressed(boolean pressed) {
    isPressed = pressed;
}

public interface OnAllSpanClickListener{
    void onClick(View widget);
}

@Override
public void updateDrawState(TextPaint ds) {
    if (isPressed){
        ds.bgColor = context.getResources().getColor(android.R.color.darker_gray);
    }else {
        ds.bgColor = context.getResources().getColor(android.R.color.transparent);
    }
    ds.setColor(context.getResources().getColor(android.R.color.holo_blue_light));
    ds.setUnderlineText(false);
}
}

3锄列、PaintUtils 畫筆工具類

public class PaintUtils 

/** 計算指定畫筆下指定字符串需要的寬度*/
public static int getTheTextNeedWidth(Paint thePaint, String text) {
    float[] widths = new float[text.length()];
    thePaint.getTextWidths(text, widths);
    int length = widths.length, nowLength = 0;
    for (int i = 0; i < length; i++) {
        nowLength += widths[i];
    }
    return nowLength;
}
}

4、測試demo:

public class ShowAllTextActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_showalltext);

    ShowAllTextView tv_1 = (ShowAllTextView) findViewById(R.id.tv_1);
    ShowAllTextView tv_2 = (ShowAllTextView) findViewById(R.id.tv_2);

    tv_1.setMaxShowLines(3);
    tv_1.setMyText("Dwayne Jones was a Jamaican 16-year-old who was killed by a violent mob " +
            "in Montego Bay on the night of 21 July 2013, after he attended a dance party " +
            "dressed in women's clothing. Perceived as effeminate, Jones had been bullied " +
            "in school and rejected by his father, and had moved into a derelict house in " +
            "Montego Bay with transgender friends. When some men at the dance party discovered " +
            "that the cross-dressing Jones was not a woman, they confronted and attacked him. " +
            "He was beaten, stabbed, shot, and run over by a car. Police investigated, " +
            "but the murder remains unsolved. The death made news internationally. " +
            "While voices on social media accused Jones of provoking his killers by ");
    tv_1.setOnAllSpanClickListener(new ShowAllSpan.OnAllSpanClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(ShowAllTextActivity.this, "點擊了全文1", Toast.LENGTH_SHORT).show();
        }
    });

    tv_2.setMaxShowLines(5);
    tv_2.setMyText("水溫剛剛合適物喷,服侍我的是那個面目清秀的男孩卤材,他把手慢慢的挪到我兩腿之間,撫摸著我白皙的長腿峦失,他小心翼翼的為我脫毛商膊,神情緊張,生怕弄疼了我宠进。\n" +
            "我就這樣躺在白砂做的浴缸里晕拆,男孩輕輕的在我胸口,腹部均勻的涂抹浴鹽材蹬,又在我的背部涂抹類似于橄欖油一樣的護膚品实幕。\n" +
            "男孩從一旁取出一些瓶瓶罐罐的香料,他把一些類似于花瓣一樣的紅色的顆粒撒在我的周圍堤器,并用紗布把那種名貴的香料擠出汁液淋在我身上昆庇。我的身體愈加的酥軟,真的太舒服了闸溃,男孩的手法嫻熟整吆,讓我如癡如醉,他不斷的在我沐浴的水中添加美白和使我皮膚細膩的香料辉川。\n" +
            "水溫有些升高了表蝙。\n" +
            "男孩突然把我已經(jīng)癱軟的身體翻了過來,他分開我的雙腿……\n" +
            "他乓旗,他拿出了相機府蛇,居然在拍照,作為一只雞屿愚,被偷拍是我們這一行的大忌汇跨,男孩把照片印在一本花名冊上,我打賭那上面一定有很多雞的照片妆距,可能都是他找過的吧穷遂。\n" +
            "可是我卻,我卻居然有些喜歡上這個男孩了娱据。\n" +
            "他還給我在水中沐浴的照片起了個奇怪的名字:“枸杞燉雞湯蚪黑。");
    tv_2.setOnAllSpanClickListener(new ShowAllSpan.OnAllSpanClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(ShowAllTextActivity.this, "點擊了全文2", Toast.LENGTH_SHORT).show();
        }
    });
}
}

效果如下:

image.png

待優(yōu)化:

int moreWidth = PaintUtils.getTheTextNeedWidth(getPaint(), "...全文");
            /**加上...全文 長度超過了textView的寬度, 則多減去5個字符*/
 if (getLayout().getLineRight(maxShowLines - 1) + moreWidth >= getLayout().getWidth()){
 this.setText(getText().subSequence(0, getLayout().getLineEnd(maxShowLines - 1) - 5));
 /**避免減5個字符后還是長度還是超出了,這里再減4個字符*/
 if (getLayout().getLineRight(maxShowLines - 1) + moreWidth >= getLayout().getWidth()){
this.setText(getText().subSequence(0, getLayout().getLineEnd(maxShowLines - 1) - 4));
                }
            }

判斷全文是否超出了本行的邏輯處理比較簡單,需求沒那么嚴格,暫時就這樣祠锣。如果要嚴格在行末的位置酷窥,可以一個一個的減字符,直到可以剛好把“...全文”放下伴网。

備注:
代碼地址:https://github.com/yang1006/ShowAllTextView

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蓬推,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子澡腾,更是在濱河造成了極大的恐慌沸伏,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件动分,死亡現(xiàn)場離奇詭異毅糟,居然都是意外死亡,警方通過查閱死者的電腦和手機澜公,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門姆另,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坟乾,你說我怎么就攤上這事迹辐。” “怎么了甚侣?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵明吩,是天一觀的道長。 經(jīng)常有香客問我殷费,道長印荔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任详羡,我火速辦了婚禮仍律,結果婚禮上,老公的妹妹穿的比我還像新娘殷绍。我一直安慰自己染苛,他們只是感情好鹊漠,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布主到。 她就那樣靜靜地躺著,像睡著了一般躯概。 火紅的嫁衣襯著肌膚如雪登钥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天娶靡,我揣著相機與錄音牧牢,去河邊找鬼。 笑死,一個胖子當著我的面吹牛塔鳍,可吹牛的內(nèi)容都是我干的伯铣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼轮纫,長吁一口氣:“原來是場噩夢啊……” “哼腔寡!你這毒婦竟也來了?” 一聲冷哼從身側響起掌唾,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤放前,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后糯彬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凭语,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年撩扒,在試婚紗的時候發(fā)現(xiàn)自己被綠了似扔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡搓谆,死狀恐怖虫几,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挽拔,我是刑警寧澤辆脸,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站螃诅,受9級特大地震影響啡氢,放射性物質發(fā)生泄漏。R本人自食惡果不足惜术裸,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一倘是、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧袭艺,春花似錦搀崭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至答倡,卻和暖如春轰传,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘪撇。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工获茬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留港庄,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓恕曲,卻偏偏與公主長得像鹏氧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子佩谣,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容