仿地图弹窗

首先看效果图

当然了这个高度和宽度是可控的。想详细修改的话去代码里面修改吧.

使用方法

BouncingMenu.makeMenu(findViewById(R.id.rl), R.layout.layout_rv_sweet, adapter).show();

是的,你没看错,就是这么简单。
只需要传入view,布局id,以及adapter(这里使用的是recycleview.Adapter)。想修改其他的话直接去修改代码吧。

具体实现

首先是activity的布局,很简单,就是放了各种ImageView,然后上面放了个searchView。
这里就不写详细布局了
弹出视图

这里把视图分成2部分了。

  1. RecycleView部分。使用的是LayoutAnimation。从下往上
布局:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:orientation="vertical">

        <RelativeLayout
            android:id="@+id/freeGrowUpParent"
            android:layout_width="match_parent"
            android:layout_height="@dimen/sheet_height"
            android:layout_gravity="bottom"
            android:background="@android:color/transparent">
            <!--自定义控件,就是那个波动view-->
            <com.xie.designpatterns.widget.BouncingView
                android:id="@+id/sv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp" />

            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="bottom"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="30dp"
                android:clipChildren="false"
                android:overScrollMode="never" />

        </RelativeLayout>
    </FrameLayout>

BouncingMenu:

    public class BouncingMenu {

        private final ViewGroup mParentViewGroup;
        private View rootView;
        private final BouncingView boucingView;
        private final RecyclerView recycleView;
        private RecyclerView.Adapter adapter;
        private int screenWidht;
        private int screenHeight;

        /**
         * @param resId   布局资源id
         * @param adapter
         */
        public BouncingMenu(View view, int resId, RecyclerView.Adapter adapter) {
            this.adapter = adapter;
            //不断地往上追溯找到 系统id为content的帧布局,添加进去.这里就是利用了这个。
            // @android:id/content 就是锚点view
            mParentViewGroup = findRootParent(view);
            //渲染
            rootView = LayoutInflater.from(view.getContext()).inflate(resId, null, false);
            //拿到高度,然后把rootview的高度设置为0
            Logger.i("视图的跟布局是" + rootView);
            boucingView = (BouncingView) rootView.findViewById(R.id.sv);
            boucingView.setAnimationListener(new MyAnimationListener());
            recycleView = (RecyclerView) rootView.findViewById(R.id.rv);
            recycleView.setAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.rv_layout_animation));
            recycleView.setLayoutManager(new LinearLayoutManager(view.getContext()));

            getScreentWight(view.getContext());
        }

        private ViewGroup findRootParent(View view) {
            do {
                //判断是帧布局
                if (view instanceof FrameLayout) {
                    if (view.getId() == android.R.id.content) {
                        //判断布局id为content
                        return (ViewGroup) view;
                    }
                }
                //没找到 ,就不断的往上找
                if (view != null) {
                    ViewParent parent = view.getParent();
                    //如果父布局是view的话往上继续找
                    view = parent instanceof View ? (View) parent : null;
                }
            } while (view != null);
            return null;
        }

        public static BouncingMenu makeMenu(View view, int resId, RecyclerView.Adapter adapter) {

            return new BouncingMenu(view, resId, adapter);
        }

        /**
         * 显示
         *
         * @return
         */
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        public BouncingMenu show() {
            //显示,往帧布局里面addView(xxxView)
            if (rootView.getParent() != null) {
                //防止已经show了再次show
                mParentViewGroup.removeView(rootView);
            }
            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
            rootView.setLayoutParams(params);
            mParentViewGroup.addView(rootView);
            boucingView.show();
            return this;
        }

        /**
         * 关闭
         */
        public void dissmiss() {
            //属性动画实现消失
            ObjectAnimator animator = ObjectAnimator.ofFloat(rootView, "translationY", 0, rootView.getHeight());
            animator.setDuration(1000);
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    //动画结束
                    super.onAnimationEnd(animation);
                    mParentViewGroup.removeView(rootView);
                    rootView = null;
                }
            });
            animator.start();
        }

        private class MyAnimationListener implements BouncingView.AnimationListener {
            @Override
            public void onShowContent() {
                recycleView.setVisibility(View.VISIBLE);
                recycleView.setAdapter(adapter);
                recycleView.scheduleLayoutAnimation();
            }
        }

        private void getScreentWight(Context context) {
            WindowManager wm = (WindowManager) context
                    .getSystemService(Context.WINDOW_SERVICE);

            screenWidht = wm.getDefaultDisplay().getWidth();
            screenHeight = wm.getDefaultDisplay().getHeight();
        }
    }
  1. BouncingView(弹动部分)

    public class BouncingView extends View {
        private Paint mPaint;
        private int mArcHeight;//当前弧度
        private int mMaxArcHeight;//最高弧度
        private Status mStatus = Status.NONE;
        private Path mPath = new Path();
        private AnimationListener animationListener;
    
        private enum Status {
            NONE,
            STATUS_SMOOTH_UP,
            STATUS_DOWN
        }
    
        public BouncingView(Context context) {
            super(context);
            init();
        }
    
        public BouncingView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(getResources().getColor(android.R.color.white));
        mMaxArcHeight = getResources().getDimensionPixelSize(R.dimen.arc_max_height);
    }


    //绘制贝塞尔曲线
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int currentPointY = 0;
        //判断当前状态
        switch (mStatus) {
            case NONE:
                currentPointY = 0;
                break;
            case STATUS_SMOOTH_UP://往上走
                //currentPointY 的值 跟mArcHeight变化率是一样的
                //getHeight()~0  (自定义控件最上面是0)           跟mArcHeight:0~mMaxArcHeight
                currentPointY = (int) (getHeight() * (1 - (float) mArcHeight / mMaxArcHeight) + mMaxArcHeight);
                break;
            case STATUS_DOWN://往下走
                currentPointY = mMaxArcHeight;
                break;

        }
        mPath.reset();
        //先落笔到左上角
        mPath.moveTo(0, currentPointY);
        //画上面的线二阶贝塞尔曲线  ,需要起始点 终点,跟拐点。起点就是(0,currentPotintY),拐点就是最中间(getWidth()/2,)
        mPath.quadTo(getWidth() / 2, currentPointY - mArcHeight, getWidth(), currentPointY);
        //右边线
        mPath.lineTo(getWidth(), getHeight());
        //最下面的直线
        mPath.lineTo(0, getHeight());
        //自动画最后一条线
        mPath.close();
        canvas.drawPath(mPath, mPaint);
    }

    public void show() {

        if (animationListener != null) {
            this.postDelayed(new Runnable() {
                @Override
                public void run() {
                    //延迟显示数据
                    animationListener.onShowContent();
                }
            }, 600);
        }

        //不短的控制mArcHeight的高度
        mStatus = Status.STATUS_SMOOTH_UP;
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, mMaxArcHeight);
        valueAnimator.setDuration(800);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mArcHeight = (int) animation.getAnimatedValue();
                if (mArcHeight == mMaxArcHeight) {
                    //出现谈一下动画
                    bounce();
                }
                //不断的刷新调用ondraw方法
                invalidate();
            }


        });
        valueAnimator.start();
    }

    /**
     * 回弹动画
     */
    private void bounce() {
        mStatus = Status.STATUS_DOWN;
        ValueAnimator valueAnimator = ValueAnimator.ofInt(mMaxArcHeight, 0);
        valueAnimator.setDuration(800);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mArcHeight = (int) animation.getAnimatedValue();
                //不断的刷新调用ondraw方法
                invalidate();
            }


        });
        valueAnimator.start();
    }

    public void setAnimationListener(AnimationListener listener) {
        this.animationListener = listener;
    }

    public interface AnimationListener {
        void onShowContent();
    }

}

具体需求自己可以设置进入改