Android开发过程中,经常遇到 Textview 展示不完全的情况。
遇到此情况,通常的处理是:
对于方案一,如果想查看被省略后的内容,如何实现?通常情况下是在 TextView 文本后面或下边添加一个可点击的图标,来实现 TextView 的展开与收缩。如下图:
收缩状态
展开状态
对于以上效果,大致的实现思路是:
下面用代码来详细描述实现的过程:
/**
* 添加监听
* @param tv 要实现伸缩效果的 TextView
* @param desc TextView 要展示的文字
*/
public static void toggleEllipsize(final TextView tv,final String desc){
if(desc == null){
return;
}
//去除点击图片后的背景色( SpannableString 在点击时会使背景变色 ,填上这句则可不变色 )
tv.setHighlightColor(Color.TRANSPARENT);
//添加 TextView 的高度监听
tv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
@Override
public void onGlobalLayout() {
int paddingLeft = tv.getPaddingLeft();
int paddingRight = tv.getPaddingRight();
TextPaint paint = tv.getPaint();
float moreText = tv.getTextSize() * 3;
float availableTextWidth = (tv.getWidth() - paddingLeft - paddingRight) * 2 - moreText;
CharSequence ellipsizeStr = TextUtils.ellipsize(desc,paint,availableTextWidth,TextUtils.TruncateAt.END);
// TextView 实际显示的文本长度 <应该显示文本的长度(收缩状态)
if(ellipsizeStr.length() openFun(tv, ellipsizeStr, desc);//显示收缩状态的文本和图标
}
// TextView 实际显示的文本长度 == 应该显示文本的长度(正常状态)
else if(ellipsizeStr.length() == desc.length()){
tv.setText(desc);//正常显示Textview
}
// TextView 实际显示的文本长度 > 应该显示文本的长度(展开状态)
else{
closeFun(tv, ellipsizeStr, desc);//显示展开状态的文本和图标
}
if(Build.VERSION.SDK_INT>=16){
tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}else{
tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
}
在 SpannableString 中,我们可以通过设置 ImageSpan 来给 TextView 添加图标,但是普通的 ImageSpan 是不能响应点击事件的而且也不能设置图片的位置,那么我们要如何实现一个可以响应点击事件并且可以设置图片位置的 ImageSpan 呢?
Step 1:
新建一个 ClickableImageSpan 类,使之具有 ImageSpan 所有属性的,并且可以点击,图片垂直居中 。
/**
* ClickableImageSpan 继承自 ImageSpan,使其能响应点击事件,并图片垂直居中显示
* @author lee
*
*/
public abstract class ClickableImageSpan extends ImageSpan {
public ClickableImageSpan(Drawable b) {
super(b);
}
/** 图片垂直居中显示 */
@Override
public int getSize(Paint paint, CharSequence text, int start, int end,
Paint.FontMetricsInt fontMetricsInt) {
Drawable drawable = getDrawable();
Rect rect = drawable.getBounds();
if (fontMetricsInt != null) {
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fOntHeight= fmPaint.bottom - fmPaint.top;
int drHeight = rect.bottom - rect.top;
int top = drHeight / 2 - fontHeight / 4;
int bottom = drHeight / 2 + fontHeight / 4;
fontMetricsInt.ascent = -bottom;
fontMetricsInt.top = -bottom;
fontMetricsInt.bottom = top;
fontMetricsInt.descent = top;
}
return rect.right;
}
/** 图片垂直居中显示 */
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
Drawable drawable = getDrawable();
canvas.save();
int transY = 0;
transY = ((bottom - top) - drawable.getBounds().bottom) / 2 + top;
canvas.translate(x, transY);
drawable.draw(canvas);
canvas.restore();
}
/** 添加点击事件 */
public abstract void onClick(View view);
}
Step 2:
新建一个 ClickableMovementMethod (修改 LinkMovementMethod 的 onTouchEvent 方法), 使其支持 ClickableImageSpan 。
/**
* ClickableMovementMethod 继承自 LinkMovementMethod,使其能响应 ClickableImageSpan
* @author lee
*
*/
public class ClickableMovementMethod extends LinkMovementMethod {
private static ClickableMovementMethod sInstance;
public static ClickableMovementMethod getInstance() {
if (sInstance == null) {
sInstance = new ClickableMovementMethod();
}
return sInstance;
}
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
/** 修改位置【1】 START **/
ClickableImageSpan[] imageSpans = buffer.getSpans(off, off, ClickableImageSpan.class);
/****** END ******/
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
}
/** 修改位置【2】START **/
else if (imageSpans.length != 0) {
if (action == MotionEvent.ACTION_UP) {
imageSpans[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(imageSpans[0]),
buffer.getSpanEnd(imageSpans[0]));
}
return true;
}
/****** END ******/
else {
Selection.removeSelection(buffer);
}
}
return false;
}
}
// 显示收缩状态的文本,设置点击图标,并添加点击事件
private static void openFun(final TextView tv,final CharSequence ellipsizeStr,final String desc){
CharSequence temp = ellipsizeStr+".";
SpannableStringBuilder ssb = new SpannableStringBuilder(temp);
Drawable dd = tv.getResources().getDrawable(R.drawable.ic_expand);
dd.setBounds(0, 0, dd.getIntrinsicWidth(), dd.getIntrinsicHeight());
ClickableImageSpan is = new ClickableImageSpan(dd) {
@Override
public void onClick(View view) {
closeFun(tv,ellipsizeStr,desc);
}
};
ssb.setSpan(is, temp.length()-1, temp.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
tv.setText(ssb);
tv.setMovementMethod(ClickableMovementMethod.getInstance());
}
// 显示展开状态的文本,设置点击图标,并添加点击事件
private static void closeFun(final TextView tv,final CharSequence ellipsizeStr,final String desc) {
SpannableStringBuilder ssb = new SpannableStringBuilder(desc);
Drawable dd = tv.getResources().getDrawable(R.drawable.ic_normal);
dd.setBounds(0, 0, dd.getIntrinsicWidth(), dd.getIntrinsicHeight());
ClickableImageSpan is = new ClickableImageSpan(dd) {
@Override
public void onClick(View view) {
openFun(tv,ellipsizeStr,desc);
}
};
ssb.setSpan(is, desc.length()-1, desc.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
tv.setText(ssb);
tv.setMovementMethod(ClickableMovementMethod.getInstance());
}
public class MainActivity extends Activity {
private TextView mTv;
private String str = "我有一只小毛驴,我从来也不骑~ "
+ "有一天我心血来潮骑它去赶集,我手里拿着小皮鞭,我心里正得意~ "
+ "不知怎么哗啦啦啦啦,我摔了一身泥~";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.tv_test);
//调用 toggleEllipsize 方法来设置 mTv
Utils.toggleEllipsize(mTv,str);
}
}
完整Demo链接:http://download.csdn.net/detail/l_lhc/9460197
还有一些使用其他方法实现可伸缩的 TextView(使用 setMaxLines 方法),传送门:
参考文章: