天天看点

封装可展开和收缩的View

先上效果图

封装可展开和收缩的View
封装可展开和收缩的View

写在前面:

当时我看到三年二班王尼玛发表高仿哔哩哔哩客户端的SearchView这篇文章(没错,搜索布局就是借用这篇文章的,嘿嘿),当时感觉这个动画很酷,于是就迫不及待的试试。

当你看完上面2个gif,是不是忍不住说,你这搞毛啊,谷歌的ViewAnimationUtils不是已经实现了吗,为什么你还去重复的造轮子,这不是浪费表情浪费青春么,大兄弟莫激动,慢慢听我道来:

ViewAnimationUtils.createCircularReveal()是安卓5.0才引入的,可以快速实现圆形缩放动画,但是在低版本上使用的话,只要你敢用,我分分钟抛createCircularReveal() not found异常给你看。所以想在低版本上使用,只能靠自己的智慧,自己造轮子了,下面直入主题。

既然轮子造好了,正所谓是骡子是马拉出来溜溜

一、使用:

在你项目的build.gradle添加

1.1 自定义属性

属性 属性说明
anim_orientation 动画开始执行的方向
anim_duration 动画时长
centerX 动画开始圆心x
centerY 动画开始圆心y

anim_orientation属性说明:

属性 属性说明
upleft 动画从左上方开始扩散
upright 动画从右上方开始扩散
leftbottom 动画从左下方开始扩散
rightbottom 动画从右下方开始扩散
center 动画从中心开始扩散

布局中

<com.cool.expandviewlibrary.ExpandView
            android:id="@+id/ev_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            android:visibility="invisible"
            app:anim_orientation="center"
            app:anim_duration="500">

            <ImageView
                android:layout_width="350dp"
                android:layout_height="350dp"
                android:scaleType="centerCrop"
                android:src="@mipmap/meizi2" />
        </com.cool.expandviewlibrary.ExpandView>
           

1.2 使用地方

ExpandView  = mImgExpandView = (ExpandView) findViewById(R.id.ev_img);

mImgExpandView.doExpandAnim();//展开动画

mImgExpandView.doPackupAnim();//收缩动画
           

1.3 使用说明:

  • ExpandView只能有一个

    子view

  • 如果需要作展开动画,请将ExpandView设置为invisible,设置为gone是没有效果的

二、实现思路

这种效果可以通过自定义ViewGroup来实现,在父View中拿到子view的对象,再通过子view获取子view对应的bitmap,有了bitmap,我们就可以搞事情了。首先将viewGroup中的子view设置为invisible,然后偷偷的添加一个真正做动画的view,将这个bitmap交给动画view,然后就可以可以使用xfermode或者bitmapShader了。在这里,我两种方式都试过,都能实现最终效果,但是使用BitmapShader是最为简单的,核心代码只有一行,思路说完了,再总结一下,我们需要自定义2个view,一个ViewGroup,继承FrameLayout,一个做动画的view继承view,接下来直接开始撸码了。

三、动画AnimView

3.1 初始化画笔和圆心坐标

private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.WHITE);
        mDuration = ;
        mCirclePoint = new PointF(,);
    }
           

3.2 开始做展开动画

/**
     * 展开动画
     *
     * @param backgroundAnimView
     */
    public void doExpandAnim(View backgroundAnimView) {
        createBackgroundBitmap(backgroundAnimView);
        if(mBackgroundBitmap == null){
            return;
        }
        startExpandAnim();
    }
           

参数backgroundAnimView是自定义viewGroup传过来的子view,通过这个view来创建相对应的bitmap,看看是如何创建bitmap的。

private void createBackgroundBitmap(View backgroundAnimView) {
        width = backgroundAnimView.getWidth();
        height = backgroundAnimView.getHeight();
        if(width <= || height <= ){
            return;
        }
        mBackgroundBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        backgroundAnimView.draw(new Canvas(mBackgroundBitmap));//创建bitmap

        mEndRadius = (float) Math.sqrt(width * width + height * height);//view的对角线,也就是最大的圆心
        BitmapShader bitmapShader = new BitmapShader(mBackgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);//mBackgroundBitmap使用bitmapShader
        mPaint.setShader(bitmapShader);
    }
           

接下来就是开启动画了

private void startPackupAnim() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(mEndRadius, );
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCurrentRadius = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.setDuration(mDuration);
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (onPackupAnimEndListener != null) {
                    onPackupAnimEndListener.onPackupAnimEnd();
                }
                mBackgroundBitmap.recycle();
                mBackgroundBitmap = null;
            }
        });
        valueAnimator.start();
    }
           

onAnimationUpdate不断计算出当前圆心坐标,然后不断的重绘页面,看onDraw()方法

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(mCirclePoint.x, mCirclePoint.y, mCurrentRadius, mPaint);
    }
           

收缩动画和展开动画是一样的,这里就不贴了,下面将目光转向自定义ViewGroup

四、ExpandView

4.1 自定义属性

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandView);
        mAnimOrientation = ta.getInt(R.styleable.ExpandView_anim_orientation,UPLEFT);//动画执行方向
        mAnimDuration = ta.getInt(R.styleable.ExpandView_anim_duration,ANIM_DURATION_DEFAULT);//动画执行时间
        mCenterX = ta.getDimension(R.styleable.ExpandView_centerX,-);//动画开始圆心x坐标
        mCenterY = ta.getDimension(R.styleable.ExpandView_centerY,-);//动画开始圆心y坐标
        ta.recycle();
           

4.2 重写generateLayoutParams()方法,让支持子view的margin属性

@Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
        return new MarginLayoutParams(lp);
    }
           

4.3 重写onMeasure()方法

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int childCount = getChildCount();
        int expectWidth = ;
        int expectHeight = ;

        for (int i = ; i < childCount; i++) {
            View childView = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
            measureChildWithMargins(childView,widthMeasureSpec,,heightMeasureSpec,);
            int childMeasuredWidth = childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
            int childMeasuredHeight = childView.getMeasuredHeight();
            expectWidth = childMeasuredWidth;
            expectHeight = childMeasuredHeight;
        }

        if(widthMode == MeasureSpec.EXACTLY){
            expectWidth = widthSize;
        }else {
            expectWidth = MeasureSpec.makeMeasureSpec(expectWidth,MeasureSpec.EXACTLY);
        }

        if(heightMode == MeasureSpec.EXACTLY){
            expectHeight = heightSize;
        }else {
            expectHeight = MeasureSpec.makeMeasureSpec(expectHeight,MeasureSpec.EXACTLY);
        }

        setMeasuredDimension(expectWidth,expectHeight);
    }
           

首先测量子view的宽高,再设置自身的宽高

4.4 做展开动画doExpandAnim

/**
     * 做展开动画
     */
    public void doExpandAnim() {
        if(isAnimating){//如果还在动画中,什么都不做
            return;
        }
        isAnimating = true;
        setVisibility(VISIBLE);

        int childCount = getChildCount();
        if(childCount >){
            throw new IllegalArgumentException("ExpandView只能有一个子View");
        }
        if(childCount <=){
            return;
        }
        setChildViewVisibility(INVISIBLE);//将子view设置不可见

        if(animView == null) {
            animView = new AnimView(getContext());
        }
        animView.setOnExpandAnimEndListener(this);
        View view = addAnimView(childCount);
        animView.doExpandAnim(view);
    }
           

setChildViewVisibility方法

/**
     * 设置子view显示或隐藏
     * @param visibility 显示或隐藏
     */
    private void setChildViewVisibility(int visibility) {
        int childCount = getChildCount();
        for (int i = ; i < childCount; i++) {
            View childView = getChildAt(i);
            if(childView instanceof AnimView){
                removeView(childView);
            }
            childView.setVisibility(visibility);
        }
    }
           

addAnimView方法将动画view添加到ExpandAnim中并进行相关初始化

/**
     * 获取子view的宽高并添加动画animview
     * @param childCount 孩子个数
     * @return 返回第一个孩子
     */
    @NonNull
    private View addAnimView(int childCount) {
        View view = getChildAt();
        mAnimViewWidth = view.getMeasuredWidth();
        mAnimViewHight = view.getHeight();
        MarginLayoutParams l = (MarginLayoutParams) view.getLayoutParams();
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(mAnimViewWidth,mAnimViewHight);
        layoutParams.leftMargin = l.leftMargin;
        layoutParams.topMargin = l.topMargin;
        layoutParams.rightMargin = l.rightMargin;
        layoutParams.bottomMargin = l.bottomMargin;
        addView(animView, childCount, layoutParams);
        initAnimView(animView);
        return view;
    }
           

initAnimView进行animview的初始化

/**
     * 设置动画view的一些属性
     * @param animView 动画view
     */
    private void initAnimView(AnimView animView){
        animView.setDuration(mAnimDuration);
        animView.setCenterPosition(calculateCirclePoint());
        animView.setStartRadius(mStartRadius);
    }

    /**
     * 计算动画起点圆心
     * @return 起点圆心
     */
    private PointF  calculateCirclePoint(){
        PointF circlePoint = new PointF();
        if(mCenterX != - && mCenterY != -){
            circlePoint.set(mCenterX,mCenterY);
            return circlePoint;
        }
        switch (mAnimOrientation) {
            case UPLEFT:
                mCenterX = mStartRadius;
                mCenterY = mStartRadius;
            break;
            case UPRIGHT:
                mCenterX = mAnimViewWidth -mStartRadius;
                mCenterY = mStartRadius;
            break;
            case LEFTBOTTOM:
                mCenterX = mStartRadius;
                mCenterY = mAnimViewHight - mStartRadius;
            break;
            case RIGHTBOTTOM:
                mCenterX = mAnimViewWidth - mStartRadius;
                mCenterY = mAnimViewHight - mStartRadius;
            break;
            case CENTER:
                mCenterX = mAnimViewWidth /;
                mCenterY = mAnimViewHight /;
            break;
        }
        circlePoint.set(mCenterX,mCenterY);
        return circlePoint;
    }
           

4.5做收缩动画

/**
     * 做收起动画
     */
    public void doPackupAnim() {
        if(isAnimating){
            return;
        }
        isAnimating = true;
        setVisibility(VISIBLE);

        int childCount = getChildCount();
        setChildViewVisibility(GONE);

        if(childCount <=){
            return;
        }
        if(animView == null) {
            animView = new AnimView(getContext());
        }
        animView.setOnPackupAnimEndListener(this);

        View view = addAnimView(childCount);
        animView.doPackupAnim(view);
    }
           

4.6展开动画和收缩动画完成时的监听

@Override
    public void onExpandAnimEnd() {
        isAnimating = false;
        if (animView != null) {
            removeView(animView);
        }
        setChildViewVisibility(VISIBLE);

    }

    @Override
    public void onPackupAnimEnd() {
        isAnimating = false;
        if (animView != null) {
            removeView(animView);
        }
        setVisibility(INVISIBLE);
    }
           

五,两个view的完整版

ExpandView

/**
 * Created by cool on 2017/8/3.
 */

public class ExpandView extends FrameLayout implements AnimView.OnExpandAnimEndListener, AnimView.OnPackupAnimEndListener {

    private int mAnimOrientation;
    private int mAnimDuration;//动画时长
    private float mCenterX;//动画开始圆心x坐标
    private float mCenterY;//动画开始圆心y坐标
    private final static int UPLEFT = ;//左上
    private final static int UPRIGHT = ;//右上
    private final static int LEFTBOTTOM = ;//左下
    private final static int RIGHTBOTTOM = ;//右下
    private final static int CENTER = ;//中间
    private final static int ANIM_DURATION_DEFAULT = ;//动画默认时长
    private float mStartRadius;//开始执行时的圆半径
    private int mAnimViewWidth;
    private int mAnimViewHight;
    private boolean isAnimating = false;//是否在动画中

    private AnimView animView;

    public ExpandView(@NonNull Context context) {
        this(context, null);
    }

    public ExpandView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, );
    }

    public ExpandView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandView);
        mAnimOrientation = ta.getInt(R.styleable.ExpandView_anim_orientation,UPLEFT);
        mAnimDuration = ta.getInt(R.styleable.ExpandView_anim_duration,ANIM_DURATION_DEFAULT);
        mCenterX = ta.getDimension(R.styleable.ExpandView_centerX,-);
        mCenterY = ta.getDimension(R.styleable.ExpandView_centerY,-);
        ta.recycle();
        init();
    }

    private void init() {
        mStartRadius = dp2px();
    }

    /**
     * 做展开动画
     */
    public void doExpandAnim() {
        if(isAnimating){
            return;
        }
        isAnimating = true;
        setVisibility(VISIBLE);

        int childCount = getChildCount();
        if(childCount >){
            throw new IllegalArgumentException("ExpandView只能有一个子View");
        }
        if(childCount <=){
            return;
        }
        setChildViewVisibility(INVISIBLE);

        if(animView == null) {
            animView = new AnimView(getContext());
        }
        animView.setOnExpandAnimEndListener(this);
        View view = addAnimView(childCount);
        animView.doExpandAnim(view);
    }

    /**
     * 做收起动画
     */
    public void doPackupAnim() {
        if(isAnimating){
            return;
        }
        isAnimating = true;
        setVisibility(VISIBLE);

        int childCount = getChildCount();
        setChildViewVisibility(GONE);

        if(childCount <=){
            return;
        }
        if(animView == null) {
            animView = new AnimView(getContext());
        }
        animView.setOnPackupAnimEndListener(this);

        View view = addAnimView(childCount);
        animView.doPackupAnim(view);
    }

    /**
     * 获取子view的宽高并添加动画animview
     * @param childCount 孩子个数
     * @return 返回第一个孩子
     */
    @NonNull
    private View addAnimView(int childCount) {
        View view = getChildAt();
        mAnimViewWidth = view.getMeasuredWidth();
        mAnimViewHight = view.getHeight();
        MarginLayoutParams l = (MarginLayoutParams) view.getLayoutParams();
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(mAnimViewWidth,mAnimViewHight);
        layoutParams.leftMargin = l.leftMargin;
        layoutParams.topMargin = l.topMargin;
        layoutParams.rightMargin = l.rightMargin;
        layoutParams.bottomMargin = l.bottomMargin;
        addView(animView, childCount, layoutParams);
        initAnimView(animView);
        return view;
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
        return new MarginLayoutParams(lp);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int childCount = getChildCount();
        int expectWidth = ;
        int expectHeight = ;

        for (int i = ; i < childCount; i++) {
            View childView = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
            measureChildWithMargins(childView,widthMeasureSpec,,heightMeasureSpec,);
            int childMeasuredWidth = childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
            int childMeasuredHeight = childView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
            expectWidth = childMeasuredWidth;
            expectHeight = childMeasuredHeight;
        }

        if(widthMode == MeasureSpec.EXACTLY){
            expectWidth = widthSize;
        }else {
            expectWidth = MeasureSpec.makeMeasureSpec(expectWidth,MeasureSpec.EXACTLY);
        }

        if(heightMode == MeasureSpec.EXACTLY){
            expectHeight = heightSize;
        }else {
            expectHeight = MeasureSpec.makeMeasureSpec(expectHeight,MeasureSpec.EXACTLY);
        }

        setMeasuredDimension(expectWidth,expectHeight);
    }

    /**
     * 设置子view显示或隐藏
     * @param visibility 显示或隐藏
     */
    private void setChildViewVisibility(int visibility) {
        int childCount = getChildCount();
        for (int i = ; i < childCount; i++) {
            View childView = getChildAt(i);
            if(childView instanceof AnimView){
                removeView(childView);
            }
            childView.setVisibility(visibility);
        }
    }

    /**
     * 设置动画view的一些属性
     * @param animView 动画view
     */
    private void initAnimView(AnimView animView){
        animView.setDuration(mAnimDuration);
        animView.setCenterPosition(calculateCirclePoint());
        animView.setStartRadius(mStartRadius);
    }

    /**
     * 计算动画起点圆心
     * @return 起点圆心
     */
    private PointF  calculateCirclePoint(){
        PointF circlePoint = new PointF();
        if(mCenterX != - && mCenterY != -){
            circlePoint.set(mCenterX,mCenterY);
            return circlePoint;
        }
        switch (mAnimOrientation) {
            case UPLEFT:
                mCenterX = mStartRadius;
                mCenterY = mStartRadius;
            break;
            case UPRIGHT:
                mCenterX = mAnimViewWidth -mStartRadius;
                mCenterY = mStartRadius;
            break;
            case LEFTBOTTOM:
                mCenterX = mStartRadius;
                mCenterY = mAnimViewHight - mStartRadius;
            break;
            case RIGHTBOTTOM:
                mCenterX = mAnimViewWidth - mStartRadius;
                mCenterY = mAnimViewHight - mStartRadius;
            break;
            case CENTER:
                mCenterX = mAnimViewWidth /;
                mCenterY = mAnimViewHight /;
            break;
        }
        circlePoint.set(mCenterX,mCenterY);
        return circlePoint;
    }

    @Override
    public void onExpandAnimEnd() {
        isAnimating = false;
        if (animView != null) {
            removeView(animView);
        }
        setChildViewVisibility(VISIBLE);

    }

    @Override
    public void onPackupAnimEnd() {
        isAnimating = false;
        if (animView != null) {
            removeView(animView);
        }
        setVisibility(INVISIBLE);
    }

    private int dp2px(int dp){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
    }
}
           

AnimView

/**
 * Created by cool on 2017/8/3.
 */

public class AnimView extends View {

    private Bitmap mBackgroundBitmap;
    private Paint mPaint;
    private float mEndRadius;
    private float mStartRadius;
    private PointF mCirclePoint;//封装圆心坐标
    private float mCurrentRadius = mStartRadius;
    private int width;
    private int height;
    private long mDuration;

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

    public AnimView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, );
    }

    public AnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.WHITE);
        mDuration = ;
        mCirclePoint = new PointF(,);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(mCirclePoint.x, mCirclePoint.y, mCurrentRadius, mPaint);
    }

    /**
     * 展开动画
     *
     * @param backgroundAnimView
     */
    public void doExpandAnim(View backgroundAnimView) {
        createBackgroundBitmap(backgroundAnimView);
        if(mBackgroundBitmap == null){
            return;
        }
        startExpandAnim();
    }

    /**
     * 收起动画
     */
    public void doPackupAnim(View backgroundAnimView) {
        createBackgroundBitmap(backgroundAnimView);
        if(mBackgroundBitmap == null){
            return;
        }
        startPackupAnim();
    }

    private void createBackgroundBitmap(View backgroundAnimView) {
        width = backgroundAnimView.getWidth();
        height = backgroundAnimView.getHeight();
        if(width <= || height <= ){
            return;
        }
        mBackgroundBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        backgroundAnimView.draw(new Canvas(mBackgroundBitmap));

        mEndRadius = (float) Math.sqrt(width * width + height * height);
        BitmapShader bitmapShader = new BitmapShader(mBackgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint.setShader(bitmapShader);
    }

    private void startPackupAnim() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(mEndRadius, );
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCurrentRadius = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.setDuration(mDuration);
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (onPackupAnimEndListener != null) {
                    onPackupAnimEndListener.onPackupAnimEnd();
                }
                mBackgroundBitmap.recycle();
                mBackgroundBitmap = null;
            }
        });
        valueAnimator.start();
    }

    private void startExpandAnim() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(mStartRadius, mEndRadius);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCurrentRadius = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (onExpandAnimEndListener != null) {
                    onExpandAnimEndListener.onExpandAnimEnd();
                }
                mBackgroundBitmap.recycle();
                mBackgroundBitmap = null;
            }
        });
        valueAnimator.setDuration(mDuration);
        valueAnimator.start();
    }

    /**
     * 设置动画时长
     * @param duration 时长
     */
    public void setDuration(long duration){
        this.mDuration = duration;
    }

    /**
     * 设置圆心坐标
     * @param point 圆心坐标
     */
    public void setCenterPosition(PointF point){
        this.mCirclePoint = point;
    }

    /**
     * 设置开始是圆半径
     * @param startRadius 圆半径
     */
    public void setStartRadius(float startRadius){
        this.mStartRadius = startRadius;
    }

    private OnExpandAnimEndListener onExpandAnimEndListener;

    public void setOnExpandAnimEndListener(OnExpandAnimEndListener listener) {
        this.onExpandAnimEndListener = listener;
    }

    public interface OnExpandAnimEndListener {
        void onExpandAnimEnd();
    }

    private OnPackupAnimEndListener onPackupAnimEndListener;

    public void setOnPackupAnimEndListener(OnPackupAnimEndListener listener){
        this.onPackupAnimEndListener = listener;
    }

    public interface OnPackupAnimEndListener{
        void onPackupAnimEnd();
    }
}
           

如果有问题,欢迎指出

源码地址:https://github.com/lkkz/ExpandView

欢迎star,issuse