Android实现可点击展开的TextView

概述

Android开发过程中,经常遇到 Textview 展示不完全的情况。

遇到此情况,通常的处理是:

方案一
Textview 添加 android:ellipsize 属性,让展示不完的部分使用省略号代替。
方案二
Textview 采用走马灯效果,使其滚动展示全部文本内容。
对于方案一,如果想查看被省略后的内容,如何实现?通常情况下是在 TextView 文本后面或下边添加一个可点击的图标,来实现 TextView 的展开与收缩。如下图:

收缩状态

展开状态

实现原理

对于以上效果,大致的实现思路是:

  • 对 TextView 添加视图高度监听 (addOnGlobalLayoutListener),监控 TextView 的状态。
  • 利用 SpannableString 在 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() < desc.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

在 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;
  }
}

将改好的 SpannableString 设置到 TextView 中

  // 显示收缩状态的文本,设置点击图标,并添加点击事件
  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());
  }

在Activity 中调用

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链接:ExpandableTextView

还有一些使用其他方法实现可伸缩的 TextView(使用 setMaxLines 方法),传送门:

如何写一个可以展开的TextView
android Textview 使用之一:伸缩效果

参考文章:

用SpannableString和ImageSpan在textview中插入图片
自定义可点击的ImageSpan并在TextView中内置“View“

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android TextView多文本折叠展开效果

    最近做项目,效果图要用到TextView的折叠,超过一定行数的时候,就会折叠起来,点击可以展开.网上找了一些效果,自己也稍作了修改.便拿来与网友分享分享. 参考文献:Android UI实现多行文本折叠展开效果 第一种:通过多个布局组合实现 大概步骤: - 定义布局,垂直的线性LinearLayout布局.TextView和ImageView. 在layout中定义基本组件. - 设置TextView的高度为指定行数*行高. 不使用maxLine的原因是maxLine会控制显示文本的行数,不方便

  • Android实现可以展开的TextView

    本文实例为大家分享了一个可以展开的TextView的具体代码,供大家参考,具体内容如下 原理是通过点击的时候设置 setMaxLines(lineNum)来实现: public class ExpandableTextView extends TextView { private static int MIN_LINE_NUM = 2; private static int MAX_LINE_NUM = 20; private int lineNum = MIN_LINE_NUM; privat

  • Android实现带动画效果的可点击展开TextView

    本文为大家分享了Android实现带动画效果的可点击展开TextView 制作代码,效果图: 收起(默认)效果: 点击展开后的效果: 源码: 布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:id="@+id/activity_main" xmlns:android="http://schemas.android.com/apk/res/a

  • Android TextView实现多文本折叠、展开效果

    背景 在开发过程中,当我们的需求中包含说说或者评论等内容的展示时,我们都会考虑当内容太多时该如何显示.当内容的字数太多,如果全部展示出来可能会影响体验效果,但是又不能只截取一部分内容进行展示,此时就需要考虑使用多行显示折叠的效果来实现. 效果图: 使用 1.布局文件调用 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:o

  • Android实现可点击展开的TextView

    概述 Android开发过程中,经常遇到 Textview 展示不完全的情况. 遇到此情况,通常的处理是: 方案一 Textview 添加 android:ellipsize 属性,让展示不完的部分使用省略号代替. 方案二 Textview 采用走马灯效果,使其滚动展示全部文本内容. 对于方案一,如果想查看被省略后的内容,如何实现?通常情况下是在 TextView 文本后面或下边添加一个可点击的图标,来实现 TextView 的展开与收缩.如下图: 收缩状态 展开状态 实现原理 对于以上效果,大

  • android的ListView点击item使item展开的做法的实现代码

    本文介绍了android的ListView点击item使item展开的做法的实现代码,分享给大家,具体如下: 效果图: 原理是点击item的时候,重新measure list的各个item的高度 list.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { My

  • Android开发实现的文本折叠点击展开功能示例

    本文实例讲述了Android开发实现的文本折叠点击展开功能.分享给大家供大家参考,具体如下: 信息栏,景点介绍,购物信息,进场会使用到文本折叠的方法 实现非常简单,这里就不哆嗦了 效果如下: Demo:https://github.com/LonglyWolf/NavigationSystemHLJU 这里用到了三方类库,在app/gradle添加依赖如下: //文本过长 点击展开全部 implementation 'com.ms-square:expandableTextView:0.1.4'

  • Android开发实现ListView点击展开收起效果示例

    本文实例讲述了Android开发实现ListView点击展开收起效果.分享给大家供大家参考,具体如下: 废话不说先上效果: 实际上这是采用一个ExpandableListView实现的 布局文件很简单: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xm

  • Android编程实现Listview点击展开和隐藏的方法

    本文实例讲述了Android编程实现Listview点击展开和隐藏的方法.分享给大家供大家参考,具体如下: 代码较多,所以找关键点大家贴出来,相信大家看了之后很容易就明白的, 在listview的activity中 List<Map<String, Object>> listItems = new ArrayList<Map<String, Object>>() myAdapter = new MyAdapter(getApplicationContext(

  • 一文搞懂Android RecyclerView点击展开、折叠效果的实现代码

    RecyclerView是什么 RecycleView是Android5.0后谷歌推出的一个用于在有限的窗口中展示大量数据集的控件,位于support-v7包中.它可以实现与ListView和GridView一样的效果,提供了一种插拔式的体验,高度的解耦,异常的灵活,只需设置其提供的不同的LayoutManager,ItemAnimator和ItemDecoration,就能实现不同的效果. RecyclerView的优点 1.支持局部刷新.    2.可以自定义item增删时的动画.    3

  • Android Studio使用recyclerview实现展开和折叠功能(在之前的微信页面基础之上)

    Android中RecyclerView点击item展开列表详细内容 效果如下: 依然是xml文件的设计,使用了两个RelativeLayout,zu作为主布局和副布局,里面都加入textview显示内容,在副布局里加入一个imageview在这里插入图片描述作为子内容的背景图,代码如下: tab01.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&qu

  • 原生JS仿QQ阅读点击展开、收起效果

    使用JS技术实现QQ阅读类似的点击展开.收起效果,具体内容如下 一.定义展开函数showdiv(),实现点击"全文"按钮,全文展开. 1.点击展开函数,需要将触发点击事件的按钮作为参数传入 2.通过传入的按钮,查找其父元素,将其父元素设置为隐藏. 3.将紧跟其父元素之后的元素设置为显示. 二.定义收起函数hidediv(),实现点击"收起全文"按钮,全文内容隐藏. 1.点击收起函数,需要将触发点击事件的按钮作为参数传入 2.通过传入的按钮,查找其父元素,将其父元素设

随机推荐