Android下拉选择弹窗

起因呢又是有个需求,也是自己觉得好看,很多app都有下拉选择框,就想着实现下(主要是时间空闲,如果时间少,项目急,就找现成的),先看图片:

直接上代码:
xml:

    <com.yinp.proapp.module.work.view.DownSelectDialogView
        android:id="@+id/dsd_dialog"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

attrs:这个属性的作用是在内容较长的情况下保持底部距离

    <declare-styleable name="DownSelectDialogView">
        <attr name="dsdvBottomMargin" format="integer" />
    </declare-styleable>

view:

package com.yinp.proapp.module.work.view;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.yinp.proapp.R;
import com.yinp.proapp.utils.AppUtils;

/**
 * @Author: yinp
 * @Date: 2021/12/6
 * @Description:
 */
public class DownSelectDialogView extends FrameLayout {
    private int contentWidth;
    private int contentHeight;
    /**
     * 是否时第一次初始化
     */
    private boolean isFirstLoad = false;
    /**
     * 是否能够结束动画
     */
    private boolean isCanEnd = false;
    /**
     * 判断弹窗是否打开了
     */
    private boolean isStarting = false;
    /**
     * 动画的时长
     */
    private long duration = 400;
    /**
     * 存储弹窗页面的矩形,用于点击事件计算,
     * 方便控制弹窗收回
     */
    private Rect rectContent;


    public DownSelectDialogView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DownSelectDialogView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public DownSelectDialogView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initFirstView(attrs);
    }

    private FrameLayout dialogContentView;
    private RelativeLayout dialogBgView;
    private int bottomMargin = 100;

    private void initFirstView(AttributeSet attrs) {
        setVisibility(GONE);
        isFirstLoad = true;

        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.DownSelectDialogView);
        bottomMargin = ta.getInteger(R.styleable.DownSelectDialogView_dsdvBottomMargin, bottomMargin);
        ta.recycle();

        dialogBgView = new RelativeLayout(getContext());
        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        dialogBgView.setLayoutParams(layoutParams);
        dialogBgView.setBackgroundColor(Color.BLACK);
        addView(dialogBgView, 0);

        dialogContentView = new FrameLayout(getContext());
        LayoutParams layoutParams2 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams2.bottomMargin = AppUtils.dpToPx(getContext(), bottomMargin);
        dialogContentView.setLayoutParams(layoutParams2);
        addView(dialogContentView, 1);
    }

    public void mAddView(View child) {
        if (dialogContentView != null) {
            dialogContentView.addView(child);
        }
    }

    private String mFlag = "";

    public void mAddView(View child, String flag) {
        if (child != null && dialogContentView != null) {
            boolean isDiff = mFlag.equals(flag);
            if (!isDiff) {
                mRemoveView();
                dialogContentView.addView(child);
            }
            startAnimations(isDiff);
            mFlag = flag;
        }
    }

    public void mRemoveView() {
        if (dialogContentView != null && dialogContentView.getChildCount() > 0) {
            dialogContentView.removeAllViews();
        }
    }

    /**
     * 获取当前viewGroup中的所有子view
     * 对第一个子view重新摆放位置,放在上边,相当于隐藏,需要时再弹出
     * 所以必须固定格式
     *
     * @param changed
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (getChildCount() != 2) {
            return;
        }
        if (isFirstLoad) {
            isFirstLoad = false;
            rectContent = new Rect();
        } else {
            contentWidth = dialogContentView.getWidth();
            contentHeight = dialogContentView.getHeight();

            rectContent.left = dialogContentView.getLeft();
            rectContent.top = dialogContentView.getTop();
            rectContent.right = dialogContentView.getRight();
            rectContent.bottom = dialogContentView.getBottom();

            dialogContentView.layout(0, 0, contentWidth, contentHeight);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!rectContent.contains((int) event.getX(), (int) event.getY())) {
                    endAnimation();
                }
                break;
        }
        //必须对点击事件进行拦截
        return true;
    }

    /**
     * 弹窗打开和关闭时的动画
     */
    public void startAnimations(boolean isDiff) {
        if (isStarting || !isEnd) {
            return;
        }
        setVisibility(VISIBLE);
        if (!isDiff) {
            dialogContentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    contentHeight = dialogContentView.getHeight();
                    start();
                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            });
        } else {
            start();
        }
    }

    private void start() {
        //清理动画
        dialogContentView.clearAnimation();
        dialogBgView.clearAnimation();
        isCanEnd = false;
        isStarting = true;

        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(dialogBgView, "alpha", 0, 0.4f);
        ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(dialogContentView, "translationY", -contentHeight * 1.0f, 0f);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(alphaAnimator, translationAnimator);
        animatorSet.setDuration(duration);
        animatorSet.start();
        if (animatorSet.isStarted()) {
            isCanEnd = true;
        }
    }

    private boolean isEnd = true;

    /**
     * 结束动画
     */
    public void endAnimation() {
        //目的时判断当前是否有开启动画,有才允许关闭动画
        if (!isCanEnd) {
            return;
        }
        //清理动画
        dialogContentView.clearAnimation();
        dialogBgView.clearAnimation();
        isCanEnd = false;
        isEnd = false;
        isStarting = false;

        ObjectAnimator alphaOb = ObjectAnimator.ofFloat(dialogBgView, "alpha", 0.4f, 0f);
        ObjectAnimator translationYOb = ObjectAnimator.ofFloat(dialogContentView, "translationY", 0f, 1.0f * (-contentHeight));
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(alphaOb, translationYOb);
        animatorSet.setDuration(duration);
        animatorSet.start();
        animatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                setVisibility(GONE);
                isEnd = true;
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

    /**
     * 监听弹窗的打开和关闭
     */
    private OnDialogOpenListener onDialogOpenListener;

    public interface OnDialogOpenListener {
        void onDialogOpen(boolean isOpen);
    }

    public void setOnDialogOpenListener(OnDialogOpenListener onDialogOpenListener) {
        this.onDialogOpenListener = onDialogOpenListener;
    }

    /**
     * 设置动画的时长
     *
     * @param duration
     */
    public void setDuration(long duration) {
        this.duration = duration;
    }

    public boolean isStarting() {
        return isStarting;
    }
}


activity:

public class DownSelectDialogActivity extends AppBaseFragmentActivity<ActivityDownSelectDialogBinding> {
    View viewOne;
    View viewTwo;

    //onCreate调用的方法
    @Override
    protected void initViews() {
        setStatusBarHeight(StatusBarUtil.getStatusBarHeight(mContext));
        bd.btnOne.setOnClickListener(v -> {
            bd.dsdDialog.mAddView((viewOne == null ? initViewOne() : viewOne), "viewOne");
        });
        bd.btnTwo.setOnClickListener(v -> {
            bd.dsdDialog.mAddView((viewTwo == null ? initViewTwo() : viewTwo), "viewTwo");
        });
//        dsvView.endAnimation();可以通过调用主动结束
    }

    private View initViewOne() {
        viewOne = LayoutInflater.from(this).inflate(R.layout.layout_one_one, null);
        //可处理逻辑
        return viewOne;
    }

    private View initViewTwo() {
        viewTwo = LayoutInflater.from(this).inflate(R.layout.layout_one_two, null);
        //可处理逻辑
        return viewTwo;
    }
}

直接拿来就可以用,没有做所有情况的适配,自己用可以修改。因为是模拟的弹窗效果,实际上是布局,所以需要在layout中添加它的布局。还有另外一种方案使用poupwindow
,但是popupWindow.setAnimationStyle(R.style.popwin_anim_style);只能这样使用这种动画设置,就是透明度变化,而不能做到根据位置来实现平移动画,所以放弃了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值