TextView多文本折叠/展开效果

背景

在开发过程中,当我们的需求中包含说说或者评论等内容的展示时,我们都会考虑当内容太多时该如何显示。当内容的字数太多,如果全部展示出来可能会影响体验效果,但是又不能只截取一部分内容进行展示,此时就需要考虑使用多行显示折叠的效果来实现。

效果图:

多文本展示

使用

1.布局文件调用

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.wiggins.expandable.widget.MoreLineTextView
        android:id="@+id/tv_more_line_short"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:padding="@dimen/padding_small"
        app:clickAll="true"
        app:textColor="@color/red" />

    <View style="@style/spaceLine" />

    <com.wiggins.expandable.widget.expandable.ExpandableTextView
        android:id="@+id/tv_expandable_short"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:ellipsize="end"
        android:padding="@dimen/padding_small"
        android:textColor="@color/blue"
        app:allClickable="false"
        app:contentTextColor="@color/blue"
        app:isDisplayIcon="false"
        app:maxCollapsedLines="4" />

    <View style="@style/spaceLine" />

    <com.wiggins.expandable.widget.MoreLineTextView
        android:id="@+id/tv_more_line_long"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:padding="@dimen/padding_small"
        app:clickAll="true"
        app:textColor="@color/red" />

    <View style="@style/spaceLine" />

    <com.wiggins.expandable.widget.expandable.ExpandableTextView
        android:id="@+id/tv_expandable_long"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:ellipsize="end"
        android:padding="@dimen/padding_small"
        android:textColor="@color/blue"
        app:allClickable="false"
        app:contentTextColor="@color/blue"
        app:isDisplayIcon="false"
        app:maxCollapsedLines="4" />
</LinearLayout>

2.Java文件调用

private void initData() {
    mTvMoreLineShort.setText(Constant.content1);
    mTvExpandableShort.setText(Constant.content2);
    mTvMoreLineLong.setText(Constant.content3);
    mTvExpandableLong.setText(Constant.content4);
}

MoreLineTextView使用

1.在attr.xml中定义属性

<declare-styleable name="MoreTextStyle">
    <!--内容大小-->
    <attr name="textSize" format="dimension" />
    <!--内容颜色-->
    <attr name="textColor" format="color" />
    <!--内容默认最大行数-->
    <attr name="maxLine" format="integer" />
    <!--展开/收起图标-->
    <attr name="expandIcon" format="reference" />
    <!--展开/收起动画执行时间-->
    <attr name="durationMillis" format="integer" />
    <!--可点击区域,默认展开/收起区域可点击-->
    <attr name="clickAll" format="boolean" />
</declare-styleable>

2.是否显示折叠效果

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    // 如果没有变化,测量并返回
    if (!mRelayout || getVisibility() == View.GONE) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        return;
    }
    mRelayout = false;

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    // 内容区域初始显示行高
    mTvContent.setHeight(mTvContent.getLineHeight() * (mMaxLine > mTvContent.getLineCount() ? mTvContent.getLineCount() : mMaxLine));
    mLlExpand.post(new Runnable() {

        @Override
        public void run() {
            // 是否显示折叠效果
            mLlExpand.setVisibility(mTvContent.getLineCount() > mMaxLine ? View.VISIBLE : View.GONE);
        }
    });
}

3.设置显示内容

/**
 * @Description 设置显示内容
 */
public void setText(String str) {
    mRelayout = true;
    mTvContent.setText(str);
    setVisibility(TextUtils.isEmpty(str) ? View.GONE : View.VISIBLE);
}

4.展开/收起动画

@Override
public void onClick(View v) {
    if (mTvContent.getLineCount() <= mMaxLine) {
        return;
    }
    isExpand = !isExpand;
    mTvContent.clearAnimation();
    final int deltaValue;
    final int startValue = mTvContent.getHeight();
    if (isExpand) {
        deltaValue = mTvContent.getLineHeight() * mTvContent.getLineCount() - startValue;//计算要展开高度
        RotateAnimation animation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setDuration(mDurationMillis);
        animation.setFillAfter(true);
        mIvExpand.startAnimation(animation);
        mTvExpand.setText(getContext().getString(R.string.collapse));
    } else {
        deltaValue = mTvContent.getLineHeight() * mMaxLine - startValue;//为负值,收缩的高度
        RotateAnimation animation = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setDuration(mDurationMillis);
        animation.setFillAfter(true);
        mIvExpand.startAnimation(animation);
        mTvExpand.setText(getContext().getString(R.string.expand));
    }
    Animation animation = new Animation() {
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            //interpolatedTime:为当前动画帧对应的相对时间,值总在0-1之间,原始长度+高度差*(从0到1的渐变)即表现为动画效果
            mTvContent.setHeight((int) (startValue + deltaValue * interpolatedTime));
        }
    };
    animation.setDuration(mDurationMillis);
    mTvContent.startAnimation(animation);
}

ExpandableTextView使用

1.在attr.xml中定义属性

<declare-styleable name="ExpandableTextView">
    <!--内容默认最大行数,超过隐藏-->
    <attr name="maxCollapsedLines" format="integer" />
    <!--展开/收起动画执行时间-->
    <attr name="animDuration" format="integer" />
    <!--展开图片-->
    <attr name="expandDrawable" format="reference" />
    <!--收起图片-->
    <attr name="collapseDrawable" format="reference" />
    <!--内容颜色-->
    <attr name="contentTextColor" format="color" />
    <!--内容大小-->
    <attr name="contentTextSize" format="dimension" />
    <!--收起/展开颜色-->
    <attr name="collapseExpandTextColor" format="color" />
    <!--收起/展开大小-->
    <attr name="collapseExpandTextSize" format="dimension" />
    <!--收起文字-->
    <attr name="textCollapse" format="string" />
    <!--展开文字-->
    <attr name="textExpand" format="string" />
    <!--可点击区域,默认展开/收起区域可点击-->
    <attr name="allClickable" format="boolean" />
    <!--是否显示展开/收起图标,默认显示-->
    <attr name="isDisplayIcon" format="boolean" />
    <!--收起/展开位置,默认左边-->
    <attr name="collapseExpandGrarity">
        <flag name="left" value="3" />
        <flag name="right" value="5" />
    </attr>
    <!--收起/展开图标位置,默认右边-->
    <attr name="drawableGrarity">
        <flag name="left" value="3" />
        <flag name="right" value="5" />
    </attr>
</declare-styleable>

2.是否显示折叠效果

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 如果没有变化,测量并返回
    if (!mRelayout || getVisibility() == View.GONE) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        return;
    }
    mRelayout = false;

    // Setup with optimistic case
    // i.e. Everything fits. No button needed
    mLlExpand.setVisibility(View.GONE);
    mTvContent.setMaxLines(Integer.MAX_VALUE);

    // Measure
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    //如果内容真实行数小于等于最大行数,不处理
    if (mTvContent.getLineCount() <= mMaxCollapsedLines) {
        return;
    }
    // 获取内容tv真实高度(含padding)
    mTextHeightWithMaxLines = getRealTextViewHeight(mTvContent);

    // 如果是收起状态,重新设置最大行数
    if (mCollapsed) {
        mTvContent.setMaxLines(mMaxCollapsedLines);
    }
    mLlExpand.setVisibility(View.VISIBLE);

    // Re-measure with new setup
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mCollapsed) {
        // Gets the margin between the TextView's bottom and the ViewGroup's bottom
        mTvContent.post(new Runnable() {
            @Override
            public void run() {
                mMarginBetweenTxtAndBottom = getHeight() - mTvContent.getHeight();
            }
        });
        // 保存这个容器的测量高度
        mCollapsedHeight = getMeasuredHeight();
    }
}

3.设置显示内容

/**
 * @Description 设置显示内容
 */
public void setText(CharSequence text) {
    mRelayout = true;
    mTvContent.setText(text);
    setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
}

4.展开/收起动画

@Override
public void onClick(View view) {
    if (mLlExpand.getVisibility() != View.VISIBLE) {
        return;
    }

    mCollapsed = !mCollapsed;
    // 修改收起/展开图标、文字
    setDrawbleAndText();
    // 保存位置状态
    if (mCollapsedStatus != null) {
        mCollapsedStatus.put(mPosition, mCollapsed);
    }

    // 执行展开/收起动画
    mAnimating = true;
    ValueAnimator valueAnimator;
    if (mCollapsed) {
        valueAnimator = new ValueAnimator().ofInt(getHeight(), mCollapsedHeight);
    } else {
        mCollapsedHeight = getHeight();
        valueAnimator = new ValueAnimator().ofInt(getHeight(), getHeight() + mTextHeightWithMaxLines - mTvContent.getHeight());
    }

    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            int animatedValue = (int) valueAnimator.getAnimatedValue();
            mTvContent.setMaxHeight(animatedValue - mMarginBetweenTxtAndBottom);
            getLayoutParams().height = animatedValue;
            requestLayout();
        }
    });

    valueAnimator.addListener(new Animator.AnimatorListener() {

        @Override
        public void onAnimationStart(Animator animator) {

        }

        @Override
        public void onAnimationEnd(Animator animator) {
            // 动画结束后发送结束的信号,清除动画标志
            mAnimating = false;
            // 通知监听
            if (mListener != null) {
                mListener.onExpandStateChanged(mTvContent, !mCollapsed);
            }
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });

    valueAnimator.setDuration(mAnimationDuration);
    valueAnimator.start();
}

项目地址 ☞ 传送门


版权声明:本文为my_rabbit原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>