Android仿外卖购物车功能
GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
【 如果你想靠AI翻身,你先需要一个靠谱的工具! 】
本文实例为大家分享了Android实现外卖购物车功能的具体代码,供大家参考,具体内容如下
先看看效果图:
知识点分析
效果图来看不复杂内容并没多少,值得介绍一下的知识点也就下面几个吧
- 列表标题悬停
- 左右列表滑动时联动
- 添加商品时的抛物线动画
- 底部弹出购物车清单
- 数据的同步
另外就是实现效果的时候可能会遇到的几个坑。。。
布局很简单直接进入代码
1:列表标题悬停
现在做项目列表什么的基本抛弃了ListView改用RecyclerView,上篇博客中的标题悬停也是使用了一个RecyclerView的开源项目sticky-headers-recyclerview,不过写这个demo的时候遇到了两个坑
1)、sticky-headers-recyclerview做悬停标题的时候scrollToPosition(int position)方法滚动的位置不准确。
2)、当布局复杂点的时候 如果RecyclerView的宽度自适应或者使用权重百分比之类可能会导致header显示空白。
并且该开源项目作者已经停止维护,所以这次又换回了StickyListHeadersListView。
需要购物车Demo的很多都是新手,这里简单介绍下StickyListHeadersListView的使用
1)、AS引用 gradle文件dependencies内添加
2)、xml文件中使用StickyListHeadersListView代替ListView
1 2 3 4 5 6 | < se.emilsjolander.stickylistheaders.StickyListHeadersListView android:layout_width = "match_parent" android:background = "#fff" android:id = "@+id/itemListView" android:layout_height = "match_parent" > </ se.emilsjolander.stickylistheaders.StickyListHeadersListView > |
3)、Adapter继承BaseAdapter和接口StickyListHeadersAdapter
StickyListHeadersAdapter接口包括两个方法
1 2 3 | View getHeaderView( int position, View convertView, ViewGroup parent); long getHeaderId( int position); |
代码中使用和ListView一样,下面是几个特有的方法,看方法名也很容易理解用途
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void setAreHeadersSticky( boolean areHeadersSticky); public boolean areHeadersSticky(); public void setOnHeaderClickListener(OnHeaderClickListener listener); public interface OnHeaderClickListener { public void onHeaderClick(StickyListHeadersListView l, View header, int itemPosition, long headerId, boolean currentlySticky); } public void setOnStickyHeaderChangedListener(OnStickyHeaderChangedListener listener); public interface OnStickyHeaderChangedListener { void onStickyHeaderChanged(StickyListHeadersListView l, View header, int itemPosition, long headerId); } public View getListChildAt( int index); public int getListChildCount(); |
2:左右列表联动
联动主要有两个效果
- 左侧列表点击选择分类,右侧列表滑动到对应分类
- 右侧列表滑动过程中左侧列表高亮的分类跟随变化
第一个效果简单,左侧列表item添加点击事件,事件中调用右侧列表的setSelection(int positon) 方法。
第二个效果要给右侧列表添加ScrollListener,根据列表中显示的第一条数据设置左侧选中的分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | listView.setOnScrollListener( new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //根据firstVisibleItem获取分类ID,根据分类id获取左侧要选中的位置 GoodsItem item = dataList.get(firstVisibleItem); if (typeAdapter.selectTypeId != item.typeId) { typeAdapter.selectTypeId = item.typeId; typeAdapter.notifyDataSetChanged(); //左侧列表是个RecyclerView 所以使用smoothScrollToPosition(int position) 使当对应position的item可以滚动显示出来 rvType.smoothScrollToPosition( int position)(getSelectedGroupPosition(item.typeId)); } } }); |
3:添加商品的动画
添加商品一共有三个动画
- 当商品从0到1 旋转左移显示出减号按钮
- 当商品从1到0 减号按钮旋转右移消失
- 添加商品时抛物线动画添加到购物车图标
前两个动画很简单可以分解成三个补间动画 旋转、平移、透明度。
可以用xml完成,也可以代码设置,不过有个小坑要注意一下 旋转动画一定要在平移动画前面,否则就不是滚动平移了,而是乱跳。。。
这里贴一下动画的代码设置方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | //显示减号的动画 private Animation getShowAnimation(){ AnimationSet set = new AnimationSet( true ); RotateAnimation rotate = new RotateAnimation( 0 , 720 ,RotateAnimation.RELATIVE_TO_SELF, 0 .5f,RotateAnimation.RELATIVE_TO_SELF, 0 .5f); set.addAnimation(rotate); TranslateAnimation translate = new TranslateAnimation( TranslateAnimation.RELATIVE_TO_SELF,2f ,TranslateAnimation.RELATIVE_TO_SELF, 0 ,TranslateAnimation.RELATIVE_TO_SELF, 0 ,TranslateAnimation.RELATIVE_TO_SELF, 0 ); set.addAnimation(translate); AlphaAnimation alpha = new AlphaAnimation( 0 , 1 ); set.addAnimation(alpha); set.setDuration( 500 ); return set; } //隐藏减号的动画 private Animation getHiddenAnimation(){ AnimationSet set = new AnimationSet( true ); RotateAnimation rotate = new RotateAnimation( 0 , 720 ,RotateAnimation.RELATIVE_TO_SELF, 0 .5f,RotateAnimation.RELATIVE_TO_SELF, 0 .5f); set.addAnimation(rotate); TranslateAnimation translate = new TranslateAnimation( TranslateAnimation.RELATIVE_TO_SELF, 0 ,TranslateAnimation.RELATIVE_TO_SELF,2f ,TranslateAnimation.RELATIVE_TO_SELF, 0 ,TranslateAnimation.RELATIVE_TO_SELF, 0 ); set.addAnimation(translate); AlphaAnimation alpha = new AlphaAnimation( 1 , 0 ); set.addAnimation(alpha); set.setDuration( 500 ); return set; } //执行动画 只需给对应控件setAnimation然后调用setVisibility方法即可 { .... tvMinus.setAnimation(getHiddenAnimation()); tvMinus.setVisibility(View.GONE); } |
抛物线动画和上面的差不多可以分解成两个平移动画,不过两个平移动画的差值器一个线性一个加速而已,因为动画界面跨度比较大所以需要在根部局内写,不能写在列表的item中(这样会显示不全)。
代码中的anim_mask_layout 即为整个布局文件的根布局,这里是一个RelativeLayout
实现过程:
1、首先点击加号图标,拿到控件在屏幕上的绝对坐标,回调activity显示动画
2、创建动画的控件并添加到根部局并在动画结束后移除动画view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | public void playAnimation( int [] start_location){ ImageView img = new ImageView( this ); img.setImageResource(R.drawable.button_add); setAnim(img,start_location); } //创建动画 平移动画直接传递偏移量 private Animation createAnim( int startX, int startY){ int [] des = new int [ 2 ]; imgCart.getLocationInWindow(des); AnimationSet set = new AnimationSet( false ); Animation translationX = new TranslateAnimation( 0 , des[ 0 ]-startX, 0 , 0 ); //线性插值器 默认就是线性 translationX.setInterpolator( new LinearInterpolator()); Animation translationY = new TranslateAnimation( 0 , 0 , 0 , des[ 1 ]-startY); //设置加速插值器 translationY.setInterpolator( new AccelerateInterpolator()); Animation alpha = new AlphaAnimation( 1 , 0 .5f); set.addAnimation(translationX); set.addAnimation(translationY); set.addAnimation(alpha); set.setDuration( 500 ); return set; } //计算动画view在根部局中的坐标 添加到根部局中 private void addViewToAnimLayout( final ViewGroup vg, final View view, int [] location) { int x = location[ 0 ]; int y = location[ 1 ]; int [] loc = new int [ 2 ]; vg.getLocationInWindow(loc); view.setX(x); view.setY(y-loc[ 1 ]); vg.addView(view); } //设置动画结束移除动画view private void setAnim( final View v, int [] start_location) { addViewToAnimLayout(anim_mask_layout, v, start_location); Animation set = createAnim(start_location[ 0 ],start_location[ 1 ]); set.setAnimationListener( new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd( final Animation animation) { //直接remove可能会因为界面仍在绘制中成而报错 mHanlder.postDelayed( new Runnable() { @Override public void run() { anim_mask_layout.removeView(v); } }, 100 ); } @Override public void onAnimationRepeat(Animation animation) { } }); v.startAnimation(set); } |
4:底部弹出购物车清单
底部弹出的效果大家一定都很熟悉了,几回每个项目中都会用的到,官方没有提供简单的控件实现,一般都需要自己写,不过要做到简单流畅,便于移植推荐使用第三方库,这里向大家推荐一个
集成简单,效果多样这里简单介绍一下使用方法
集成
使用
xml中使用BottomSheetLayout包裹弹出view时候的背景布局,BottomSheetLayout继承自帧布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | < com.flipboard.bottomsheet.BottomSheetLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:id = "@+id/bottomSheetLayout" android:layout_width = "match_parent" android:layout_height = "match_parent" > < LinearLayout android:orientation = "horizontal" android:layout_width = "match_parent" android:layout_height = "match_parent" > < android.support.v7.widget.RecyclerView android:layout_width = "100dp" android:id = "@+id/typeRecyclerView" android:layout_height = "match_parent" > </ android.support.v7.widget.RecyclerView > < se.emilsjolander.stickylistheaders.StickyListHeadersListView android:layout_width = "match_parent" android:background = "#fff" android:id = "@+id/itemListView" android:layout_height = "match_parent" > </ se.emilsjolander.stickylistheaders.StickyListHeadersListView > </ LinearLayout > </ com.flipboard.bottomsheet.BottomSheetLayout > |
代码中使用很简单
1 2 3 4 5 | //弹出View bottomSheet即是要弹出的view bottomSheetLayout.showWithSheetView(bottomSheet); //代码隐藏view (点击弹出view以外的地方可以隐藏弹出的view,向下滑动也可以) bottomSheetLayout.dismissSheet(); |
5:数据的同步
同步数据,控制界面刷新应该是新手最容易绕弯的地方了,其实只要仔细一点也不难,这里简单提供一种思路(并不一定适合你的项目).
1 2 3 4 5 6 7 8 | //商品列表 private ArrayList<GoodsItem> dataList; //分类列表 private ArrayList<GoodsItem> typeList; //已选择的商品 private SparseArray<GoodsItem> selectedList; //用于记录每个分组选择的数目 private SparseIntArray groupSelect; |
SparseArray这个类其实就是 HashMap< Integer,Object >
不过SparseArray既可以根据key查找Value,也可以根据位置查找value,性能比HashMap高,是官方推荐的替代类,
同样SparseIntArray 其实是HashMap< Integer,Integer> 的替代者。
Activity里实现了下面几个方法,用于数据统一管理
列表中显示的商品购买数量统一从activity获取,商品的加减统一调用Activity的方法然后notifiDatasetChanged,由于代码不少具体的还是看源码吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /** * Item代表商品的购买数量加一 * @param item * @param refreshGoodList 是否刷新商品list */ public void add(GoodsItem item, boolean refreshGoodList){ int groupCount = groupSelect.get(item.typeId); if (groupCount== 0 ){ groupSelect.append(item.typeId, 1 ); } else { groupSelect.append(item.typeId,++groupCount); } GoodsItem temp = selectedList.get(item.id); if (temp== null ){ item.count= 1 ; selectedList.append(item.id,item); } else { temp.count++; } update(refreshGoodList); } /** * Item商品的购买数量减一 * @param item * @param refreshGoodList 是否刷新商品list */ public void remove(GoodsItem item, boolean refreshGoodList){ int groupCount = groupSelect.get(item.typeId); if (groupCount== 1 ){ groupSelect.delete(item.typeId); } else if (groupCount> 1 ){ groupSelect.append(item.typeId,--groupCount); } GoodsItem temp = selectedList.get(item.id); if (temp!= null ){ if (temp.count< 2 ){ selectedList.remove(item.id); } else { item.count--; } } update(refreshGoodList); } /** * 刷新界面 总价、购买数量等 * @param refreshGoodList 是否刷新商品list */ private void update( boolean refreshGoodList){ ... } //根据商品id获取当前商品的采购数量 public int getSelectedItemCountById( int id){ GoodsItem temp = selectedList.get(id); if (temp== null ){ return 0 ; } return temp.count; } //根据类别Id获取属于当前类别的数量 public int getSelectedGroupCountByTypeId( int typeId){ return groupSelect.get(typeId); } |
具体逻辑还是看代码吧,也许有更简单的实现。。。
Demo下载地址,下载到的文件是个AS module,你可以在自己新建的工程中Import Module.
源码下载:Android仿外卖购物车功能
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
微信公众号搜索 “ 脚本之家 ” ,选择关注
程序猿的那些事、送书等活动等着你
相关文章
Android编程基于自定义View实现绚丽的圆形进度条功能示例
这篇文章主要介绍了Android编程基于自定义View实现绚丽的圆形进度条功能,结合实例形式详细分析了Android自定义view实现圆形进度条的具体步骤与相关操作技巧,需要的朋友可以参考下2017-01-01Android 自定义View之边缘凹凸的优惠券效果的开发过程
这篇文章主要介绍了Android 自定义View之边缘凹凸的优惠券效果,主要总结一下在自定义View的开发过程中需要注意的一些地方.需要的朋友可以参考下2018-03-03Android 使用 SharedPreferences 保存少量数据的实现代码
这篇文章主要介绍了Android 使用 SharedPreferences 保存少量数据的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2021-04-04Andriod Studio实现保存QQ密码功能(案例代码详解)
这篇文章主要介绍了Andriod Studio实现保存QQ密码功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-03-03
最新评论