最近有個需求就是文字顯示指定行數(shù)比如3行榆鼠,超過3行時就在第三行的末尾顯示省略號加“全文”勤庐。
效果類似今日頭條和微博:
實現(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();
}
});
}
}
效果如下:
待優(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));
}
}
判斷全文是否超出了本行的邏輯處理比較簡單,需求沒那么嚴格,暫時就這樣祠锣。如果要嚴格在行末的位置酷窥,可以一個一個的減字符,直到可以剛好把“...全文”放下伴网。