起因呢又是有个需求,也是自己觉得好看,很多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);只能这样使用这种动画设置,就是透明度变化,而不能做到根据位置来实现平移动画,所以放弃了