美文网首页Android程序员Android开发
仿饿了么添加购物车效果

仿饿了么添加购物车效果

作者: Master_文 | 来源:发表于2018-05-31 00:11 被阅读397次

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

    最近有一个需求,要实现一个像饿了么添加购物车的效果,下面是效果图
    主要有以下几点
    • 1 沉浸式状态栏

    • 2 上下滑动的动画

    • 3 添加减少的动画

    • 4 贝塞尔曲线动画

    • 5 底部购物车弹窗动画

    • 6 购物车缓存

    沉浸式状态栏
    沉浸式状态栏网上有很多,通常都是放v19和v21的包,然后在最外层ViewGroup设置fitsSystemWindows = "true" ,这种方式我不太推荐,因为你每次都要写fitsSystemWindows 这个属性,可能你会说我抽取出来不就好了,ok,那有时候要的效果不是我想要的,就比如我图上的效果,那怎么办呢,其实,有时候,你只需要一点小小的bang助!!!,几行代码的事

    values

    <style name="AppTheme.TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar"/>
    

    values-v19

      <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="android:windowTranslucentStatus">true</item>
            <item name="android:windowTranslucentNavigation">true</item>
      </style>
    

    values-v21

     <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="android:windowTranslucentStatus">false</item>
            <item name="android:windowTranslucentNavigation">true</item>
            <!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色-->
            <item name="android:statusBarColor">@android:color/transparent</item>
     </style>
    
    重头戏来了,以ToolBar为例,动态设置ToolBar的高度,并且设置一个padding,top为状态栏的高度,搞定收工!
    • activity
      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                setHeight(mToolbar);
            }
        }
    
        public void setHeight(View view) {
            // 获取actionbar的高度
            TypedArray actionbarSizeTypedArray = obtainStyledAttributes(new int[]{
                    android.R.attr.actionBarSize
            });
            float height = actionbarSizeTypedArray.getDimension(0, 0);
            // ToolBar的top值
            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            double statusBarHeight = getStatusBarHeight(this);
            lp.height = (int) (statusBarHeight + height);
            view.setPadding(0,(int) statusBarHeight,0, 0);
            mToolbar.setLayoutParams(lp);
        }
    
        private double getStatusBarHeight(Context context) {
            int result = 0;
            int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
                    "android");
            if (resourceId > 0) {
                result = context.getResources().getDimensionPixelSize(resourceId);
            }
            return result;
        }
    
    • xml
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.Toolbar
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:contentInsetStart="0dp"
        app:layout_collapseMode="pin">
    
        <FrameLayout
            android:layout_gravity="bottom"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize">
    
            <ImageView
                android:id="@+id/iv_back"
                android:layout_width="44dp"
                android:layout_height="44dp"
                android:layout_gravity="center_vertical"
                android:padding="14dp"
                android:src="@mipmap/icon_back"/>
    
            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:textColor="#ffffff"
                android:textSize="18sp"
                tools:text="Title"/>
        </FrameLayout>
    </android.support.v7.widget.Toolbar>
    
    下面是效果图
    上下滑动的动画
    没什么好说的,design包下的协调式布局(CoordinatorLayout),可以很好地实现这种效果,监听AppBarLayout的状态,打开的时候,隐藏标题,关闭的时候.显示标题,同时监听高度变化做透明度动画效果
    • XML布局
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".ui.activity.ShoppingGoodsActivity">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:orientation="vertical">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@mipmap/icon_shopping_goods_bg"
                app:expandedTitleMarginStart="?attr/actionBarSize"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                <RelativeLayout
                    android:id="@+id/rl_header"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="bottom">
    
                    <ImageView
                        android:id="@+id/iv_goods_icon"
                        android:layout_width="80dp"
                        android:layout_height="80dp"
                        android:layout_marginBottom="15dp"
                        android:layout_marginLeft="16dp"
                        android:src="@mipmap/ic_launcher"/>
    
                    <TextView
                        android:id="@+id/tv_goods_title"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="115dp"
                        android:text="巴比馒头(四季店)"
                        android:textColor="#FFFFFF"
                        android:textSize="18sp"/>
    
                    <TextView
                        android:id="@+id/tv_goods_count"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignLeft="@id/tv_goods_title"
                        android:layout_below="@id/tv_goods_title"
                        android:layout_marginTop="1dp"
                        android:text="月销2390单"
                        android:textColor="#FFFFFF"
                        android:textSize="12sp"/>
    
                    <TextView
                        android:id="@+id/tv_goods_address"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignLeft="@id/tv_goods_title"
                        android:layout_below="@id/tv_goods_count"
                        android:layout_marginTop="3dp"
                        android:text="上海市浦东新区东二环路与新海大道"
                        android:textColor="#FFFFFF"
                        android:textSize="12sp"/>
    
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignLeft="@id/tv_goods_title"
                        android:layout_below="@id/tv_goods_address"
                        android:text="021-87689283"
                        android:textColor="#FFFFFF"
                        android:textSize="12sp"/>
    
                    <LinearLayout
                        android:id="@+id/ll_discount_coupon"
                        android:layout_width="match_parent"
                        android:layout_height="44dp"
                        android:layout_below="@id/iv_goods_icon"
                        android:background="#FFFFFF"
                        android:gravity="center_vertical"
                        android:orientation="horizontal">
    
                        <ImageView
                            android:id="@+id/iv_discount_coupon"
                            android:layout_width="17dp"
                            android:layout_height="17dp"
                            android:layout_marginLeft="16dp"
                            android:src="@mipmap/icon_discount_coupon"/>
    
                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="6dp"
                            android:text="5元优惠券"
                            android:textColor="#333333"
                            android:textSize="12sp"/>
    
                        <Space
                            android:layout_width="0dp"
                            android:layout_height="0dp"
                            android:layout_weight="1"/>
    
                        <TextView
                            android:id="@+id/tv_goods_get"
                            android:layout_width="wrap_content"
                            android:layout_height="match_parent"
                            android:layout_marginRight="19dp"
                            android:gravity="center"
                            android:text="点击领取"
                            android:textColor="#BCBCBC"
                            android:textSize="12sp"/>
                    </LinearLayout>
    
                    <View
                        android:layout_width="match_parent"
                        android:layout_height="9dp"
                        android:layout_below="@id/ll_discount_coupon"
                        android:background="#EFEFEF"/>
                </RelativeLayout>
    
                <include layout="@layout/layout_toolbar"/>
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_goods"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="50dp"
            android:background="#ffffff"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    
        <include
            android:id="@+id/layout_shopping_cart"
            layout="@layout/layout_shopping_cart"/>
    </android.support.design.widget.CoordinatorLayout>
    
    • AppBarStateChangeListener
    public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {
    
        private State mCurrentState = State.IDLE;
    
        @Override
        public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
            if (i == 0) {
                if (mCurrentState != State.EXPANDED) {
                    onStateChanged(appBarLayout, State.EXPANDED);
                }
                mCurrentState = State.EXPANDED;
            } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
                if (mCurrentState != State.COLLAPSED) {
                    onStateChanged(appBarLayout, State.COLLAPSED);
                }
                mCurrentState = State.COLLAPSED;
            } else {
                if (mCurrentState != State.IDLE) {
                    onStateChanged(appBarLayout, State.IDLE);
                }
                mCurrentState = State.IDLE;
            }
            onStateChanged(i);
            Logger.d("滑动的的高度" + i);
        }
    
        public abstract void onStateChanged(AppBarLayout appBarLayout, State state);
    
        public void onStateChanged(int i) {
        }
    
        public enum State {
            EXPANDED,       // 展开状态
            COLLAPSED,      // 折叠状态
            IDLE            // 准备状态
        }
    }
    
    • activity
    private void initToolbar() {
            mAppBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
                @Override
                public void onStateChanged(AppBarLayout appBarLayout, State state) {
                    if (state == State.EXPANDED) {
                        // 展开状态
                        mTvTitle.setText("");
                        mRlHeader.setVisibility(View.VISIBLE);
                    } else if (state == State.COLLAPSED) {
                        // 折叠状态
                        mTvTitle.setText("芭比馒头");
                        mRlHeader.setVisibility(View.GONE);
                    } else {
                        mTvTitle.setText("");
                        mRlHeader.setVisibility(View.VISIBLE);
                    }
                }
    
                @Override
                public void onStateChanged(int i) {
                    float height = mRlHeader.getHeight();
                    float alpha = i / height;
                    Logger.d("透明度" + (1 - Math.abs(alpha)));
                    mRlHeader.setAlpha(1 - Math.abs(alpha));
                }
            });
        }
    
    上面要注意的是,动态设置mRlHeader的显示隐藏,因为透明度有时候不为0的时候,还是会显示出来的
    添加减少的动画
    使用的是属性动画,单这里有一个坑,后面会讲到,尝试过用补间动画,但是由于补间动画的特性,本身的位置不变,达不到预期效果,有兴趣的可以尝试一下
    • 动画的代码
     public void animOpen(final ImageView imageView) {
            AnimatorSet animatorSet = new AnimatorSet();
            ObjectAnimator translationAnim = ObjectAnimator.ofFloat(imageView, "translationX", addLeft - reduceLeft, 0);
            ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(imageView, "rotation", 0, 180);
            animatorSet.play(translationAnim).with(rotationAnim);
            animatorSet.setDuration(TIME).start();
        }
    
    
     public void animClose(final ImageView imageView) {
            AnimatorSet animatorSet = new AnimatorSet();
            ObjectAnimator translationAnim = ObjectAnimator.ofFloat(imageView, "translationX", 0, addLeft - reduceLeft);
            ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(imageView, "rotation", 0, 180);
            animatorSet.play(translationAnim).with(rotationAnim);
            animatorSet.setDuration(TIME).start();
    }
    
    • 动画打开还是关闭
            // 减少
            final ImageView iv_goods_reduce = holder.getView(R.id.iv_goods_reduce);
            iv_goods_reduce.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    // 获取减少图标的位置
                    reduceLeft = iv_goods_reduce.getLeft();
                    iv_goods_reduce.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            });
            iv_goods_reduce.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = holder.getLayoutPosition();
                    ShopGoodsBean shopGoodsBean = mGoodsList.get(position);
                    int count = shopGoodsBean.getCount();
                    count--;
                    // 防止过快点击出现多个关闭动画
                    if (count == 0) {
                        animClose(iv_goods_reduce);
                        tv_goods_count.setText("");
                        // 考虑到用户点击过快
                        allCount--;
                    } else if (count < 0) {
                        // 防止过快点击出现商品数为负数
                        count = 0;
                    } else {
                        allCount--;
                        tv_goods_count.setText(String.valueOf(count));
                    }
                    // 商品的数量是否显示
                    if (allCount <= 0) {
                        allCount = 0;
                        mTvShoppingCartCount.setVisibility(View.GONE);
                    } else {
                        mTvShoppingCartCount.setText(String.valueOf(allCount));
                        mTvShoppingCartCount.setVisibility(View.VISIBLE);
                    }
                    shopGoodsBean.setCount(count);
                }
            });
    // 增加
            final ImageView iv_goods_add = holder.getView(R.id.iv_goods_add);
            iv_goods_add.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    // 获取增加图标的位置
                    addLeft = iv_goods_add.getLeft();
                    iv_goods_add.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            });
            iv_goods_add.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = holder.getLayoutPosition();
                    ShopGoodsBean shopGoodsBean = mGoodsList.get(position);
                    int count = shopGoodsBean.getCount();
                    count++;
                    allCount++;
                    if (allCount > 0) {
                        mTvShoppingCartCount.setVisibility(View.VISIBLE);
                    }
                    mTvShoppingCartCount.setText(String.valueOf(allCount));![1.gif](https:https://img.haomeiwen.com/i5286943/8616ee25e4dd2edc.gif?imageMogr2/auto-orient/strip)
    
                    if (count == 1) {
                        iv_goods_reduce.setVisibility(View.VISIBLE);
                        animOpen(iv_goods_reduce);
                    }
                    addGoods2CartAnim(iv_goods_add);
                    tv_goods_count.setText(String.valueOf(count));
                    shopGoodsBean.setCount(count);
                }
            });
    
    通过getViewTreeObserver获取图标的位置,计算距离,做属性动画,同时通过数据层记录,要注意的是,如果手指点击点击过快的时候,会使减少的变为负数,所以我们要做判断,如果为0不减少
    你可以看到,减少的图标不见了,而且点击也没效果,很简单,主要是RecycleView的item复用机制,属性动画改变控件位置,所以做完动画位置还原就好了
    public void animClose(final ImageView imageView) {
            AnimatorSet animatorSet = new AnimatorSet();
            ObjectAnimator translationAnim = ObjectAnimator.ofFloat(imageView, "translationX", 0, addLeft - reduceLeft);
            ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(imageView, "rotation", 0, 180);
            animatorSet.play(translationAnim).with(rotationAnim);
            animatorSet.setDuration(TIME).start();
            animatorSet.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    // TODO: 2018/5/19 因为属性动画会改变位置,所以当结束的时候,要回退的到原来的位置,同时用补间动画的位移不好控制
                    ObjectAnimator oa = ObjectAnimator.ofFloat(imageView, "translationX", addLeft - reduceLeft, 0);
                    oa.setDuration(0);
                    oa.start();
                    imageView.setVisibility(View.GONE);
                }
            });
        }
    

    4 贝塞尔曲线动画

    一个二阶贝塞尔动画,没有涉及到动态点,比较简单,在你点击的时候,在最外层布局添加一个ImageView,做值动画,动画结束的时候,移除该控件
    /**
         * 贝塞尔曲线动画
         *
         * @param goodsImageView
         */
        public void addGoods2CartAnim(ImageView goodsImageView) {
            final ImageView goods = new ImageView(ShoppingGoodsActivity.this);
            goods.setImageResource(R.mipmap.icon_goods_add);
            int size = Util.dp2px(ShoppingGoodsActivity.this, 24);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(size, size);
            goods.setLayoutParams(lp);
            mCoordinatorLayout.addView(goods);
            // 控制点的位置
            int[] recyclerLocation = new int[2];
            mCoordinatorLayout.getLocationInWindow(recyclerLocation);
            // 加入点的位置起始点
            int[] startLocation = new int[2];
            goodsImageView.getLocationInWindow(startLocation);
            // 购物车的位置终点
            int[] endLocation = new int[2];
            mIvShoppingCart.getLocationInWindow(endLocation);
            // TODO: 2018/5/21 0021 考虑到状态栏的问题,不然会往下偏移状态栏的高度
            int startX = startLocation[0] - recyclerLocation[0];
            int startY = startLocation[1] - recyclerLocation[1];
            // TODO: 2018/5/21 0021 和上面一样
            int endX = endLocation[0] - recyclerLocation[0];
            int endY = endLocation[1] - recyclerLocation[1];
            // 开始绘制贝塞尔曲线
            Path path = new Path();
            // 移动到起始点位置(即贝塞尔曲线的起点)
            path.moveTo(startX, startY);
            // 使用二阶贝塞尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
            path.quadTo((startX + endX) / 2, startY, endX, endY);
            // mPathMeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,如果是true,path会形成一个闭环
            final PathMeasure pathMeasure = new PathMeasure(path, false);
            // 属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());
            // 计算距离
            int tempX = Math.abs(startX - endX);
            int tempY = Math.abs(startY - endY);
            // 根据距离计算时间
            int time = (int) (0.3 * Math.sqrt((tempX * tempX) + tempY * tempY));
            valueAnimator.setDuration(time);
            valueAnimator.start();
            valueAnimator.setInterpolator(new AccelerateInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 当插值计算进行时,获取中间的每个值,
                    // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
                    float value = (Float) animation.getAnimatedValue();
                    // 获取当前点坐标封装到mCurrentPosition
                    // boolean getPosTan(float distance, float[] pos, float[] tan) :
                    // 传入一个距离distance(0<=distance<=getLength()),然后会计算当前距离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
                    // mCurrentPosition此时就是中间距离点的坐标值
                    pathMeasure.getPosTan(value, mCurrentPosition, null);
                    // 移动的商品图片(动画图片)的坐标设置为该中间点的坐标
                    goods.setTranslationX(mCurrentPosition[0]);
                    goods.setTranslationY(mCurrentPosition[1]);
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    // 移除图片
                    mCoordinatorLayout.removeView(goods);
                    // 购物车数量增加
                    mTvShoppingCartCount.setText(String.valueOf(allCount));
                }
            });
        }
    

    5 底部购物车弹窗动画

    底部的一个动画效果,大致原理就是,用一个FrameLayout,底部保持不动,上面的ViewGroup做位移动画,同时判断商品个数,动态控制RecycleView的高度
    • 动态设置RecycleView的高度
    private void initAdapter() {
            // 如果商品个数大于指定数时,高度写死,其他wrap_content
            if (list.size() >= 4) {
                ViewGroup.LayoutParams lp = mRvCartGoods.getLayoutParams();
                lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
                lp.height = DensityUtil.dp2px(200);
                mRvCartGoods.setLayoutParams(lp);
            }
            mAdapter = new BaseAdapter(list, R.layout.item_cart_goods, this);
            mRvCartGoods.setLayoutManager(new LinearLayoutManager(mActivity));
            mRvCartGoods.setAdapter(mAdapter);
        }
    
    网上找过很多设置最大值的,都没有效果,后面直接改用代码动态设置
    • 动画效果
    private void initScreen() {
            WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics dm = new DisplayMetrics();
            wm.getDefaultDisplay().getMetrics(dm);
            // 获取屏幕的高度
            mHeightPixels = dm.heightPixels;
        }
    
        public void openAnim() {
            ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mLlShoppingCart, "translationY", mHeightPixels, 0);
            objectAnimator.setDuration(TIME);
            objectAnimator.start();
        }
    
        public void closeAnim() {
            ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mLlShoppingCart, "translationY", 0, mHeightPixels);
            objectAnimator.setDuration(TIME);
            objectAnimator.start();
            objectAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    dismiss();
                }
            });
        }
    
    可以看到,在关闭的时候做了一个动画监听,直接调用dismiss()方法,会直接销毁掉,我们要等到动画执行完毕之后在销毁掉
    购物车缓存
    storeId为文件名,同时内部用一个map集合储存数据,key为goodsID,value为count,allCount为商铺总数
        /**
       * 添加商品缓存
       *
       * @param storeId 商品的id
       */
      public ShoppingCartHistoryManager add(String storeId, @NonNull StoreGoodsBean storeGoodsBean) {
        File file = new File(PATH);
        if (!file.exists()) {
          file.mkdirs();
        }
        FileOutputStream fileOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
          fileOutputStream = new FileOutputStream(file.getAbsolutePath() + File.separator + storeId + FILE_FORMAT);
          objectOutputStream = new ObjectOutputStream(fileOutputStream);
          objectOutputStream.writeObject(storeGoodsBean);
          objectOutputStream.flush();
          objectOutputStream.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
        return this;
      }
    
      /**
       * 得到商品缓存
       *
       * @param storeId 商铺的id
       */
      public HashMap<String, Integer> get(String storeId) {
        File file = new File(PATH);
        if (!file.exists()) {
          return null;
        }
        FileInputStream FileInputStream = null;
        ObjectInputStream objectInputStream = null;
        StoreGoodsBean storeGoodsBean = null;
        try {
          FileInputStream = new FileInputStream(file.getAbsolutePath() + File.separator + storeId + FILE_FORMAT);
          objectInputStream = new ObjectInputStream(FileInputStream);
          storeGoodsBean = (StoreGoodsBean) objectInputStream.readObject();
          objectInputStream.close();
        } catch (Exception e) {
          e.printStackTrace();
          return null;
        }
        return storeGoodsBean.getHashMap();
      }
    
      /**
       * 获取商铺选择的总个数
       *
       * @param storeId 商铺id
       * @return
       */
      public int getAllGoodsCount(String storeId) {
        HashMap<String, Integer> hashMap = get(storeId);
        int allCount = 0;
        if (hashMap != null) {
          for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            Integer value = entry.getValue();
            if (value != 0) {
              allCount += value;
            }
          }
        }
        return allCount;
      }
    
      /**
       * 删除商铺缓存,如果数量为0
       *
       * @param storeId 商铺的id
       */
      public ShoppingCartHistoryManager delete(@NonNull String storeId) {
        File file = new File(PATH, storeId + FILE_FORMAT);
        if (file.exists()) {
          file.delete();
        }
        return this;
      }
    
        /**
         * 删除商铺缓存,如果数量为0
         *
         * @param ShopId 商铺的id
         */
        public ShoppingCartHistoryManager delete(@NonNull int ShopId) {
            File file = new File(PATH, +ShopId + FILE_FORMAT);
            if (file.exists()) {
                file.delete();
            }
            return this;
        }
    
    退出页面时,根据商铺的总数是否为0,选择删除数据(如果文件之前存在)还是保存数据
     @Override
        protected void onDestroy() {
            super.onDestroy();
            if (allCount != 0) {
                HashMap<String, Integer> hashMap = new HashMap<>();
                StoreGoodsBean storeGoodsBean = new StoreGoodsBean(hashMap);
                for (ShopGoodsBean bean : mGoodsList) {
                    int count = bean.getCount();
                    String goodsId = bean.getGoodsId();
                    if (count != 0) {
                        hashMap.put(goodsId, count);
                    }
                }
                ShoppingCartHistoryManager.getInstance().add(SHOP_ID, storeGoodsBean);
            } else {
                ShoppingCartHistoryManager.getInstance().delete(SHOP_ID);
            }
        }
    
    进入页面的时候,获取缓存数据(如果存在)
    private void initData() {
            int id = 0x100;
            HashMap<String, Integer> hashMap = ShoppingCartHistoryManager.getInstance().get(SHOP_ID);
            this.allCount = ShoppingCartHistoryManager.getInstance().getAllGoodsCount(SHOP_ID);
            showToast("商品总数" + allCount);
            // 根据缓存是否显示
            mTvShoppingCartCount.setVisibility(allCount == 0 ? View.GONE : View.VISIBLE);
            mTvShoppingCartCount.setText(String.valueOf(allCount));
            // TODO: 2018/6/5 0005 模拟请求到的数据
            for (int i = 0; i < 10; i++) {
                mGoodsList.add(new ShopGoodsBean(0, "小猪包套餐" + i, id++ + ""));
            }
            if (hashMap != null) {
                for (ShopGoodsBean bean : mGoodsList) {
                    String goodsId = bean.getGoodsId();
                    if (hashMap.containsKey(goodsId)) {
                        Integer count = hashMap.get(goodsId);
                        bean.setCount(count);
                    }
                }
            }
        }
    
    在最外面的商铺列表通过storeId获取商铺总数
    this.allCount =ShoppingCartHistoryManager.getInstance().getAllGoodsCount(SHOP_ID);
    
    github:https://github.com/GongWnbo/SuperRecycleView
    有疑问,不足的地方,或更好的方式,请以留言的方式告知,如果觉得不错就顺手给个Star![]

    相关文章

      网友评论

      • 9ac0b73d7717:我点击一条数据添加到recycleview的底部时如何让视图在底部显示我添加的数据呢?
        Master_文:先用notifyItemInserted,你要刷新的位置,然后在scrollToPosition
      • 七岁就狠拽:最近也在写类似购物车的demo, 加减按钮用张旭童写的AnimShopButton还是很方便的, 购物车的弹出和关闭其实不用动态改变RecyclerView的高度, 改变transY的值就行了, 这样再购物车中进行商品的删除时还能进行动画效果, 达到高仿饿了么
        七岁就狠拽:@Master_文 看到了, 你不做删减条目时候的动态改变高度的动画, 这个就无所谓了
        Master_文:@七岁就狠拽 购物车的弹出,是防止条目数过多,导致页面过长
      • d332415439cf:导航栏没去掉啊
        d332415439cf:还有在animClose中为什么在onAnimationEnd中还要再进行动画了呢?不多余吗?
        d332415439cf:shoppingGoodsActivity还得重新padding一下,不更费事了么
        Master_文:@不动蛋蛋 去掉导航栏要添加一个flag属性,具体百度一下
      • 帘外海棠别人叫了:棒棒哒!
        Master_文:@andy2222 那必须的:relaxed:
        帘外海棠别人叫了:@Master_文 一定的哦 ,那你可多发点呀
        Master_文:@andy2222 谢谢:smile: ,顺带求点赞和star:wink:
      • linruihang::smile: 谢谢分享
        Master_文:@linruihang 个人觉得分享可以让自己知道有哪些不足,欢迎大佬们提出宝贵的意见:smile:
      • yanix4536:很棒,写得很好!!!
        Master_文:@yanix4536 谢谢:wink:

      本文标题:仿饿了么添加购物车效果

      本文链接:https://www.haomeiwen.com/subject/gywnjftx.html