封装可展开和收缩的View

先上效果图

展开&收缩美女
展开&收缩搜索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 动画从中心开始扩散

布局中

  <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 = 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,756评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,059评论 4 62
  • 一句话,坑多需谨慎!下面我们来开始填坑: 需要下载的辅助软件我已经都下载好,直接下载会比较慢,可以直接去我云盘里下...
    呉囲仌犮yzx阅读 483评论 0 0
  • 关于增高有什么常识? 矮小成了很多人的痛苦,但是现在很多人却出现了矮小的情况,很多的患者因此想通过增高药来解决自己...
    小仪仪阅读 477评论 0 2
  • 晚上姥姥和姥爷要去串门,我也跟着一起去了。是去的我大姥姥家。给我大姥姥送礼去。到了大姥姥家,还有好多人,有的我都不...
    朱嘉诚11阅读 195评论 0 0