ExpandableTextView——一个可折叠的Textview

本文介绍了一个名为ExpandableTextView的Android库,该库可以帮助开发者轻松地在应用中实现文本视图的扩展和折叠功能,类似于Google Play应用商店中的应用描述部分。文章详细说明了如何将该库集成到项目中,并提供了自定义样例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简单介绍

本文是在GitHub上找的一个库

ExpandableTextView is an Android library that allows developers to easily create an TextView which can expand/collapse just like the Google Play's app description. Feel free to use it all you want in your Android apps provided that you cite this project.

ExpandableTextView 是一个提供给android开发者的安卓库,它能让你很容易实现textview的拓展折叠像Google Play’s app那样。在你的app项目中用它你会感觉到自由定制你想要的一切。

这里写图片描述

二、具体使用

Requirements(要求)

 API Level 8 (Froyo) and above.

导包

The library is pushed to Maven Central as an AAR, so you just need to add the followings to your build.gradle file:

dependencies {
    compile 'com.ms-square:expandableTextView:0.1.4'
}

Usage

Using the library is really simple, just look at the source code of the provided sample. (Look at the SampleTextListAdapter.java for the use within a ListView)

The important thing to note is that the view Ids for TextView and ImageButton must be set to "@id/expandable_text" and "@id/expand_collapse" respectively for this library to work.

Also, you can optionally set the following attributes in your layout xml file to customize the behavior of the ExpandableTextView.

maxCollapsedLines (defaults to 8) The maximum number of text lines allowed to be shown when the TextView gets collapsed

animDuration (defaults to 300ms) Duration of the Animation for the expansion/collapse

animAlphaStart (defaults to 0.7f) Alpha value of the TextView when the animation starts (NOTE) Set this value to 1 if you want to disable the alpha animation.

expandDrawable Customize a drawable set to ImageButton to expand the TextView

collapseDrawable Customize a drawable set to ImageButton to collapse the TextView

使用本库很简单,看例子就行。
重要的是:

1、TextView 和ImageButton 的id必须按照库要求的设置,分别设置为”@id/expandable_text” 和”@id/expand_collapse” ,只有这样才能生效。

2、当然,你也可以设置以下属性满足你项目的需要。

maxCollapsedLines 折叠的时候允许显示的最大行,默认为8

animDuration 折叠展开时动画所用的时间,默认300ms

animAlphaStart 动画执行的时候,渐变动画,背景虚化。默认为0.7f,设置为1,则不会有虚化。

expandDrawable 你自定义展开动画。

collapseDrawable 你自定义折叠动画。

三、小例子。

 <!-- sample xml -->
  <com.ms.square.android.expandabletextview.ExpandableTextView
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:expandableTextView="http://schemas.android.com/apk/res-auto"
      android:id="@+id/expand_text_view"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      expandableTextView:maxCollapsedLines="4"
      expandableTextView:animDuration="200">
      <TextView
          android:id="@id/expandable_text"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginLeft="10dp"
          android:layout_marginRight="10dp"
          android:textSize="16sp"
          android:textColor="#666666" />
      <ImageButton
          android:id="@id/expand_collapse"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:padding="16dp"
          android:layout_gravity="right|bottom"
          android:background="@android:color/transparent"/>
  </com.ms.square.android.expandabletextview.ExpandableTextView>



// sample code snippet to set the text content on the ExpandableTextView
ExpandableTextView expTv1 = (ExpandableTextView) rootView.findViewById(R.id.sample1)
                    .findViewById(R.id.expand_text_view);

// IMPORTANT - call setText on the ExpandableTextView to set the text content to display
expTv1.setText(getString(R.string.dummy_text1));

需要注意的是,设置文本内容时候我们用的是ExpandableTextView 而不是Textview。

看效果
**展开前:**

这里写图片描述

展开后

这里写图片描述

。。。。。。。。。。。。。。

后续,老板觉得不好看,就自定义了个。

attrs里面

 <declare-styleable name="ExpandTextView">
        <!-- 文本颜色 -->
        <attr name="textColor" format="color|reference"/>
        <!-- 最大显示文本行数 -->
        <attr name="My_maxLines" format="integer|reference"/>
        <!-- 文本字体大小 -->
        <attr name="textSize" format="dimension|reference"/>
        <!-- 动画显示时间-->
        <attr name="animDuration" format="integer|reference"/>
        <!-- 提示图标的宽度 -->
        <attr name="drawableWidth" format="dimension|reference"/>
        <!-- 提示图标的高度 -->
        <attr name="drawableHeight" format="dimension|reference"/>
        <!-- 展开时候的提示图标 -->
        <attr name="expandDrawable" format="color|reference"/>
        <!-- 收缩收起时候的提示图标 -->
        <attr name="shrinkDrawable" format="color|reference"/>
    </declare-styleable>

自定义:

package com.toolbar;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * 缩放展开的动画简单Textview
 *
 * Created by igeek on 2016/9/1.
 * @author igeek2014@hotmail.com
 */
public class ExpandTextView extends View implements View.OnClickListener {

    //行文本记录集
    private List<LineText> lineTexts = new ArrayList<LineText>();
    //最大显示文本行数
    private int My_maxLines;
    //目标文本行
    private int targetLine;
    //收缩收起时候的提示图标
    private Drawable expandDrawable;
    //展开时候的提示图标
    private Drawable shrinkDrawable;
    //提示图标的宽度
    private int drawableWidth;
    //提示图标的高度
    private int drawableHeight;
    //最大显示文本行对应的本视图高度
    private int maxLinesHeight;
    //展开时候的视图高度
    private int expandHeight;
    //当前视图的高度
    private int viewHeight;
    //收缩行结尾提示语文本宽度
    private float ellipsizWidth;
    //收缩行结尾提示语文本绘制水平起点
    private float ellipsizStartX;

    //文本字体大小
    private int textSize;
    //文本颜色
    private int textColor;
    //当前文本
    private String text;
    private String ellipsizText = "...";
    //收缩行文本
    private String shrinkLineText;
    //动画显示时间
    private int animDuration;
    //是否能够显示 ellipsizText 【需要收缩行当前文本的宽度】
    private boolean showEllipsizText = false;
    private boolean showTipDrawalbe = false;
    private boolean needMeasure = true;

    private StaticLayout layout;
    private TextPaint textPaint;


    public ExpandTextView(Context context) {
        this(context, null);
    }

    public ExpandTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ExpandTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandTextView);

        My_maxLines = ta.getInt(R.styleable.ExpandTextView_My_maxLines, -1);
        animDuration = ta.getInt(R.styleable.ExpandTextView_animDuration, 300);
        textSize = ta.getDimensionPixelSize(R.styleable.ExpandTextView_textSize, 14);
        textColor = ta.getColor(R.styleable.ExpandTextView_textColor, 14);
        drawableWidth = ta.getDimensionPixelSize(R.styleable.ExpandTextView_drawableWidth, 14);
        drawableHeight = ta.getDimensionPixelSize(R.styleable.ExpandTextView_drawableHeight, 14);
        expandDrawable = ta.getDrawable(R.styleable.ExpandTextView_expandDrawable);
        shrinkDrawable = ta.getDrawable(R.styleable.ExpandTextView_shrinkDrawable);

        ta.recycle();

        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.density = context.getResources().getDisplayMetrics().density;
        textPaint.setColor(textColor);
        textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        textPaint.setTextSize(textSize);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = MeasureSpec.getSize(widthMeasureSpec);

        if (needMeasure && (!TextUtils.isEmpty(text))) {
            needMeasure = false;
            measureHeightState(text, width);
            startDrawAnim(0, viewHeight);
        }else{
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY);
            setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        }

    }

    public void updateText(String text) {
        if (!TextUtils.isEmpty(text)) {
            this.text = text;
            needMeasure = true;
            requestLayout();
        }
    }

    private synchronized void measureHeightState(String text, int width) {

        layout = new StaticLayout(text, textPaint, width - getPaddingLeft() - getPaddingRight(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, true);
        final int lineCount = layout.getLineCount();
        My_maxLines = (My_maxLines == -1 || My_maxLines > lineCount) ? lineCount : My_maxLines;

        int text_Height = 0;

        List<LineText> tempLines = new ArrayList<LineText>();

        for (int index = 0; index < lineCount; index++) {
            int start = layout.getLineStart(index);
            int end = layout.getLineEnd(index);
            LineText lineText = new LineText();
            lineText.setStartIndex(start);
            lineText.setEndIndex(end - 1);
            lineText.setText(text.substring(start, end));
            lineText.setTopOffset(layout.getLineTop(index));
            lineText.setBottomOffset(layout.getLineBottom(index));
            lineText.setBaseLine(layout.getLineBaseline(index)+getPaddingTop());
            lineText.setWidth(layout.getLineWidth(index));
            lineText.setHeight(lineText.getBottomOffset() - lineText.getTopOffset());
            tempLines.add(lineText);

            if (index < My_maxLines) {
                maxLinesHeight += lineText.getHeight();
            }

            text_Height += lineText.getHeight();
        }

        maxLinesHeight+=getPaddingTop()+getPaddingBottom();
        expandHeight+=getPaddingTop()+getPaddingBottom();

        ellipsizWidth = textPaint.measureText(ellipsizText);

        if (My_maxLines < lineCount) {

            showTipDrawalbe = expandDrawable != null && shrinkDrawable != null;

            float textWidth = tempLines.get(My_maxLines - 1).getWidth();
            float contentWidth = width - getPaddingLeft() - getPaddingRight();
            float toMarginRight = ellipsizWidth + (showTipDrawalbe ? drawableWidth : 0);

            String ellipsizLineText = tempLines.get(My_maxLines - 1).getText();

            if (contentWidth - textWidth < toMarginRight) {
                showEllipsizText = true;
                String subString = null;
                for (int index = ellipsizLineText.length() - 1; index > 0; index--) {
                    subString = ellipsizLineText.substring(0, index);
                    float subStrWidth = textPaint.measureText(subString);
                    if (contentWidth - subStrWidth >= toMarginRight) {
                        ellipsizStartX = subStrWidth + getPaddingLeft();
                        shrinkLineText = subString;
                        break;
                    }
                }
            } else {
                shrinkLineText = ellipsizLineText;
                showEllipsizText = false;
            }
        } else {
            showTipDrawalbe = false;
            showEllipsizText = false;
        }

        expandHeight += text_Height + ((expandDrawable != null && showTipDrawalbe) ? drawableHeight : 0);

        viewHeight = My_maxLines == lineCount ? expandHeight : maxLinesHeight;

        targetLine = My_maxLines;

        lineTexts = tempLines;


        if (viewHeight < expandHeight) {
            setClickable(true);
            setOnClickListener(this);
        } else {
            setClickable(false);
        }

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (lineTexts.size() == 0) return;

        for (int index = 0; index < targetLine; index++) {

            LineText lineText = lineTexts.get(index);

            if (index < targetLine - 1) {
                canvas.drawText(lineText.getText(), getPaddingLeft(), lineText.getBaseLine(), textPaint);
            } else {
                if (targetLine == My_maxLines && My_maxLines < lineTexts.size()) {
                    //收缩转态
                    if (showEllipsizText)
                        canvas.drawText(ellipsizText, ellipsizStartX, lineText.getBaseLine(), textPaint);
                    canvas.drawText(shrinkLineText, getPaddingLeft(), lineText.getBaseLine(), textPaint);
                    if (showTipDrawalbe){
                        int left=getWidth() - drawableWidth - getPaddingRight();
                        int top=getHeight() - drawableHeight-getPaddingBottom();
                        canvas.drawBitmap(drawabletoZoomBitmap(shrinkDrawable, drawableWidth, drawableHeight), left, top, null);
                    }
                } else if (targetLine == lineTexts.size()) {
                    //展开状态
                    canvas.drawText(lineText.getText(), getPaddingLeft(), lineText.getBaseLine(), textPaint);
                    if (showTipDrawalbe){
                        int left=getWidth() - drawableWidth - getPaddingRight();
                        int top=getHeight() - drawableHeight-getPaddingBottom();
                        canvas.drawBitmap(drawabletoZoomBitmap(expandDrawable, drawableWidth, drawableHeight), left, top, null);
                    }
                }
            }
        }

    }


    @Override
    public void onClick(View view) {

        if (My_maxLines == lineTexts.size())
            return;

        if (targetLine == My_maxLines) {
            targetLine = lineTexts.size();
            startDrawAnim(maxLinesHeight, expandHeight);
        } else if (targetLine == lineTexts.size()) {
            targetLine = My_maxLines;
            startDrawAnim(expandHeight, maxLinesHeight);
        }
    }

    private void startDrawAnim(int startHeight, int endHeight) {
        ObjectAnimator animator = ObjectAnimator.ofInt(this, "viewHeight", startHeight, endHeight);
        animator.setDuration(animDuration);
//        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
    }


    public  int getViewHeight() {
        return viewHeight;
    }

    public  void setViewHeight(int viewHeight) {
        this.viewHeight = viewHeight;
        requestLayout();
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    /**
     * drawlable 缩放
     *
     * @return
     */
    public static Bitmap drawabletoZoomBitmap(Drawable drawable, int w, int h) {
        // 取 drawable 的长宽
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        // drawable转换成bitmap
        Bitmap oldbmp = drawabletoBitmap(drawable);
        // 创建操作图片用的Matrix对象
        Matrix matrix = new Matrix();
        // 计算缩放比例
        float sx = ((float) w / width);
        float sy = ((float) h / height);
        // 设置缩放比例
        matrix.postScale(sx, sy);
        // 建立新的bitmap,其内容是对原bitmap的缩放后的图
        Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height,
                matrix, true);
        return newbmp;
    }

    /**
     * Drawable转换成Bitmap
     */
    public static Bitmap drawabletoBitmap(Drawable drawable) {
        // 取 drawable 的长宽
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        // 取 drawable 的颜色格式
        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                : Bitmap.Config.RGB_565;
        Bitmap bitmap = Bitmap.createBitmap(width, height, config);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }


}

lineText.java

package com.toolbar;

/**
 * Created by igeek on 2016/9/1.
 * @author igeek2014@hotmail.com
 */
public class LineText {
    private String text;
    private int startIndex;
    private int endIndex;
    private int lineIndex;
    private int topOffset;
    private int bottomOffset;
    private int paddingTop;
    private int paddingBottom;
    private float width;
    private int height;
    private int baseLine;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }

    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }

    public int getLineIndex() {
        return lineIndex;
    }

    public void setLineIndex(int lineIndex) {
        this.lineIndex = lineIndex;
    }

    public int getTopOffset() {
        return topOffset;
    }

    public void setTopOffset(int topOffset) {
        this.topOffset = topOffset;
    }

    public int getPaddingTop() {
        return paddingTop;
    }

    public void setPaddingTop(int paddingTop) {
        this.paddingTop = paddingTop;
    }

    public int getPaddingBottom() {
        return paddingBottom;
    }

    public void setPaddingBottom(int paddingBottom) {
        this.paddingBottom = paddingBottom;
    }

    public float getWidth() {
        return width;
    }

    public void setWidth(float width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getBaseLine() {
        return baseLine;
    }

    public void setBaseLine(int baseLine) {
        this.baseLine = baseLine;
    }

    public int getBottomOffset() {
        return bottomOffset;
    }

    public void setBottomOffset(int bottomOffset) {
        this.bottomOffset = bottomOffset;
    }
}
  <com.toolbar.ExpandTextView
                android:id="@+id/collapsing_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                android:paddingLeft="8dp"
                android:paddingRight="8dp"
                android:paddingBottom="8dp"
                android:paddingTop="8dp"
                app:drawableHeight="12dp"
                app:drawableWidth="14dp"
                app:expandDrawable="@mipmap/up"
                app:My_maxLines="2"
                app:shrinkDrawable="@mipmap/down"
                app:textColor="@color/colorAccent"
                app:textSize="14sp" />
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值