欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android PopupWindow实现带背景阴影的下滑选择框

程序员文章站 2022-05-31 16:06:20
...

先上图:

Android PopupWindow实现带背景阴影的下滑选择框

效果还是很丝滑的,这里动画主要用到了属性动画。实现逻辑很简单,动画都是在popupwindow调用showAsDropDown()dismiss()时执行。这里主要是注意退出动画的实现,在dismiss()中执行动画是无效的,需要在动画执行完毕后再执行super.dismiss(); 可拓展能力强,可以结合自身需求实现不同的效果。

以下是源码:

PopupWindow :

public class TypeSelectWindow extends PopupWindow {

    private Activity activity;
    private BDBaseAdapter bdBaseAdapter = null;
    View contentView;//弹出框的根布局,可以监听其点击事件,达到点击阴影消失弹框的效果
    GridView gridView;

    public TypeSelectWindow(Activity activity){
        this.activity = activity;
        initDatas();
        initView();
    }

    private void initView(){
        this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        this.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
        contentView = LayoutInflater.from(activity).inflate(R.layout.join_popup_layout,null);
        gridView =  contentView.findViewById(R.id.gridView);
        bdBaseAdapter = new BDBaseAdapter<String>(activity,listDatas,R.layout.join_select_item) {
            @Override
            public void convert(BDViewHolder helper, String item, int position) {
                ((TextView)helper.getView(R.id.text)).setText(item);
            }
        };
        gridView.setAdapter(bdBaseAdapter);
        this.setContentView(contentView);//设置布局
        this.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        this.setAnimationStyle(R.style.SelectPopupWindow);
        this.setOutsideTouchable(true);
        this.setFocusable(true);
    }

    /**
     * 模拟数据
     */
    List<String> listDatas = new ArrayList<>();
    private void initDatas(){
        listDatas.clear();
        for (int i = 0;i<10;i++){
            listDatas.add("menu"+i);
        }
    }

    //显示
    public void showPopupWindow(View parent) {
        if (!this.isShowing()) {
            this.showAsDropDown(parent,0,0);
            //执行进入动画,这里主要是执行列表下滑,背景变半透明在setAnimationStyle(R.style.SelectPopupWindow);中实现
            AnimationUtil.createAnimation(true,contentView,gridView,null);
        } else {
            dismissPopup();
        }
    }

    //消失
    public void dismissPopup(){
        super.dismiss();// 调用super.dismiss(),如果直接dismiss()会一直会调用下面的dismiss()
    }

    @Override
    public void dismiss() {
        //执行推出动画,列表上滑退出,同时背景变透明
        AnimationUtil.createAnimation(false,contentView,gridView , new AnimationUtil.AnimInterface() {
            @Override
            public void animEnd() {
                dismissPopup();//动画执行完毕后消失
            }
        });
    }
}

AnimationUtil:

public class AnimationUtil {

    //动画持续时间
    public final static int ANIMATION_IN_TIME=500;
    public final static int ANIMATION_OUT_TIME=500;

    /**
     * @param isIn  动画类型,进入或消失
     * @param rootView  根布局,主要用来设置半透明背景
     * @param target    要移动的view
     * @param animInterface  动画执行完毕后的回调
     */
    public static void createAnimation(final boolean isIn, final View rootView, final View target,
                                       final AnimInterface animInterface){
        final int toYDelta = ViewUtils.getViewMeasuredHeight(target);//测量布局高度
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(isIn?-toYDelta:0,isIn?0:-toYDelta);
        valueAnimator.setDuration(isIn?ANIMATION_IN_TIME:ANIMATION_OUT_TIME);
        valueAnimator.setRepeatCount(0);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentValue = (Float) animation.getAnimatedValue();
                target.setY(currentValue);
                if (!isIn){//因为在setAnimationStyle(R.style.SelectPopupWindow);设置了进入动画,所以执行进入动画时不再设置
                    rootView.setAlpha(1-Math.abs(currentValue)/animation.getDuration());
                }
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (animInterface!=null){
                    animInterface.animEnd();
                }
            }
        });
        valueAnimator.start();
    }
    public interface AnimInterface{
        void animEnd();
    }
}

ViewUtils

public class ViewUtils {

    /**
     * 获取控件的高度
     */
    public static int getViewMeasuredHeight(View view) {
        calculateViewMeasure(view);
        return view.getMeasuredHeight();
    }

    /**
     * 获取控件的宽度
     */
    public static int getViewMeasuredWidth(View view) {
        calculateViewMeasure(view);
        return view.getMeasuredWidth();
    }

    /**
     * 测量控件的尺寸
     */
    private static void calculateViewMeasure(View view) {
        int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);

        view.measure(w, h);
    }
}

style.xml
这里我只设置了进入的背景渐变的动画,列表的滑动动画我在AnimationUtil中实现了,在上面的AnimationUtil中也可以实现进入动画,这样也更统一,看个人喜好。

  <style name="SelectPopupWindow">
        <item name="android:windowEnterAnimation">@anim/p_in</item>
    </style>

p_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="500"
        android:fromAlpha="0"
        android:toAlpha="1"/>
</set>

布局部分就是简单的gridview,布局可以有多种方式来实现。可以直接用View标签来实现阴影效果。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="#88000000"
    android:layout_height="match_parent">
    <GridView
        android:id="@+id/gridView"
        android:scrollbars="none"
        android:numColumns="3"
        android:background="@color/white"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</RelativeLayout>

执行:

JoinTypeSelectWindow joinTypeSelectWindow = new JoinTypeSelectWindow(getActivity());
                joinTypeSelectWindow.showPopupWindow(targetView);

————— 完 ——————

一个小小的源码链接