打造淡入淡出过渡自然的PopupWindow

效果如图,Gif压缩得厉害,效果失真,凑合看:
这里写图片描述
我所谓的淡入淡出过渡自然是指一下几点:
1. 窗体从底部弹出以及收回过程有移动动画;
2. 伴随窗体的弹出和收起,背景也应有个由明到暗和由暗到明的动画,否则会显得生硬。

附加功能:
1. 点击窗体外,弹窗收回;
2. 弹窗打开时,点击返回键,弹窗收回。

项目地址:https://github.com/sheaye/SlidePopupWindow
部分代码:
SlidePopupWindow:

public abstract class SlidePopupWindow extends PopupWindow {

    private final Context mContext;
    private FrameLayout mContainer;
    // 透明遮罩,接收窗体外部的点击事件用
    private FrameLayout mMaskLayout;

    public SlidePopupWindow(Context context) {
        super(context);
        mContext = context;
        setAnimationStyle(R.style.pop_anim);
        // 收起时渐进恢复背景,从半透明-
        setOnDismissListener(new OnDismissListener() {
            @Override
            public void onDismiss() {
                ValueAnimator animator = ValueAnimator.ofFloat(0.5f, 1f).setDuration(500);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        setBackgroundAlpha((Float) animation.getAnimatedValue());
                    }
                });
                animator.start();
            }
        });

        mContainer = (FrameLayout) LayoutInflater.from(context).inflate(R.layout.window_slide_popup, null);

        // 添加窗体
        View contentView = onCreateView(context, mContainer);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.BOTTOM;
        contentView.setLayoutParams(layoutParams);
        mContainer.addView(contentView, layoutParams);
        setContentView(mContainer);

        // 宽度必须设置MATCH_PARENT,否则不显示
        setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        // 高度MATCH_PARENT,contentView铺满全屏,点击内容区域外时即点击透明遮罩mMaskLayout
        setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
        // 设置popupWindow背景透明,否则PopupWindow整体是灰色背景
        setBackgroundDrawable(null);
        // 点击返回时收起弹窗
        setBackCancel();
        mMaskLayout = mContainer.findViewById(R.id.mask_layout);
        // 点击窗体外部的透明遮罩时收起弹窗
        mMaskLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
    }

    public abstract View onCreateView(Context context, ViewGroup container);

    /**
     * 点返回键时收起弹窗
     */
    private void setBackCancel() {
        // 返回键窗体消失必须设置
        setFocusable(true);
        setOutsideTouchable(true);
        // 设置contentView能够监听事件,设置点击返回键窗体消失有必要
        mContainer.setFocusable(true);
        mContainer.setFocusableInTouchMode(true);
        // 设置点击返回键窗体消失
        mContainer.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    dismiss();
                    return true;
                }
                return false;
            }
        });
    }

    /**
     * 显示弹窗,过程中伴随背景逐渐变暗。
     * 不要改变MaskLayout的背景色,否则会是灰色背景的框框整体向上升起的效果。
     */
    public void show(View parent) {
        showAtLocation(parent, Gravity.BOTTOM, 0, 0);
        ValueAnimator animator = ValueAnimator.ofFloat(1, 0.5f).setDuration(500);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setBackgroundAlpha((Float) animation.getAnimatedValue());
            }
        });
        animator.start();
    }

    /**
     * 设置PopupWindow下面的内容的透明度
     */
    private void setBackgroundAlpha(float bgAlpha) {
        WindowManager.LayoutParams lp = ((Activity) mContext).getWindow().getAttributes();
        lp.alpha = bgAlpha;
        ((Activity) mContext).getWindow().setAttributes(lp);
    }

}

使用时继承SlidePopupWindow,例如:

public class SharePopupWindow extends SlidePopupWindow implements View.OnClickListener {

    private final Context mContext;
    Button mSendToFriends;
    Button mShareInTimeline;
    TextView mCancelShare;


    public SharePopupWindow(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public View onCreateView(Context context, ViewGroup container) {
        View contentView = LayoutInflater.from(context).inflate(R.layout.window_share, container, false);
        mSendToFriends = contentView.findViewById(R.id.send_to_friends);
        mShareInTimeline = contentView.findViewById(R.id.share_in_timeline);
        mCancelShare = contentView.findViewById(R.id.cancel_share);

        mSendToFriends.setOnClickListener(this);
        mShareInTimeline.setOnClickListener(this);
        mCancelShare.setOnClickListener(this);
        return contentView;
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.cancel_share) {// 取消
            dismiss();
            return;
        }
        switch (v.getId()) {
            case R.id.send_to_friends:// 发送给朋友
                Toast.makeText(mContext,"发送给朋友", Toast.LENGTH_SHORT).show();
                break;
            case R.id.share_in_timeline:// 分享到朋友圈
                Toast.makeText(mContext,"分享到朋友圈", Toast.LENGTH_SHORT).show();
                break;
        }
        dismiss();
    }
}

然后就可以愉快地“Show”啦:

new SharePopupWindow(this).show(getWindow().getDecorView().findViewById(android.R.id.content));

更多代码参见上述项目地址。

猜你喜欢

转载自blog.csdn.net/KevinsCSDN/article/details/82697841