关于ExpandableTextView几点优化

作者 : 开心源码 本文共7526个字,预计阅读时间需要19分钟 发布时间: 2022-05-12 共194人阅读

前一段时间公司项目需要使用到相似于朋友圈效果的折叠和收起功能。
1.点击翻译时,全文开展,并显示下方翻译结果;
2.点击收起翻译时,全文收起,翻译结果隐藏;
3.item开展或者收起状态需要保存。上网搜索到了Manabu-GT/ExpandableTextView和Chen-Sir/ExpandableTextView,三下五除二快速完成交给测试,简直so easy!

但是随后测试提交给我的bug却给我了很大的难题:

1.内容足够长,超出一屏, mCollapsedHeight计算的有问题;
2.当显示文字的View错位的时候,点击“收起/开展”事件无效。
3.屡次滑动列表过程中,重复点击“收起/开展”操作时,有时文字不可见,并“收起/开展”按钮消失;

图1 APP效果图展现.png

为何会出现上述情况,首页先ExpandableTextView看看有木有处理办法,但是看了一圈的Issue,上面出现的问题仍然没有得到处理。下面记录着我是如何处理问题和分享问题的思路,仅供参考,不肯定适使用于所使用项目。

一、内容足够长,超出一屏, mCollapsedHeight为0的处理方法

从下面的ExpandableTextView可以看出折叠高度在OnMeasure获取,当点击“收起/开展”按钮时,将高度赋给View,目前按程序代码上看没有什么大的问题。那么就只能从Debug出手,在Debug跟踪过程中,我们发现在点击“收起”时,mCollapsedHeight高度为0。明明我们存储高度,为何高度为0呢?Why??

   @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        if (!mRelayout || getVisibility() == View.GONE) {            super.onMeasure(widthMeasureSpec, heightMeasureSpec);            return;        }        mRelayout = false;        ...        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (mTv.getLineCount() <= mMaxCollapsedLines) {            return;        }        ...        // Re-measure with new setup        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (mCollapsed) {            ...            mCollapsedHeight = getMeasuredHeight();            if (mListener != null) {                mListener.onCollapsedHeight(mCollapsedHeight);            }        }    }    @Override    public void onClick(View view) {        ...        Animation animation;        if (mCollapsed) {            animation = new ExpandCollapseAnimation(this, getHeight(), mCollapsedHeight);        } else {            animation = new ExpandCollapseAnimation(this, getHeight(), getHeight() +                    mTextHeightWithMaxLines - mTv.getHeight());        }        ...    }   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;        }    }

这时我们要冷静下来,先分析一波,首先我使用的RecyclerView,ExpandableTextView放在item中,这会不会是View错位而引发的问题呢?果然Debug中,我查到当前View的已不是同一个。这时我做了分析,首先ExpandableTextView在OnMeasure拿到View的高度是折叠时的高度,当屡次RecyclerView列表后,点击“收起”按钮,我们应该将高度赋值进ExpandableTextView,根据产品的需求特性,我们对代码进行如下修改(PS:各位可以根据自己项目实况,做相应的修改):

1.将测量之后的高度放到监听事件中
2.在Adapter中将监听事件的高度赋值给全局变量;
3.在RecyclerView滑动时,会重新执行onBindViewHolder方法,此时将高度传入ExpandableTextView中

ExpandableTextView源码中

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        ...        if (mCollapsed) {            ...            if (mListener != null) {                mListener.onCollapsedHeight(mCollapsedHeight);            }        }    }    public void setmCollapsedHeight(int mCollapsedHeight) {        this.mCollapsedHeight = mCollapsedHeight;    }    public interface OnExpandStateChangeListener {        void onExpandStateChanged(TextView textView, boolean isExpanded);        void onCollapsedHeight(int mCollapsedHeight);    }

RecyclerView中Adapter的部分代码

public class FeedAllRvAdapter extends RecyclerView.Adapter<FeedAllRvAdapter.FeedViewHolder> implements Const {    private int mCollapsedHeight = -1;    ...    @Override    public FeedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        ...        if (onFeedAllAdapterListener != null) {            holder.tvContent.setOnExpandStateChangeListener(new ExpandableTextView.OnExpandStateChangeListener() {                @Override                public void onExpandStateChanged(TextView textView, boolean isExpanded) {                    lists.get(holder.position).setmCollapsedStatus(!isExpanded);                    onFeedAllAdapterListener.toMixpanelTrack(isExpanded);                }                @Override                public void onCollapsedHeight(int mCollapsedHeight) {                    FeedAllRvAdapter.this.mCollapsedHeight = mCollapsedHeight;                }            });        }        return holder;    }    @Override    public void onBindViewHolder(final FeedViewHolder holder, int position) {        ...        if (mCollapsedHeight != -1) {            holder.tvContent.setmCollapsedHeight(mCollapsedHeight);        }        ...    }}

二、当显示文字的View错位的时候,点击“收起/开展”事件无效

经过上面的代码修改,超过一屏之长问题得到处理,但是屡次进行滑动和“收起/开展”的操作时,偶现当View中文字错位时,“开展/收起”的点击事件无效,重新下拉刷新列表,依然点击事件无效。

    @Override    public void onClick(View view) {        ...        mAnimating = true;        ...        animation.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {                applyAlphaAnimation(mTv, mAnimAlphaStart);            }            @Override            public void onAnimationEnd(Animation animation) {                clearAnimation();                mAnimating = false;                ...            }            @Override            public void onAnimationRepeat(Animation animation) { }        });       ...    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return mAnimating;    }

从上面代码中我们看出当前View能否响应事件,是onInterceptTouchEvent的状态决定的。于是我Debug调试,发现出现改情况是mAnimating状态总是为false,那么我们就知道问了,是动画结束的监听没有执行。

onInterceptTouchEvent()是使用于解决事件(相似于预解决,当然也可以不解决)并改变事件的传递方向,也就是决定能否允许Touch事件继续向下(子控件)传递,一但返回True(代表事件在当前的viewGroup中会被解决),则向下传递之路被截断(所有子控件将没有机会参加Touch事件),同时把事件传递给当前的控件的onTouchEvent()解决;返回false,则把事件交给子控件的onInterceptTouchEvent(),因而我们去查看mAnimating状态的变化。

于是度娘发现一个比较有说服力的理由。

动画播放完毕之后给我们的回调onAnimationEnd函数里面可能系统有少量逻辑没有执行,我们就执行了清理动画等操作,没有给系统留出肯定的时间去解决。

在ExpandableTextView中Issue也有人提出过可能是动画问题,于是我使用ObjectAnimator动画来替换该动画

@Override    public void onClick(View view) {        if (mStateTv.getVisibility() != View.VISIBLE) {            return;        }        mCollapsed = !mCollapsed;        mStateTv.setText(mCollapsed ? mExpandString : mCollapsedString);        mAnimating = true;        ObjectAnimator animator3 = ObjectAnimator.ofFloat(this.view, "alpha", 1f, 0f);//变淡        final AnimatorSet set = new AnimatorSet();        set.playTogether(animator3);        set.setDuration(mAnimationDuration).start();        animator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int mStartHeight = getHeight();                int mEndHeight;                if (mCollapsed) {                    mEndHeight = mCollapsedHeight;                } else {                    mEndHeight = getHeight() + mTextHeightWithMaxLines - mTv.getHeight();                }                final int newHeight = (int) ((mEndHeight - mStartHeight) * animation.getAnimatedFraction() + mStartHeight);                mTv.setMaxHeight(newHeight - mMarginBetweenTxtAndBottom);                ExpandableTextView.this.getLayoutParams().height = newHeight;                ExpandableTextView.this.requestLayout();            }        });        set.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {                     clearAnimation();                   mAnimating = false;                if (mListener != null) {                    mListener.onExpandStateChanged(mTv, !mCollapsed);                }            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });    }

三、屡次点击“收起/开展”按钮,偶现文字消失的情况

我们知道在ListView、RecyclerView等控件中,每个Item是与数据进行一对一的绑定,那么现在就好办了。将开展能否开展和收起的状态放在实体类中,并与上面获取高度的方法一起使用,能够达到效果。RecyclerView滑动时,onBindView将该状态赋值。同时也可处理Recyclerview加载更多同时开展全文,而引起的空白问题。
Bean实体类中的字段我就在不在形容了。

ExpandableTextView修改如下

public void setText(@Nullable SpannableStringBuilder originContent,     boolean isCollapsed) {       clearAnimation();       mCollapsed = isCollapsed;       mStateTv.setText(mCollapsed ? mExpandString : mCollapsedString);       setText(originContent);       getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;       requestLayout();   }   public void setText(@Nullable SpannableStringBuilder originContent) {       mRelayout = true;//        mTv.setText(text);       mTv.setText(originContent);       setVisibility(TextUtils.isEmpty(originContent) ? View.GONE : View.VISIBLE);       mTv.setMovementMethod(LinkMovementClickMethod.getInstance());   }

Adapter中修改

@Override    public void onBindViewHolder(final FeedViewHolder holder, int position) {                if (mCollapsedHeight != -1) {            holder.tvContent.setmCollapsedHeight(mCollapsedHeight);        }        holder.tvContent.setText(feedBean.getShowContent(), feedBean.ismCollapsedStatus());    }

由于工作中遇到的这些问题,真的很辣手,多亏了顾爷和小秦的帮助,才让我赶在上线之前完成开发。这也督促需要多多学习,这也是加强了我开始写博客记录自己工作中遇到问题及如何处理问题的决心,感谢他们。最后因为本人第一次写博客,如有问题,还请多多指点,多多包涵。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 关于ExpandableTextView几点优化

发表回复