打造淡入淡出过渡自然的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));

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

android 下拉,淡入淡出特效源码 依赖support v7 // /** * PopupWindow上菜单进入动画 */ public static Animation createPopupAnimIn(Context context, int fromYDelta) { AnimationSet animationSet = new AnimationSet(context, null); // animationSet.setInterpolator(new BounceInterpolator()); //结束时弹跳 animationSet.setFillAfter(true); //移动 TranslateAnimation translateAnim = new TranslateAnimation(0, 0, fromYDelta, 20); translateAnim.setDuration(TIME_IN); animationSet.addAnimation(translateAnim); //回弹效果 TranslateAnimation translateAnim2 = new TranslateAnimation(0, 0, 0, -20); translateAnim2.setStartOffset(TIME_IN); translateAnim2.setDuration(TIME_IN_BACK); animationSet.addAnimation(translateAnim2); return animationSet; } /** * PopupWindow上菜单离开动画 */ public static Animation createPopupAnimOut(Context context, int toYDelta) { AnimationSet animationSet = new AnimationSet(context, null); animationSet.setFillAfter(true); TranslateAnimation translateAnim = new TranslateAnimation(0, 0, 0, toYDelta); translateAnim.setDuration(TIME_OUT); animationSet.addAnimation(translateAnim); return animationSet; } /** * PopupWindow背景进入动画(透明度渐变) */ public static Animation createPopupBgFadeInAnim() { AlphaAnimation anim = new AlphaAnimation(0, 1.0f); anim.setDuration(TIME_IN); anim.setFillAfter(true); return anim; } /** * PopupWindow背景离开动画(透明度渐变) */ public static Animation createPopupBgFadeOutAnim(int duration) { AlphaAnimation anim = new AlphaAnimation(1.0f, 0); anim.setDuration(duration); anim.setFillAfter(true); return anim; } /** * PopupWindow按钮点击动画 */ public static Animation createPopupItemBiggerAnim(Context context) { AnimationSet animationSet = new AnimationSet(context, null); animationSet.setFillAfter(true); //放大(设置缩放的中心点为自己的中心) ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnim.setDuration(TIME_OUT_CLICK); animationSet.addAnimation(scaleAnim); //渐变 AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0); alphaAnim.setInterpolator(new AccelerateInterpolator()); alphaAnim.setDuration(TIME_OUT_CLICK); animationSet.addAnimation(alphaAnim); return animationSet; } /** * PopupWindow按钮点击时其它按钮的动画 */ public static Animation createPopupItemSmallerAnim(Context context) { //放大(设置缩放的中心点为自己的中心) ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0, 1.0f, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnim.setDuration(TIME_OUT_CLICK); scaleAnim.setFillAfter(true); return scaleAnim; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值