Android 美团购物车效果 您所在的位置:网站首页 仿美团外卖app源码 Android 美团购物车效果

Android 美团购物车效果

2023-10-18 08:33| 来源: 网络整理| 查看: 265

老规矩先上效果图

美团购物车.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展示的,效果图有了,还是要用文字介绍下的。 效果就是左右两个列表,左侧列表点击时,右侧的标题自动显示到列表的顶部,标题是悬浮吸顶的,没组的标题固定悬浮在顶部,当右侧列表滑动时,左侧列表自动定位至和左侧相同的分类保持统一,底部的弹出购物车区域,购物车的高度是在屏幕的70%以下是自适应的高度,最大高度是当前屏幕的70%,下边是部分代码和实现思路。

这个效果主要要处理的就是两个RecyclerView 的互相交互和数据处理

image.png

这就是整个页面的主要布局,两个recyclerView

至于右侧又一个热销水果的标题,其实使用StickyHeaderLayoutManager 也可以使用标题吸顶,但是使用这个类的话,在后边左侧点击让右侧显示到顶部的时候会特别难处理,而且StickyHeaderLayoutManager这个Manager里边也没有recyclerView的.scrollToPositionWithOffset()方法。 这个方法主要就是为了让这个东西在顶部

image.png

这里提到了scrollToPositionWithOffset()方法,就顺便说一下scrollToPosition和scrollToPositionWithOffset的区别

scrollToPosition 会把不在屏幕的 Item 移动到屏幕上,原来在上方的 Item 移动到 可见 Item 的第一项,在下方的移动到屏幕可见 Item 的最后一项。已经显示的 Item 不会移动。

scrollToPositionWithOffset 会把 Item 移动到可见 Item 的第一项,即使它已经在可见 Item 之中。另外它还有 offset 参数,表示 Item 移动到第一项后跟 RecyclerView 上边界或下边界之间的距离(默认是 0)

要实现这个效果还就得使用scrollToPositionWithOffset()这个方法,我也没有重写一个Manager,就这样直接使用了

使用到的数据结构 image.png

就是一个简单的省市结构类型 这里我也把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":"热销水果"}]

数据源准备完之后就开始下面的实现了

设置adapter //设置数据源,数据绑定展示 leftAdapter = new LeftProductTypeAdapter(MainActivity.this, productListEntities); rightAdapter = new RightProductAdapter(MainActivity.this, productListEntities, shopCart); rightMenu.setAdapter(rightAdapter); leftMenu.setAdapter(leftAdapter); //左侧列表单项选择 leftAdapter.addItemSelectedListener(this); rightAdapter.setShopCartImp(this); //设置初始头部 initHeadView();

刚才从图上也看到了右侧列表又一个标题,这个标题就是为了占位和显示使用

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

别忘了设置LayoutManager,这里为什么使用LinearLayoutManager在上边也说了,主要是使用LinearLayoutManager的scrollToPositionWithOffset()方法

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

数据绑定之后,就是列表的滑动了,先说右侧列表数据滑动,然后让左侧选中

右侧列表滑动监听 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.getMeasuredHeight() * 4 / 5 && !leftClickType) {// underView.getTop()>9 int dealtY = underView.getTop() - headerLayout.getMeasuredHeight(); headerLayout.setTranslationY(dealtY); } else if (dy < 0 && headerLayout.getTranslationY() = 0) { this.mSelectedNum = selectedNum; notifyDataSetChanged(); } }

然后adapter中设置选中的样式就可以

@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); }

说完了右侧滑动让左侧选中,那么接下来就是左侧点击让右侧对应的分组显示出来

左侧列表点击,右侧分组显示在顶部

在LeftProductTypeAdapter中暴露接口

public interface onItemSelectedListener { public void onLeftItemSelected(int postion, ProductListEntity menu); } public void addItemSelectedListener(onItemSelectedListener listener) { if (mSelectedListenerList != null) mSelectedListenerList.add(listener); }

在Activity中实现,这里直接使用scrollToPositionWithOffset方法就可以了,相对来说比较简单

/** * 左侧列表单项选中 * * @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; }

目前为至,左右两个列表就可以实现交互了,左侧点击右侧显示指定数据,右侧滑动左侧选中对应的内容 列表的联动处理完成,接下来就是右侧列表商品加减操作了

右侧列表加减处理

右侧列表的加减操作这里没有暴露到Activity中操作,是在Adapter中设置的

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); //当前数字变化刷新 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); //当前数字变化刷新 notifyDataSetChanged(); if (shopCartImp != null) shopCartImp.remove(view, position,dish); } } });

这里没有使用notifyItemChanged()方法刷新,因为这个方法,完成操作之后刷新,商品图片有闪烁效果,所以这里使用的notifyDataSetChanged()方法替代

在上边adapter中的处理中,我们看到了shopCart.addShoppingSingle(dish) 的判读 这里的shopCart 是一个购物车实体,可以理解为 中转处 看下ShopCart的内容

public class ShopCart { private int shoppingAccount;//数量 private double shoppingTotalPrice;//购物车总价格 private Map shoppingSingle;//保存数量 private Map parentCountMap;//父保存数量 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) { 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); } /** * 初始数据绑定及操作 */ private void initDataViewBind() { //数据绑定及展示 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")); } /** * 更新数字 */ private void updateShoppingCartNum() { if (shopCart != null) { int textCount = 0; for (ProductListEntity.ProductEntity m : shopCart.getShoppingSingle().keySet()) { Log.e("btn_shopping_cart_pay", "map集合中存储的数据---->" + 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说明没数据了,如果购物车弹窗开着,则关闭 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来通知更新右侧列表数量和左侧列表的角标更新 对应的接受方法

//定义处理接收的方法 @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(); } }

最后就是清空数据和提交时提交的数据

清空 /** * 清空购物车及左侧列表都角标和商品列表 */ private void clearCartDataAndListData() { shopCart.clear(); shopCart.getParentCountMap().clear(); showTotalPrice(null); //左侧清空 leftAdapter.setClearCount(); } 提交 //结算的商品列表 ToastUtil.showShort(MainActivity.this, "dianjile"); if (shopCart.getShoppingSingle().size() > 0) { List commitListData = new ArrayList(); for (ProductListEntity.ProductEntity m : shopCart.getShoppingSingle().keySet()) { Log.e("btn_cart_pay", "map集合中存储的数据---->" + 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)); }

这里提交的数据其实就是改变了count的对象,由于后台要快照,所以会要求我们给传递数据,所以咋回来的,咋在给他们就完了,只把最后的数量更改提交就可以了。

提交的例子 [{ "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

项目自己下载一下,代码挺简单的,注释也写了不算少,代码还没进行优化处理,效果和基本的思路是这样的,如果有这种效果更好的实现思路和方法的也欢迎各位大神指教,共同进步。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有