自定义PopupWindow:兼容7.0,向下展开向上收起动画

设置背景色

在Android App的业务场景中,经常需要在屏幕弹出PopupWindow后,将背景设置为半透明的灰色效果。比较常见的解决方案就是设置Window的透明度。

具体代码如下:

//设置屏幕的透明度
public void setBackgroundAlpha(Activity activity,float alpha)  
{  
    WindowManager.LayoutParams lp = activity .getWindow().getAttributes();  
    lp.alpha = alpha; //0.0-1.0  
    activity .getWindow().getWindow().setAttributes(lp);  
}  

PopupWindow在使用时,需要监听其Touch事件,允许触摸弹窗外地方时取消显示。同时需要监听dismiss事件,便于在弹窗消失后恢复背景的透明度。

moreZPop = new PopupWindow(mContext);
moreZPop.setContentView(popZSRootView);
moreZPop.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
moreZPop.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
moreZPop.setBackgroundDrawable(new BitmapDrawable());
moreZPop.setOutsideTouchable(true);
moreZPop.setFocusable(true);
moreZPop.setAnimationStyle(R.style.optional_index_pop_style);
popWin.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                setBackgroundAlpha(TestActivity.this, 1f);
            }
        });  

但是该方案不适合于app页面中间某位置弹窗的场景,下图示例:

自定义PopupWindow:兼容7.0,向下展开向上收起动画_第1张图片
展开弹窗展示具体信息

7.0手机showAsDropDown兼容性问题

针对上述场景,常见的实现就是PopupWindow的视图中包括两部分,一部分是上部的content,另一部分是下部的透明背景。其中透明背景部分使用match_parent充满全屏。

但是该方案配合showAsDropDown方法,在android 7.0及以上手机时,弹窗会出现在状态栏下面,而不是指定的控件下。

解决方案如下,通过设置PopupWindow的高度来解决。但是针对华为虚拟键盘存在兼容性问题,目前未找到解决方案。

public class NougatFixPopwindow extends PopupWindow {
    public NougatFixPopwindow(Context context) {
        super(context);
    }

    public NougatFixPopwindow(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NougatFixPopwindow(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public NougatFixPopwindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public NougatFixPopwindow() {
    }

    public NougatFixPopwindow(View contentView) {
        super(contentView);
    }

    public NougatFixPopwindow(int width, int height) {
        super(width, height);
    }

    public NougatFixPopwindow(View contentView, int width, int height) {
        super(contentView, width, height);
    }

    public NougatFixPopwindow(View contentView, int width, int height, boolean focusable) {
        super(contentView, width, height, focusable);
    }

    @Override
    public void showAsDropDown(View anchor) {
        if (Build.VERSION.SDK_INT >= 24) {
            //TODO 下列代码在华为手机虚拟键盘隐藏的情况下有兼容性问题。目前未找到完美解决方案
            Rect visibleFrame = new Rect();
            anchor.getGlobalVisibleRect(visibleFrame);
            int height = anchor.getResources().getDisplayMetrics().heightPixels - visibleFrame.bottom;
            setHeight(height);
            super.showAsDropDown(anchor);
            //下列方案在小米android 7.1.1上遇到问题。popupwindow未在showAtLocation指定的位置出现,而是在屏幕顶部
//            int[] location = new int[2];
//            anchor.getLocationOnScreen(location);
//            super.showAtLocation(anchor, Gravity.NO_GRAVITY, 0, location[1] + anchor.getHeight());
        } else {
            super.showAsDropDown(anchor);
        }
    }
}

向下展开向上收起动画

在上面的解决方案中,如果需要为弹窗加入向下展开向上收起的动画,不能使用简单的traslate,scale等系统动画。

我使用ValueAnimator来动态设置PopupWindow中content部分的高度来实现动画。具体见下面代码:

public class ExpandAndCollapseAnimPopupWindow extends NougatFixPopwindow {
    private int mContentHeight;
    private View mContentView;
    private AnimatorSet mOpenAnimator;
    private AnimatorSet mCloseAnimator;
    private int mDuration = 200;

    private boolean mIsDismiss = false;
    public ExpandAndCollapseAnimPopupWindow(Context context) {
        super(context);

        init();
    }

    public ExpandAndCollapseAnimPopupWindow(View contentView, int width, int height) {
        super(contentView, width, height);

        init();
    }

    private void init(){

    }

    public void setAnimationView(View contentView, int duration){
        contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        this.setAnimationView(contentView, contentView.getMeasuredHeight(), duration);
    }

    public void setAnimationView(View contentView, int contentHeight, int duration){
        this.mContentView = contentView;
        this.mContentHeight = contentHeight;
        this.mDuration = duration;

        if (mOpenAnimator == null){
            mOpenAnimator = createOpenAnimation();
        }
        if (mCloseAnimator == null){
            mCloseAnimator = createCloseAnimation();
        }

        //TODO 设置setOutsideTouchable为false,因目前未找到好的方法监听dismiss开始事件
        this.setOutsideTouchable(false);
    }

    @Override
    public void showAsDropDown(View anchor){
        super.showAsDropDown(anchor);
        if (mOpenAnimator != null){
            mOpenAnimator.start();
        }
    }

    @Override
    public void dismiss(){
        if (mIsDismiss){
            super.dismiss();
            mIsDismiss = false;
        } else {
            if (mCloseAnimator != null){
                mCloseAnimator.start();
            }
        }
    }

    private static ValueAnimator createDropAnimator(final View v, int start, int end) {
        ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator arg0) {
                int value = (int) arg0.getAnimatedValue();
                ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
                layoutParams.height = value;
                v.setLayoutParams(layoutParams);
            }
        });
        return animator;
    }

    private AnimatorSet createOpenAnimation() {
        AnimatorSet set = new AnimatorSet();
        set.setDuration(mDuration);
        ValueAnimator downAnimator = createDropAnimator(mContentView, 0, mContentHeight);
        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this.getContentView(), View.ALPHA, 0.0f, 1.0f);
        set.playTogether(downAnimator, alphaAnimator);
        set.setDuration(mDuration);
        return set;
    }

    private AnimatorSet createCloseAnimation() {
        AnimatorSet set = new AnimatorSet();
        set.setDuration(mDuration);
        ValueAnimator upAnimator = createDropAnimator(mContentView, mContentHeight, 0);
        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this.getContentView(), View.ALPHA, 1.0f, 0.0f);
        set.playTogether(upAnimator, alphaAnimator);
        set.addListener(new AnimatorListenerAdapter() {
            @Override

            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mIsDismiss = true;
                ExpandAndCollapseAnimPopupWindow.this.dismiss();
            }

        });
        set.setDuration(mDuration);
        return set;
    }
}

你可能感兴趣的:(自定义PopupWindow:兼容7.0,向下展开向上收起动画)