Android 仿饿了么底部栏购物车弹出/消失效果

饿了么效果图,比例不太对,不过看出效果就好~
当点击底部栏时,会有一个窗口从下往上弹出来,且背景色变暗。点击背景或按返回键时,窗口会由上往下消失,然后背景色变亮。

看到这点,我们很容易想到使用Dialog来实现效果,下面就来尝试下吧。

方案1:

首先是要自定义Dialog,且给Dialog添加动画效果,如下。

  
                Dialog dialog = new Dialog(MainActivity.this,R.style.Theme_AppCompat_Dialog);
                dialog.setContentView(LayoutInflater.from(MainActivity.this).inflate(R.layout.dialog, null));
                Window dialogWindow = dialog.getWindow();
                dialogWindow.getDecorView().setPadding(0, 0, 0, 0);// 边距设为0
                dialogWindow.setBackgroundDrawableResource(android.R.color.transparent);//背景透明,不然会有个白色的东东
                dialogWindow.setWindowAnimations(R.style.dialogWindowAnim); //设置窗口弹出动画
                WindowManager.LayoutParams lp = dialogWindow.getAttributes();
                lp.width = WindowManager.LayoutParams.MATCH_PARENT; // 宽度
                lp.height = 300; // 高度
                dialogWindow.setAttributes(lp);
                dialogWindow.setGravity(Gravity.BOTTOM);
                // 弹出dialog
                dialog.show();
                

R.style.dialogWindowAnim里定义了Dialog的弹出、消失动画,如下。

    <style name="dialogWindowAnim" parent="android:Animation" >
        <item name="android:windowEnterAnimation">@anim/show</item>
        <item name="android:windowExitAnimation">@anim/exit</item>
    </style>

弹出动画 show.xml,如下。

<translate 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromXDelta="0"
    android:fromYDelta="100%"
    android:toXDelta="0"
    android:toYDelta="0">
</translate>

消失动画 exit.xml,如下。

<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="0"
    android:toYDelta="100%"
    android:duration="300">
</translate>

因为有些麻烦,所以R.layout.dialog的布局暂不贴出 = =(不贴大家都知道它长什么样~)

Done!我们得到这样的效果。
方案1

这个实现的效果和饿了么还有有挺大区别的。饿了么是弹出商品菜单,而底部栏是不动的,而我们实现的效果则是底部栏+商品菜单一起弹出、消失。
因为我们是给整个Dialog添加了动画,而底部栏(绿色)+商品菜单(青蓝色)都在Dialog里面,所以会一起执行动画效果,所以给直接给Dialog添加动画的办法是行不通的。
那么就可以想到,只给商品菜单添加弹出、消失动画。

方案2:

新方案的 R.layout.dialog如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#80333333">
    <!--半透明遮罩-->
    <View
        android:id="@+id/v_dimiss"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#00000000"/>
    <!--商品菜单-->
    <View
        android:background="#97FFFF"
        android:id="@+id/v_anim"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        />
    <!--底部栏-->
    <RelativeLayout
        android:id="@+id/rl"
        android:background="#fff"
        android:layout_width="match_parent"
        android:layout_height="70dp">
        <View
            android:background="#7FFF00"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"/>
        <ImageView
            android:layout_marginLeft="3dp"
            android:src="@mipmap/a7"
            android:layout_width="40dp"
            android:layout_height="40dp" />
    </RelativeLayout>
</LinearLayout>

所以Dialog的界面结构应该是这样的,Dialog要占据整个屏幕。
dialog
Q:为什么要我们自己做1个遮罩?而不用Dialog自带的效果?
A:首先Dialog里的底部栏和Activity界面的底部栏应该一模一样的且把Activity界面的底部栏给盖住了。而当Dialog弹出来时,Activity界面的底部栏实际上会变暗(只是我们看不到)。然后但Dialog消失时,屏幕就会由暗变亮,所以肉眼看到的效果就是底部栏会闪一下。

因此为了避免底部栏闪一下这个效果,我们要禁用Dialog弹出时,外部变暗的这个效果。并且为了模拟Dialog的效果,我们自己要画1个半透明遮罩。

 <style name="myDialog" parent="Theme.AppCompat.Dialog">
        <item name="android:backgroundDimEnabled">false</item><!--activity不变暗-->
    </style>

先拿到Dialog里面的各种View,如下。

      // 部分代码
        dialog = new CustomDialog(context, R.style.myDialog);// 风格,activity不变暗
        view = LayoutInflater.from(context).inflate(R.layout.dialog, null); // dialog布局
        dismissView = view.findViewById(R.id.v_dimiss);// 半透明遮罩布局
        animView = view.findViewById(R.id.v_anim); // 商品菜单布局
        dialog.setContentView(view);

接下来我们就可以操作商品菜单的动画效果了。首先是弹出动画。

    // 部分代码
    public void showAnim() {
        TranslateAnimation animation = new TranslateAnimation(0, 0, DensityUtil.dp2px(context, 300), 0);// 位移动画,DensityUtil是尺寸转换工具。因为布局填了300dp,所以这里要用300dp转px
        animation.setDuration(300);
        animView.startAnimation(animation);
        }

那么动画什么时候执行呢?应该当Dialog弹出后才执行,如下。

   // 部分代码
   dialog.show();
   showAnim();

然后是商品菜单的消失动画,那么消失动画什么时候执行呢?我们很容易想到在Dialog.onDismiss()的回调里执行,但这个是行不通的。因为执行onDismiss回调时,dialog已经消失了,所以商品菜单的消失动画也是看不到了。那么我们只能在dialog消失之前执行商品菜单的消失动画,遗憾的是Dialog并没有提供消失前的回调方法,所以我们只能进入Dialog的源码,看下怎么样能在Dialog消失前,执行商品菜单的消失动画。

Dialog消失,会执行Dialog.dimiss()方法,进去看看。

// dialog源码
 @Override
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            mHandler.post(mDismissAction);
        }
    }

dismiss方法的声明是public的,意味着可以重写,这样就好办了。

自定义Diloag, CustomDialog的完整代码

public class CustomDialog extends Dialog {
    IBeforeDismiss iBeforeDismiss;

    public CustomDialog(Context context, int themeResId) {
        super(context, themeResId);
    }

    @Override
    public void dismiss(){
        iBeforeDismiss.onBeforeDismiss();
    }

    // 真正让dialog消失
    public void myDismiss() {
        super.dismiss();// dialog消失
    }

    // dismiss前执行
    interface IBeforeDismiss {
        void onBeforeDismiss();
    }

    public void setBeforeDismiss(IBeforeDismiss iBeforeDismiss) {
        this.iBeforeDismiss = iBeforeDismiss;
    }
}

这样一来,当Dialog执行dismiss时,并不会让Dialog消失,而是执行IBeforeDismiss回调,然后我们在IBeforeDismiss回调里执行商品菜单消失动画,当消失动画执行完后,才让Dialog真正消失,即执行myDismiss方法。

  // 部分代码
  dialog.setBeforeDismiss(new CustomDialog.IBeforeDismiss() {
            @Override
            public void onBeforeDismiss() {
                dismissAnim();// 商品菜单消失动画
            }
        });

    // 部分代码
    // 消失动画
    private void dismissAnim() {
        TranslateAnimation animation = new TranslateAnimation(0, 0, 0, DensityUtil.dp2px(context, 300));
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                dialog.myDismiss();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        animation.setDuration(300);
        animView.startAnimation(animation);
    }

当然,别忘了给半透明遮罩添加点击事件,因为一般点击遮罩时,dialog都会消失。

  // 部分代码
  dismissView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

Done!我们得到了如下的效果。
方案2

可以看到和饿了么的效果是一样的。
自定义 Dilaog完整代码如下:

package com.yj.yttr;


import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;

public class MyDialog {
    CustomDialog dialog;
    View view; // dialog布局
    View animView; // 商品菜单布局
    View dismissView; // 半透明遮罩布局
    Context context;

    public MyDialog(Context context) {
        this.context = context;
        dialog = new CustomDialog(context, R.style.myDialog);
        // dialog的样式
        view = LayoutInflater.from(context).inflate(R.layout.dialog, null);
        animView = view.findViewById(R.id.v_anim);
        dialog.setContentView(view);

        dismissView = view.findViewById(R.id.v_dimiss);
        dismissView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        dialog.setBeforeDismiss(new CustomDialog.IBeforeDismiss() {
            @Override
            public void onBeforeDismiss() {
                dismissAnim();
            }
        });


        // 设置dialog的位置
        Window dialogWindow = dialog.getWindow();
        dialogWindow.getDecorView().setPadding(0, 0, 0, 0);
        dialogWindow.setBackgroundDrawableResource(android.R.color.transparent);//背景透明,不然会有个白色的东东
//        dialogWindow.setWindowAnimations(R.style.dialogWindowAnim); //不使用窗口弹出动画 
        WindowManager.LayoutParams lp = dialogWindow.getAttributes();

        lp.width = WindowManager.LayoutParams.MATCH_PARENT; // 宽度
        lp.height = WindowManager.LayoutParams.MATCH_PARENT; // 高度
        dialogWindow.setAttributes(lp);
        // 设置dialog为底部
        dialogWindow.setGravity(Gravity.BOTTOM);
    }

    public void show() {
        dialog.show();
        showAnim();
    }

    // 出现动画
    private void showAnim() {
        TranslateAnimation animation = new TranslateAnimation(0, 0, DensityUtil.dp2px(context, 300), 0);
        animation.setDuration(300);
        animView.startAnimation(animation);
    }

    // 消失动画
    private void dismissAnim() {
        TranslateAnimation animation = new TranslateAnimation(0, 0, 0, DensityUtil.dp2px(context, 300));
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                dialog.myDismiss();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        animation.setDuration(300);
        animView.startAnimation(animation);
    }

}

PS:若Dialog无法全屏显示(左右2边,下边有缝隙),使用如下主题可解决。

  <style name="shoppingCartDialog" parent="Theme.AppCompat.Dialog">   
        <item name="android:windowBackground">@android:color/transparent</item>  
          <item name="android:windowFrame">@null</item>  
          <item name="android:windowNoTitle">true</item>   
        <item name="android:windowIsFloating">true</item>   
        <item name="android:windowIsTranslucent">true</item>   
        <item name="android:windowContentOverlay">@null</item> 
           <item name="android:windowFullscreen">true</item>  
        <item name="android:backgroundDimEnabled">true</item>
    </style>

这种实现方式成本很低,只需要继承Dialog,重写dismiss方法,为其添加一个消失前事件监听即可。
这里只是抛砖引玉,有更好更方便的实现方式欢迎留言~

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要实现底部弹出购物车列表功能,可以使用 BottomSheet 组件。以下是实现步骤: 1.在布局文件中添加 BottomSheet 组件和购物车列表。 ``` <LinearLayout android:id="@+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:orientation="vertical" app:behavior_hideable="true" app:behavior_peekHeight="0dp" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <!-- 购物车列表 --> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> ``` 2.在代码中获取 BottomSheet 组件,并设置监听器。 ``` // 获取 BottomSheet 组件 LinearLayout bottomSheet = findViewById(R.id.bottom_sheet); // 获取 BottomSheetBehavior BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); // 设置监听器 bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { // 当 BottomSheet 状态改变时调用 } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { // 当 BottomSheet 滑动时调用 } }); ``` 3.在购物车图标上设置点击事件,并通过 BottomSheetBehavior 控制 BottomSheet 的弹出和关闭。 ``` // 获取购物车图标 ImageView cartIcon = findViewById(R.id.cart_icon); // 设置点击事件 cartIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 判断 BottomSheet 是否展开 if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { // 关闭 BottomSheet bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } else { // 展开 BottomSheet bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); } } }); ``` 4.在代码中设置购物车列表的数据和适配器。 ``` // 获取购物车列表 ListView listView = findViewById(R.id.list_view); // 设置数据 List<String> data = new ArrayList<>(); data.add("商品1"); data.add("商品2"); data.add("商品3"); // 设置适配器 ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data); listView.setAdapter(adapter); ``` 这样就可以实现底部弹出购物车列表功能了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值