封装可展开和收缩的View

先上效果图

展开&收缩美女
封装可展开和收缩的View_第1张图片
展开&收缩搜索view

写在前面:

当时我看到到三年二班王尼玛发表高仿哔哩哔哩客户端的SearchView这篇文章(没错,搜索布局就是借用这篇文章的,嘿嘿),当时感觉这个动画很酷,于是就迫不及待的试试。
当你看完上面2个gif,是不是忍不住说,你这搞毛啊,谷歌的ViewAnimationUtils不是已经实现了吗,为什么你还去重复的造轮子,这不是浪费表情浪费青春么,大兄弟莫激动,慢慢听我道来:
ViewAnimationUtils.createCircularReveal()是安卓5.0才引入的,可以快速实现圆形缩放动画,但是在低版本上使用的话,只要你敢用,我分分钟抛createCircularReveal() not found异常给你看。所以想在低版本上使用,只能靠自己的智慧,自己造轮子了,下面直入主题。

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

一、使用:

在你项目的build.gradle添加

compile 'com.cool:expandview:1.0.1'

1.1 自定义属性

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

anim_orientation属性说明:

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

布局中

  

            
        

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 = 500;
        mCirclePoint = new PointF(0,0);
    }

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 <=0 || height <= 0){
            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, 0);
       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,-1);//动画开始圆心x坐标
        mCenterY = ta.getDimension(R.styleable.ExpandView_centerY,-1);//动画开始圆心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 = 0;
        int expectHeight = 0;

        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
            measureChildWithMargins(childView,widthMeasureSpec,0,heightMeasureSpec,0);
            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 >1){
            throw new IllegalArgumentException("ExpandView只能有一个子View");
        }
        if(childCount <=0){
            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 = 0; 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(0);
        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 != -1 && mCenterY != -1){
            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 /2;
                mCenterY = mAnimViewHight /2;
            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 <=0){
            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 = 1;//左上
    private final static int UPRIGHT = 2;//右上
    private final static int LEFTBOTTOM = 3;//左下
    private final static int RIGHTBOTTOM = 4;//右下
    private final static int CENTER = 5;//中间
    private final static int ANIM_DURATION_DEFAULT = 500;//动画默认时长
    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, 0);
    }

    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,-1);
        mCenterY = ta.getDimension(R.styleable.ExpandView_centerY,-1);
        ta.recycle();
        init();
    }

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

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

        int childCount = getChildCount();
        if(childCount >1){
            throw new IllegalArgumentException("ExpandView只能有一个子View");
        }
        if(childCount <=0){
            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 <=0){
            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(0);
        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 = 0;
        int expectHeight = 0;

        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
            measureChildWithMargins(childView,widthMeasureSpec,0,heightMeasureSpec,0);
            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 = 0; 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 != -1 && mCenterY != -1){
            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 /2;
                mCenterY = mAnimViewHight /2;
            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, 0);
    }

    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 = 500;
        mCirclePoint = new PointF(0,0);
    }

    @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 <=0 || height <= 0){
            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, 0);
        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

你可能感兴趣的:(封装可展开和收缩的View)