Android 美團購物車效果

老規(guī)矩先上效果圖


美團購物車.gif

2020-11-23新增吸頂和Fragment配合


gif (2).gif

GIF圖有點不清楚,再上兩張截圖


Wec1111.jpeg
Wech222.jpeg

項目地址:https://gitee.com/dingxiansen/Vehicle-keyboard-android/tree/master/meituanshoppingcart 需要的自取
github地址:https://github.com/DingXianSen/ProDemo/tree/master/meituanshoppingcart

效果就是gif展示的,效果圖有了,還是要用文字介紹下的。
效果就是左右兩個列表,左側(cè)列表點擊時,右側(cè)的標(biāo)題自動顯示到列表的頂部,標(biāo)題是懸浮吸頂?shù)模瑳]組的標(biāo)題固定懸浮在頂部,當(dāng)右側(cè)列表滑動時,左側(cè)列表自動定位至和左側(cè)相同的分類保持統(tǒng)一,底部的彈出購物車區(qū)域,購物車的高度是在屏幕的70%以下是自適應(yīng)的高度,最大高度是當(dāng)前屏幕的70%,下邊是部分代碼和實現(xiàn)思路。

這個效果主要要處理的就是兩個RecyclerView 的互相交互和數(shù)據(jù)處理


image.png

這就是整個頁面的主要布局,兩個recyclerView

至于右側(cè)又一個熱銷水果的標(biāo)題,其實使用StickyHeaderLayoutManager 也可以使用標(biāo)題吸頂,但是使用這個類的話,在后邊左側(cè)點擊讓右側(cè)顯示到頂部的時候會特別難處理,而且StickyHeaderLayoutManager這個Manager里邊也沒有recyclerView的.scrollToPositionWithOffset()方法。
這個方法主要就是為了讓這個東西在頂部


image.png

這里提到了scrollToPositionWithOffset()方法,就順便說一下scrollToPosition和scrollToPositionWithOffset的區(qū)別

scrollToPosition 會把不在屏幕的 Item 移動到屏幕上,原來在上方的 Item 移動到 可見 Item 的第一項,在下方的移動到屏幕可見 Item 的最后一項。已經(jīng)顯示的 Item 不會移動。

scrollToPositionWithOffset 會把 Item 移動到可見 Item 的第一項,即使它已經(jīng)在可見 Item 之中。另外它還有 offset 參數(shù),表示 Item 移動到第一項后跟 RecyclerView 上邊界或下邊界之間的距離(默認(rèn)是 0)

要實現(xiàn)這個效果還就得使用scrollToPositionWithOffset()這個方法,我也沒有重寫一個Manager,就這樣直接使用了

使用到的數(shù)據(jù)結(jié)構(gòu)

image.png

就是一個簡單的省市結(jié)構(gòu)類型
這里我也把JSON放上來了
[{"productEntities":[{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"1","productImg":"img地址","productMoney":10.0,"productMonth":"34","productName":"新上市獼猴桃1-1"},{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"2","productImg":"img地址","productMoney":20.0,"productMonth":"34","productName":"新上市獼猴桃2-1"},{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"3","productImg":"img地址","productMoney":30.0,"productMonth":"34","productName":"新上市獼猴桃3-1"},{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"4","productImg":"img地址","productMoney":40.0,"productMonth":"34","productName":"新上市獼猴桃4-1"},{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"5","productImg":"img地址","productMoney":50.0,"productMonth":"34","productName":"新上市獼猴桃5-1"},{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"6","productImg":"img地址","productMoney":50.0,"productMonth":"34","productName":"新上市獼猴桃6-1"},{"parentId":"1","productCartMoney":0.0,"productCount":0,"productId":"7","productImg":"img地址","productMoney":50.0,"productMonth":"34","productName":"新上市獼猴桃7-1"}],"typeCount":0,"typeId":"1","typeName":"熱銷水果"}]

數(shù)據(jù)源準(zhǔn)備完之后就開始下面的實現(xiàn)了

設(shè)置adapter

  //設(shè)置數(shù)據(jù)源,數(shù)據(jù)綁定展示
        leftAdapter = new LeftProductTypeAdapter(MainActivity.this, productListEntities);
        rightAdapter = new RightProductAdapter(MainActivity.this, productListEntities, shopCart);


        rightMenu.setAdapter(rightAdapter);
        leftMenu.setAdapter(leftAdapter);
        //左側(cè)列表單項選擇
        leftAdapter.addItemSelectedListener(this);
        rightAdapter.setShopCartImp(this);
        //設(shè)置初始頭部
        initHeadView();

剛才從圖上也看到了右側(cè)列表又一個標(biāo)題,這個標(biāo)題就是為了占位和顯示使用

 /**
     * 初始頭部
     */
    private void initHeadView() {
        headMenu = rightAdapter.getMenuOfMenuByPosition(0);
        headerLayout.setContentDescription("0");
        headerView.setText(headMenu.getTypeName());
    }

別忘了設(shè)置LayoutManager,這里為什么使用LinearLayoutManager在上邊也說了,主要是使用LinearLayoutManager的scrollToPositionWithOffset()方法

 leftMenu.setLayoutManager(new LinearLayoutManager(this));
        rightMenu.setLayoutManager(new LinearLayoutManager(this));

數(shù)據(jù)綁定之后,就是列表的滑動了,先說右側(cè)列表數(shù)據(jù)滑動,然后讓左側(cè)選中

右側(cè)列表滑動監(jiān)聽

rightMenu.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (recyclerView.canScrollVertically(1) == false) {//無法下滑
                    showHeadView();
                    return;
                }

                View underView = null;
                if (dy > 0) {
                    underView = rightMenu.findChildViewUnder(headerLayout.getX(), headerLayout.getMeasuredHeight() + 1);
                } else {
                    underView = rightMenu.findChildViewUnder(headerLayout.getX(), 0);
                }

                if (underView != null && underView.getContentDescription() != null) {
                    int position = Integer.parseInt(underView.getContentDescription().toString());
                    ProductListEntity menu = rightAdapter.getMenuOfMenuByPosition(position);

                    if (leftClickType || !menu.getTypeName().equals(headMenu.getTypeName())) {
                        if (dy > 0 && headerLayout.getTranslationY() <= 1 && headerLayout.getTranslationY() >= -1 * headerLayout.getMeasuredHeight() * 4 / 5 && !leftClickType) {// underView.getTop()>9
                            int dealtY = underView.getTop() - headerLayout.getMeasuredHeight();
                            headerLayout.setTranslationY(dealtY);
                        } else if (dy < 0 && headerLayout.getTranslationY() <= 0 && !leftClickType) {
                            headerView.setText(menu.getTypeName());
                            int dealtY = underView.getBottom() - headerLayout.getMeasuredHeight();
                            headerLayout.setTranslationY(dealtY);
                        } else {
                            headerLayout.setTranslationY(0);
                            headMenu = menu;
                            headerView.setText(headMenu.getTypeName());
                            for (int i = 0; i < productListEntities.size(); i++) {
                                if (productListEntities.get(i) == headMenu) {
                                    leftAdapter.setSelectedNum(i);
                                    break;
                                }
                            }
                            if (leftClickType) leftClickType = false;
                        }
                    }
                }

            }
        });
   private void showHeadView() {
        headerLayout.setTranslationY(0);
        View underView = rightMenu.findChildViewUnder(headerLayout.getX(), 0);
        if (underView != null && underView.getContentDescription() != null) {
            int position = Integer.parseInt(underView.getContentDescription().toString());
            ProductListEntity entity = rightAdapter.getMenuOfMenuByPosition(position + 1);
            headMenu = entity;
            headerView.setText(headMenu.getTypeName());
            for (int i = 0; i < productListEntities.size(); i++) {
                if (productListEntities.get(i) == headMenu) {
                    leftAdapter.setSelectedNum(i);
                    break;
                }
            }
        }
    }

以上代碼就是兩個列表滑動交互右側(cè)的主要代碼,以上實現(xiàn)的是右側(cè)滑動分組置頂?shù)男Ч?br> 接下來就是

右側(cè)滑動,左側(cè)選中

選中的主要代碼是LeftProductTypeAdapter中的setSelectedNum();

   /**
     * 選中左側(cè)區(qū)域
     *
     * @param selectedNum
     */
    public void setSelectedNum(int selectedNum) {
        if (selectedNum < getItemCount() && selectedNum >= 0) {
            this.mSelectedNum = selectedNum;
            notifyDataSetChanged();
        }
    }

然后adapter中設(shè)置選中的樣式就可以

 @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ProductListEntity dishMenu = mMenuList.get(position);
        LeftMenuViewHolder viewHolder = (LeftMenuViewHolder) holder;
        viewHolder.menuName.setText(dishMenu.getTypeName());
        if (mSelectedNum == position) {
            viewHolder.menuLayout.setSelected(true);
        } else {
            viewHolder.menuLayout.setSelected(false);
        }

說完了右側(cè)滑動讓左側(cè)選中,那么接下來就是左側(cè)點擊讓右側(cè)對應(yīng)的分組顯示出來

左側(cè)列表點擊,右側(cè)分組顯示在頂部

在LeftProductTypeAdapter中暴露接口

public interface onItemSelectedListener {
        public void onLeftItemSelected(int postion, ProductListEntity menu);
    }

    public void addItemSelectedListener(onItemSelectedListener listener) {
        if (mSelectedListenerList != null)
            mSelectedListenerList.add(listener);
    }

在Activity中實現(xiàn),這里直接使用scrollToPositionWithOffset方法就可以了,相對來說比較簡單

    /**
     * 左側(cè)列表單項選中
     *
     * @param position
     * @param menu
     */
    @Override
    public void onLeftItemSelected(int position, ProductListEntity menu) {
        int sum = 0;
        for (int i = 0; i < position; i++) {
            sum += productListEntities.get(i).getProductEntities().size() + 1;
        }
//        StickyHeaderLayoutManager layoutManager = (StickyHeaderLayoutManager) rightMenu.getLayoutManager();
        LinearLayoutManager layoutManager = (LinearLayoutManager) rightMenu.getLayoutManager();
        rightMenu.scrollToPosition(position);
        layoutManager.scrollToPositionWithOffset(sum, 0);
        leftClickType = true;
    }

目前為至,左右兩個列表就可以實現(xiàn)交互了,左側(cè)點擊右側(cè)顯示指定數(shù)據(jù),右側(cè)滑動左側(cè)選中對應(yīng)的內(nèi)容
列表的聯(lián)動處理完成,接下來就是右側(cè)列表商品加減操作了

右側(cè)列表加減處理

右側(cè)列表的加減操作這里沒有暴露到Activity中操作,是在Adapter中設(shè)置的

RightProductAdapter

 //加減點擊時間
                dishholder.iv_group_list_item_count_add.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Log.e("posss", "-------------------posss:" + posss);
                        if (shopCart.addShoppingSingle(dish)) {
//                            notifyItemChanged(position);
                            //當(dāng)前數(shù)字變化刷新
                            notifyDataSetChanged();
                            if (shopCartImp != null) {
                                shopCartImp.add(view, position,dish);

                            }
                        }
                    }
                });

                dishholder.iv_group_list_item_count_reduce.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        if (shopCart.subShoppingSingle(dish)) {
//                            notifyItemChanged(position);
                            //當(dāng)前數(shù)字變化刷新
                            notifyDataSetChanged();
                            if (shopCartImp != null)
                                shopCartImp.remove(view, position,dish);

                        }
                    }
                });

這里沒有使用notifyItemChanged()方法刷新,因為這個方法,完成操作之后刷新,商品圖片有閃爍效果,所以這里使用的notifyDataSetChanged()方法替代

在上邊adapter中的處理中,我們看到了shopCart.addShoppingSingle(dish) 的判讀
這里的shopCart 是一個購物車實體,可以理解為 中轉(zhuǎn)處
看下ShopCart的內(nèi)容

public class ShopCart {
    private int shoppingAccount;//數(shù)量
    private double shoppingTotalPrice;//購物車總價格
    private Map<ProductListEntity.ProductEntity, Integer> shoppingSingle;//保存數(shù)量
    private Map<String, Integer> parentCountMap;//父保存數(shù)量


    public ShopCart() {
        this.shoppingAccount = 0;
        this.shoppingTotalPrice = 0.0;
        this.shoppingSingle = new HashMap<>();
        this.parentCountMap = new HashMap<>();
    }

    public boolean addShoppingSingle(ProductListEntity.ProductEntity dish) {
        double remain = dish.getProductCartMoney();
//        if (remain <= 0)
//            return false;
        //商品的價格,減操作直接--
        dish.setProductCartMoney(--remain);
        int num = 0;
        if (shoppingSingle.containsKey(dish)) {
            num = shoppingSingle.get(dish);
        }
        num += 1;
        /***/
        dish.setProductCount(num);
        shoppingSingle.put(dish, num);

        //如果這個map存在這個父ID的值
        int parentNum = 0;
        if (parentCountMap.containsKey(dish.getParentId())) {
            parentNum = parentCountMap.get(dish.getParentId());
            parentNum += 1;
        } else {//如果第一次存儲
            parentNum = 1;
        }
        parentCountMap.put(dish.getParentId(), parentNum);

        Log.e("TAG", "addShoppingSingle: " + shoppingSingle.get(dish));
        shoppingTotalPrice += dish.getProductMoney();//加商品的正常價格
        shoppingAccount += num;
        return true;
    }

    public boolean subShoppingSingle(ProductListEntity.ProductEntity dish) {
        int num = 0;
        if (shoppingSingle.containsKey(dish)) {
            num = shoppingSingle.get(dish);
        }
        if (num <= 0) return false;
        num--;
        double remain = dish.getProductCartMoney();
        dish.setProductCartMoney(++remain);
        dish.setProductCount(num);
        shoppingSingle.put(dish, num);
        if (num == 0) {
            shoppingSingle.remove(dish);
        }

        //如果這個map存在這個父ID的值
        int parentNum = 0;
        if (parentCountMap.containsKey(dish.getParentId())) {
            parentNum = parentCountMap.get(dish.getParentId());
            parentNum -= 1;
            parentCountMap.put(dish.getParentId(), parentNum);
        }
        shoppingTotalPrice -= dish.getProductMoney();
        shoppingAccount -= num;
        return true;
    }


    public int getShoppingAccount() {
        return shoppingSingle.size();
    }

    public void setShoppingAccount(int shoppingAccount) {
        this.shoppingAccount = shoppingAccount;
    }

    public double getShoppingTotalPrice() {
        return shoppingTotalPrice;
    }

    public void setShoppingTotalPrice(double shoppingTotalPrice) {
        this.shoppingTotalPrice = shoppingTotalPrice;
    }

    public Map<ProductListEntity.ProductEntity, Integer> getShoppingSingle() {
        return shoppingSingle;
    }

    public void setShoppingSingle(Map<ProductListEntity.ProductEntity, Integer> shoppingSingle) {
        this.shoppingSingle = shoppingSingle;
    }

    public Map<String, Integer> getParentCountMap() {
        return parentCountMap;
    }

    public void setParentCountMap(Map<String, Integer> parentCountMap) {
        this.parentCountMap = parentCountMap;
    }

    public void clear() {
        this.shoppingAccount = 0;
        this.shoppingTotalPrice = 0;
        this.shoppingSingle.clear();
    }

說了點擊在adapter中實現(xiàn),但是還有加入動畫,所以還是暴露接口給Activity使用

ShopCartImp

public interface ShopCartImp {
    void add(View view, int postion, ProductListEntity.ProductEntity entity);

    void remove(View view, int postion, ProductListEntity.ProductEntity entity);
}

右側(cè)列表加+

由于只有在加的時候才有動畫效果,只有只給add設(shè)置addCart動畫效果,加只需要注意是不是第一次添加就可以,如果是第一次添加,ShopCart中已經(jīng)判斷了,第一次直接put,否則就只改變count就可以了

   /**
     * 購物車+
     *
     * @param view
     * @param position
     */
    @Override
    public void add(View view, int position, ProductListEntity.ProductEntity entity) {
        addCart(view, entity);
    }

加入購物車動畫方法

 //加入購物車曲線動畫
    private void addCart(View view, ProductListEntity.ProductEntity entity) {
//   一、創(chuàng)造出執(zhí)行動畫的主題---imageview
        //代碼new一個imageview,圖片資源是上面的imageview的圖片
        // (這個圖片就是執(zhí)行動畫的圖片,從開始位置出發(fā),經(jīng)過一個拋物線(貝塞爾曲線),移動到購物車?yán)?
        final ImageView goods = new ImageView(MainActivity.this);
        goods.setImageDrawable(getResources().getDrawable(R.drawable.shape_shopping_cart_num_bg, null));
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(50, 50);
        rl.addView(goods, params);

//    二、計算動畫開始/結(jié)束點的坐標(biāo)的準(zhǔn)備工作
        //得到父布局的起始點坐標(biāo)(用于輔助計算動畫開始/結(jié)束時的點的坐標(biāo))
        int[] parentLocation = new int[2];
        rl.getLocationInWindow(parentLocation);

        //得到商品圖片的坐標(biāo)(用于計算動畫開始的坐標(biāo))
        int startLoc[] = new int[2];
        view.getLocationInWindow(startLoc);

        //得到購物車圖片的坐標(biāo)(用于計算動畫結(jié)束后的坐標(biāo))
        int endLoc[] = new int[2];
        iv_shopping_cart_img.getLocationInWindow(endLoc);


//    三、正式開始計算動畫開始/結(jié)束的坐標(biāo)
        //開始掉落的商品的起始點:商品起始點-父布局起始點+該商品圖片的一半
        float startX = startLoc[0] - parentLocation[0] + goods.getWidth() / 2;
        float startY = startLoc[1] - parentLocation[1] + goods.getHeight() / 2;

        //商品掉落后的終點坐標(biāo):購物車起始點-父布局起始點+購物車圖片的1/5
        float toX = endLoc[0] - parentLocation[0] + iv_shopping_cart_img.getWidth() / 5;
        float toY = endLoc[1] - parentLocation[1];

//    四、計算中間動畫的插值坐標(biāo)(貝塞爾曲線)(其實就是用貝塞爾曲線來完成起終點的過程)
        //開始繪制貝塞爾曲線
        Path path = new Path();
        //移動到起始點(貝塞爾曲線的起點)
        path.moveTo(startX, startY);
        //使用二次薩貝爾曲線:注意第一個起始坐標(biāo)越大,貝塞爾曲線的橫向距離就會越大,一般按照下面的式子取即可
        path.quadTo((startX + toX) / 2, startY, toX, toY);
        //mPathMeasure用來計算貝塞爾曲線的曲線長度和貝塞爾曲線中間插值的坐標(biāo),
        // 如果是true,path會形成一個閉環(huán)
        mPathMeasure = new PathMeasure(path, false);

        //★★★屬性動畫實現(xiàn)(從0到貝塞爾曲線的長度之間進行插值計算,獲取中間過程的距離值)
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
        valueAnimator.setDuration(500);
        // 勻速線性插值器
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 當(dāng)插值計算進行時,獲取中間的每個值,
                // 這里這個值是中間過程中的曲線長度(下面根據(jù)這個值來得出中間點的坐標(biāo)值)
                float value = (Float) animation.getAnimatedValue();
                // ★★★★★獲取當(dāng)前點坐標(biāo)封裝到mCurrentPosition
                // boolean getPosTan(float distance, float[] pos, float[] tan) :
                // 傳入一個距離distance(0<=distance<=getLength()),然后會計算當(dāng)前距
                // 離的坐標(biāo)點和切線,pos會自動填充上坐標(biāo),這個方法很重要。
                mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此時就是中間距離點的坐標(biāo)值
                // 移動的商品圖片(動畫圖片)的坐標(biāo)設(shè)置為該中間點的坐標(biāo)
                goods.setTranslationX(mCurrentPosition[0]);
                goods.setTranslationY(mCurrentPosition[1]);
            }
        });
//   五、 開始執(zhí)行動畫
        valueAnimator.start();

//   六、動畫結(jié)束后的處理
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            //當(dāng)動畫結(jié)束后:
            @Override
            public void onAnimationEnd(Animator animation) {
                //更新底部數(shù)據(jù)
                showTotalPrice(entity);
                // 把移動的圖片imageview從父布局里移除
                rl.removeView(goods);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

右側(cè)列表減-

/**
     * 購物車減
     *
     * @param view
     * @param position
     */
    @Override
    public void remove(View view, int position, ProductListEntity.ProductEntity en) {
        showTotalPrice(en);
    }

價格展示更新

   /**
     * 底部價格和數(shù)量顯示
     */
    private void showTotalPrice(ProductListEntity.ProductEntity entity) {
        if (shopCart != null && shopCart.getShoppingTotalPrice() > 0) {
            tv_shopping_cart_money.setVisibility(View.VISIBLE);
            tv_shopping_cart_money.setText("¥ " + shopCart.getShoppingTotalPrice());
            tv_shopping_cart_count.setVisibility(View.VISIBLE);
            //得到總的數(shù)量
            int textCount = 0;
            for (ProductListEntity.ProductEntity m : shopCart.getShoppingSingle().keySet()) {
                Log.e("btn_shopping_cart_pay", "map集合中存儲的數(shù)據(jù)---->" + m.getProductCount());
                textCount += m.getProductCount();
            }
            tv_shopping_cart_count.setText("" + textCount);
        } else {
            tv_shopping_cart_money.setVisibility(View.INVISIBLE);
            tv_shopping_cart_count.setVisibility(View.GONE);
        }
        updateLeftCount(entity);
    }

到了這里右側(cè)商品列表加和減還有加的動畫效果就完成了,接下來就是右側(cè)增加或者減少,怎么來改變左側(cè)的角標(biāo)顯示

左側(cè)角標(biāo)改變方法這里要注意的是

注意

有人可以在上邊數(shù)據(jù)結(jié)構(gòu)商品的對象中看到

ParentId

這個字段,有人可能會問這個有需要嗎,但是這個ID確實在這里用到了,當(dāng)然可能也有其他的實現(xiàn)方法可能不需要這個字段,這里是通過子項中的父級ID和左側(cè)列表中的ID來進行比對的,比對一致則說明我操作的數(shù)據(jù)屬于左側(cè)這一組中

 /**
     * 更新左側(cè)數(shù)字角標(biāo)(暫時不包含清空),觸發(fā)更新肯定是在加或者減的時候觸發(fā),根據(jù)子項中的父ID和左側(cè)ID比對,
     */
    private void updateLeftCount(ProductListEntity.ProductEntity entity) {
        if (shopCart != null) {
            //加和減的時候要知道是那個左側(cè)下邊的,知道下標(biāo)獲取父id,然后從map中取count
            if (entity != null) {
                Log.e("updateLeftCount", "-------parentId:" + entity.getParentId() + "---------count:" + shopCart.getParentCountMap().get(entity.getParentId()));
                leftAdapter.setUpdateMenuCount(entity.getParentId(), shopCart.getParentCountMap().get(entity.getParentId()));
            }
            if (rightAdapter != null) rightAdapter.notifyDataSetChanged();//跟新列表
        }
    }

LeftProductTypeAdapter中設(shè)置setUpdateMenuCount()方法

   /**
     * 更新左側(cè)角標(biāo),需要知道那個對象
     *
     * @param
     */
    public void setUpdateMenuCount(String parentId, int mUpdateParentCount) {
        //需要實體數(shù)據(jù)保存更新
        this.mUpdateParentId = parentId;
        this.mUpdateParentCount = mUpdateParentCount;
        notifyDataSetChanged();
        this.clearCount = false;

    }

同時在onBindViewHolder中判斷設(shè)置大于0才顯示

  @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ProductListEntity dishMenu = mMenuList.get(position);
        LeftMenuViewHolder viewHolder = (LeftMenuViewHolder) holder;
        viewHolder.menuName.setText(dishMenu.getTypeName());
        if (mSelectedNum == position) {
            viewHolder.menuLayout.setSelected(true);
        } else {
            viewHolder.menuLayout.setSelected(false);
        }

        if (dishMenu.getTypeId().equals(mUpdateParentId)) {//選中的ID
            //更改數(shù)據(jù)
            dishMenu.setTypeCount(mUpdateParentCount);
        }
        if (clearCount) {//隱藏所有數(shù)據(jù),設(shè)置count都為0
            viewHolder.tv_left_menu_count.setVisibility(View.GONE);
            dishMenu.setTypeCount(0);
        } else {
            viewHolder.tv_left_menu_count.setVisibility(View.VISIBLE);
            viewHolder.tv_left_menu_count.setText(dishMenu.getTypeCount() + "");
            dishMenu.setTypeCount(dishMenu.getTypeCount());
        }
        if (dishMenu.getTypeCount() > 0) {//展示
            viewHolder.tv_left_menu_count.setVisibility(View.VISIBLE);
            viewHolder.tv_left_menu_count.setText(dishMenu.getTypeCount() + "");
        } else {//隱藏
            viewHolder.tv_left_menu_count.setVisibility(View.GONE);
        }


    }

到這里右側(cè)加減操作,左側(cè)角標(biāo)也對應(yīng)變化展示了,最后就是購物車彈窗

展示底部購物車

展示底部購物車這里使用了XPopup,可以自己引入,也可以引我項目中的poplibrary庫,和meituanshoppingcart同級了

底部購物車展示

上面說的顯示當(dāng)前屏幕的百分之七十高度就是在這里設(shè)置的

 Log.e("getWindowHeight", "---------height:" + Tool.getWindowHeight(MainActivity.this));
                //獲取屏幕的高度,然后拿到百分之70
                int popHeight = (int) (Tool.getWindowHeight(MainActivity.this) * 0.7);
                if (shopCart != null && shopCart.getShoppingAccount() > 0) {
                    new XPopup.Builder(MainActivity.this)
                            .atView(view)
                            .maxHeight(popHeight)
                            .isRequestFocus(false)
                            .asCustom(new CustomPartShadowPopupView(MainActivity.this, shopCart))
                            .show();
                }

CustomPartShadowPopupView

/**
 * @className: CustomPartShadowPopupView
 * @description:
 * @author: dingchao
 * @time: 2020-11-19 15:13
 */
public class CustomPartShadowPopupView extends PartShadowPopupView implements ShopCartImp, View.OnClickListener {
    private ListView lv_pop_list;
    private Context context;
    private ShopCart shopCart;
    private TextView tv_shopping_cart_clear_all;
    private TextView tv_shopping_cart_top_key_v;
    ShoppingCartAdapter shoppingCartAdapter;

    public CustomPartShadowPopupView(@NonNull Context context, ShopCart shopCart) {
        super(context);
        this.context = context;
        this.shopCart = shopCart;
    }

    @Override
    protected int getImplLayoutId() {
        return R.layout.pop_shopping_cart;
    }

    @Override
    protected void onCreate() {
        super.onCreate();
        initListener();
        initDataViewBind();
    }

    /**
     * 控件初始綁定
     */
    private void initListener() {
        lv_pop_list = findViewById(R.id.lv_pop_list);
        tv_shopping_cart_clear_all = findViewById(R.id.tv_shopping_cart_clear_all);
        tv_shopping_cart_top_key_v = findViewById(R.id.tv_shopping_cart_top_key_v);
        tv_shopping_cart_clear_all.setOnClickListener(this);
    }


    /**
     * 初始數(shù)據(jù)綁定及操作
     */
    private void initDataViewBind() {
        //數(shù)據(jù)綁定及展示
        shoppingCartAdapter = new ShoppingCartAdapter(context, shopCart);
        lv_pop_list.setAdapter(shoppingCartAdapter);
        shoppingCartAdapter.setShopCartImp(this);
        updateShoppingCartNum();
    }

    @Override
    protected void onShow() {
        super.onShow();
    }

    @Override
    protected void onDismiss() {
        super.onDismiss();
    }

    @Override
    public void add(View view, int postion, ProductListEntity.ProductEntity entity) {
        updateShoppingCartNum();
        EventBus.getDefault().post(new EventBusShoppingEntity(entity, "add"));
    }

    /**
     * 更新數(shù)字
     */
    private void updateShoppingCartNum() {
        if (shopCart != null) {
            int textCount = 0;
            for (ProductListEntity.ProductEntity m : shopCart.getShoppingSingle().keySet()) {
                Log.e("btn_shopping_cart_pay", "map集合中存儲的數(shù)據(jù)---->" + m.getProductCount());
                textCount += m.getProductCount();
            }
            tv_shopping_cart_top_key_v.setText("(共" + textCount + "件商品)");
        }
    }

    @Override
    public void remove(View view, int postion, ProductListEntity.ProductEntity entity) {
        //判讀count是不是到0了,到0說明沒數(shù)據(jù)了,如果購物車彈窗開著,則關(guān)閉
        updateShoppingCartNum();
        EventBus.getDefault().post(new EventBusShoppingEntity(entity, "reduce"));
        if (shopCart != null && shopCart.getShoppingAccount() == 0) {
            this.dismiss();
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.tv_shopping_cart_clear_all:
                //清空
                shopCart.clear();
                this.dismiss();
                updateShoppingCartNum();
                EventBus.getDefault().post(new EventBusShoppingEntity(null, "clearAll"));
                break;
            default:
                break;
        }
    }
}

這里使用了EventBus來進行通知Activity來通知更新右側(cè)列表數(shù)量和左側(cè)列表的角標(biāo)更新
對應(yīng)的接受方法

 //定義處理接收的方法
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(EventBusShoppingEntity entity) {
        if (entity.getKey().equals("add")) {
            showTotalPrice(entity.getEntity());
        } else if (entity.getKey().equals("reduce")) {
            showTotalPrice(entity.getEntity());
        } else if (entity.getKey().equals("clearAll")) {//清空全部
            clearCartDataAndListData();
        }
    }

最后就是清空數(shù)據(jù)和提交時提交的數(shù)據(jù)

清空

   /**
     * 清空購物車及左側(cè)列表都角標(biāo)和商品列表
     */
    private void clearCartDataAndListData() {
        shopCart.clear();
        shopCart.getParentCountMap().clear();
        showTotalPrice(null);
        //左側(cè)清空
        leftAdapter.setClearCount();
    }

提交

  //結(jié)算的商品列表
                ToastUtil.showShort(MainActivity.this, "dianjile");
                if (shopCart.getShoppingSingle().size() > 0) {
                    List<ProductListEntity.ProductEntity> commitListData = new ArrayList<>();
                    for (ProductListEntity.ProductEntity m : shopCart.getShoppingSingle().keySet()) {
                        Log.e("btn_cart_pay", "map集合中存儲的數(shù)據(jù)---->" + m.getProductCount());
                        commitListData.add(m);
                    }
                    for (int i = 0; i < commitListData.size(); i++) {
                        Log.e("btn_cart_pay_list", "commitList---->" + commitListData.get(i));
                    }
                    Log.e("btn_cart_pay_list_JSON", "commitList---->" + JSON.toJSONString(commitListData));
                }

這里提交的數(shù)據(jù)其實就是改變了count的對象,由于后臺要快照,所以會要求我們給傳遞數(shù)據(jù),所以咋回來的,咋在給他們就完了,只把最后的數(shù)量更改提交就可以了。

提交的例子

[{
    "parentId": "1",
    "productCount": 2,
    "productId": "1",
    "productImg": "img地址",
    "productMoney": 10.0,
    "productMonth": "34",
    "productName": "新上市獼猴桃1-1"
}, {
    "parentId": "1",
    "productCount": 1,
    "productId": "4",
    "productImg": "img地址",
    "productMoney": 40.0,
    "productMonth": "34",
    "productName": "新上市獼猴桃4-1"
}]

以上就是仿美團雙列表添加購物車交互的效果的大體代碼和邏輯,說的可能比較亂,需要完整代碼的可以去上面地址找

meituanshoppingcart

項目自己下載一下,代碼挺簡單的,注釋也寫了不算少,代碼還沒進行優(yōu)化處理,效果和基本的思路是這樣的,如果有這種效果更好的實現(xiàn)思路和方法的也歡迎各位大神指教,共同進步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容