Android仿外卖购物车功能

Android仿外卖购物车功能,第1张

概述先看看效果图:知识点分析效果图来看不复杂内容并没多少,值得介绍一下的知识点也就下面几个吧

先看看效果图:

知识点分析

效果图来看不复杂内容并没多少,值得介绍一下的知识点也就下面几个吧
- 列表标题悬停
- 左右列表滑动时联动
- 添加商品时的抛物线动画
- 底部d出购物车清单
- 数据的同步

另外就是实现效果的时候可能会遇到的几个坑。。。

布局很简单直接进入代码

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内添加
    compile 'se.emilsjolander:stickyListheaders:2.7.0'

2)、xml文件中使用StickyListheadersListVIEw代替ListVIEw

 <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接口包括两个方法

 VIEw getheaderVIEw(int position,VIEw convertVIEw,VIEwGroup parent); long getheaderID(int position);

代码中使用和ListVIEw一样,下面是几个特有的方法,看方法名也很容易理解用途

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,long headerID);}public VIEw getListChildAt(int index);public int getListChildCount();

2:左右列表联动

联动主要有两个效果
- 左侧列表点击选择分类,右侧列表滑动到对应分类
- 右侧列表滑动过程中左侧列表高亮的分类跟随变化

第一个效果简单,左侧列表item添加点击事件,事件中调用右侧列表的setSelection(int positon) 方法。

第二个效果要给右侧列表添加ScrollListener,根据列表中显示的第一条数据设置左侧选中的分类

 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完成,也可以代码设置,不过有个小坑要注意一下 旋转动画一定要在平移动画前面,否则就不是滚动平移了,而是乱跳。。。

这里贴一下动画的代码设置方法

 

 //显示减号的动画 private Animation getShowAnimation(){  AnimationSet set = new AnimationSet(true);  RotateAnimation rotate = new RotateAnimation(0,720,RotateAnimation.relative_TO_SELF,0.5f,0.5f);  set.addAnimation(rotate);  TranslateAnimation translate = new TranslateAnimation(    TranslateAnimation.relative_TO_SELF,2f,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,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显示动画

 int[] loc = new int[2]; v.getLocationInWindow(loc); activity.playAnimation(loc);

2、创建动画的控件并添加到根部局并在动画结束后移除动画vIEw

 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);  //线性插值器 默认就是线性  translationX.setInterpolator(new linearInterpolator());  Animation translationY = new TranslateAnimation(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:底部d出购物车清单

底部d出的效果大家一定都很熟悉了,几回每个项目中都会用的到,官方没有提供简单的控件实现,一般都需要自己写,不过要做到简单流畅,便于移植推荐使用第三方库,这里向大家推荐一个

bottomsheet

集成简单,效果多样这里简单介绍一下使用方法

集成
compile 'com.flipboard:bottomsheet-core:1.5.1'
使用
xml中使用BottomSheetLayout包裹d出vIEw时候的背景布局,BottomSheetLayout继承自帧布局:

<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>

代码中使用很简单

 //d出VIEw bottomSheet即是要d出的vIEw bottomSheetLayout.showWithSheetVIEw(bottomSheet); //代码隐藏vIEw (点击d出vIEw以外的地方可以隐藏d出的vIEw,向下滑动也可以) bottomSheetLayout.dismissSheet();

5:数据的同步

同步数据,控制界面刷新应该是新手最容易绕弯的地方了,其实只要仔细一点也不难,这里简单提供一种思路(并不一定适合你的项目).

 //商品列表 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,由于代码不少具体的还是看源码吧

 /**  * 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仿外卖购物车功能全部内容,希望文章能够帮你解决Android仿外卖购物车功能所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1149198.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存