TextView实现展开收起的效果

作者: alighters | 来源:发表于2015-11-08 23:07 被阅读11201次

    在做Android的过程中,我们经常会遇到TextView显示文本过长的情况,这里我们以开源库ExpandableTextView为例,对其的实现做一讲解:

    实现原理描述:expandableTextView继承自LinearLayout(只支持竖直方向),包含TextView和ImageButton两个子view,通过参数 maxCollapsedLines来设置折叠最大的行数,在onMeasure方法中,判断textView的行数,来进行textview折叠和收起状态的设定;另外,收起和展开的效果则针对TextView的高度进行动画。

    ** 具体使用方法** :

    • 布局:使用ExpandableTextView作为TextView(指定id为expandable_text)和ImageButton(指定id为expand_collapse)的父View.
    • 设值:设定ExpandableText的maxCollapsedLines为折叠的行数。

    ** 实现细节:**

    • ExpandableTextView是如何确定子view的?
      答:是通过重写ViewGroup一个回调onFinishInflate()方法,来获取对应的子view(TextView跟ImageButton).
    • OnMeasure中执行了哪些操作?
      答:具体是进行根据展开折叠状态的判断,来设定当前textView的行数以及点击事件和imageButton的显示状态。具体代码如下:
    @Override
    
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    // If no change, measure and return
    
    if (!mRelayout || getVisibility() == View.GONE) {
    
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
    return;
    
    }
    
    mRelayout = false;
    
    // Setup with optimistic case
    
    // i.e. Everything fits. No button needed
    
    mButton.setVisibility(View.GONE);
    
    mTv.setMaxLines(Integer.MAX_VALUE);
    
    // Measure
    
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
    // If the text fits in collapsed mode, we are done.
    
    if (mTv.getLineCount() <= mMaxCollapsedLines) {
    
    return;
    
    }
    
    // Saves the text height w/ max lines
    
    mTextHeightWithMaxLines = getRealTextViewHeight(mTv);
    
    // Doesn't fit in collapsed mode. Collapse text view as needed. Show
    
    // button.
    
    if (mCollapsed) {
    
    mTv.setMaxLines(mMaxCollapsedLines);
    
    }
    
    mButton.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
    
    mTv.post(new Runnable() {
    
    @Override
    
    public void run() {
    
    mMarginBetweenTxtAndBottom = getHeight() - mTv.getHeight();
    
    }
    
    });
    
    // Saves the collapsed height of this ViewGroup
    
    mCollapsedHeight = getMeasuredHeight();
    
    }
    
    }
    
    • 收起展开动画的实现?
      答:这里实现定义了一个扩展收起的动画,主要针对整个View的高度做动画,因为我们已经知道了view折叠之后的高度和扩展之后的高度(可以算出来),然后修改布局就好。
      具体的动画代码如下:
    class ExpandCollapseAnimation extends Animation {
    
    private final View mTargetView;
    
    private final int mStartHeight;
    
    private final int mEndHeight;
    
    public ExpandCollapseAnimation(View view, int startHeight, int endHeight) {
    
    mTargetView = view;
    
    mStartHeight = startHeight;
    
    mEndHeight = endHeight;
    
    setDuration(mAnimationDuration);
    
    }
    
    @Override
    
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    
    final int newHeight = (int)((mEndHeight - mStartHeight) * interpolatedTime + mStartHeight);
    
    mTv.setMaxHeight(newHeight - mMarginBetweenTxtAndBottom);
    
    if (Float.compare(mAnimAlphaStart, 1.0f) != 0) {
    
    applyAlphaAnimation(mTv, mAnimAlphaStart + interpolatedTime * (1.0f - mAnimAlphaStart));
    
    }
    
    mTargetView.getLayoutParams().height = newHeight;
    
    mTargetView.requestLayout();
    
    }
    
    @Override
    
    public void initialize( int width, int height, int parentWidth, int parentHeight ) {
    
    super.initialize(width, height, parentWidth, parentHeight);
    
    }
    
    @Override
    
    public boolean willChangeBounds( ) {
    
    return true;
    
    }
    
    };
    

    动画调用的代码是在view的点击事件回调中:

    Animation animation;
    
    if (mCollapsed) {
    
    animation = new ExpandCollapseAnimation(this, getHeight(), mCollapsedHeight);
    
    } else {
    
    animation = new ExpandCollapseAnimation(this, getHeight(), getHeight() +
    
    mTextHeightWithMaxLines - mTv.getHeight());
    
    }
    

    我们这里就会产生一个疑惑,mTextHeightWithMaxLines(文本全部显示对应的textview的高度)是怎么获取到的呢?相应获取的代码如下:

    private static int getRealTextViewHeight(@NonNull TextView textView) {
    
    int textHeight = textView.getLayout().getLineTop(textView.getLineCount());
    
    int padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom();
    
    return textHeight + padding;
    
    }
    

    这里有两个主要的方法,getLineCount获取textview的行数,getLineTop获取textview的行高。
    至此,我们就可以实现整个expandableTextView的收起展开的效果了。

    • 如何在ListView中确保当前textView的状态?
      我们知道在多列表view中,都要考虑到view重用的问题,若是expandableTextView不能在这种情况下使用的话,那对我们的使用情景就大有限制了。在这里,在调用setText的时候,提供了SparseBooleanArray collapseStatus和int position来记录当前的展开收起状态了。所以,就需要我们在我们的adapter中也使用相同的数据结构了。具体代码如下:
    public void setText(@Nullable CharSequence text, @NonNull SparseBooleanArray collapsedStatus, int position) {
    
    mCollapsedStatus = collapsedStatus;
    
    mPosition = position;
    
    boolean isCollapsed = collapsedStatus.get(position, true);
    
    clearAnimation();
    
    mCollapsed = isCollapsed;
    
    mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);
    
    setText(text);
    
    getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
    
    requestLayout();
    
    }
    

    最后,我们关于ExpandableTextView的实现原理就讲完了,具体还有疑问的话,可以提出一起讨论。另外,代码的格式没有做缩进,大家多多担待。灰常感谢。。

    相关文章

      网友评论

        本文标题:TextView实现展开收起的效果

        本文链接:https://www.haomeiwen.com/subject/akpchttx.html