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内添加
    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, View header, int itemPosition, 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,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显示动画

 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, 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:底部弹出购物车清单

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

bottomsheet

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

集成
compile 'com.flipboard:bottomsheet-core:1.5.1'
使用
xml中使用BottomSheetLayout包裹弹出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>

代码中使用很简单

 //弹出View bottomSheet即是要弹出的view
 bottomSheetLayout.showWithSheetView(bottomSheet);

 //代码隐藏view (点击弹出view以外的地方可以隐藏弹出的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仿外卖购物车功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android实现仿淘宝购物车增加和减少商品数量功能demo示例

    本文实例讲述了Android实现仿淘宝购物车增加和减少商品数量功能.分享给大家供大家参考,具体如下: 在前面一篇<Android实现的仿淘宝购物车demo示例>中,小编简单的介绍了如何使用listview来实现购物车,但是仅仅是简单的实现了列表的功能,随之而来一个新的问题,买商品的时候,我们可能不止想买一件商品,想买多个,或许有因为某种原因点错了,本来想买一件来着,小手不小心抖了一下,把数量错点成了三个,这个时候就涉及到一个新的功能,那就是增加和减少商品的数量,今天这篇博文,小编就来和小伙伴们

  • Android实现二级列表购物车功能

    本文实例为大家分享了Android实现二级列表购物车功能的具体代码,供大家参考,具体内容如下 MainActivity: package com.baway.twoshopcar; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.CheckBox; import android.widget.Expa

  • Android实现购物车添加物品的动画效果

    前言:当我们写商城类的项目的时候,一般都会有加入购物车的功能,加入购物车的时候会有一些抛物线动画,最近做到这个功能,借助别人的demo写了一个. 效果: 开发环境:AndroidStudio2.1.2+gradle-2.10 涉及知识:1.沉浸式状态栏,2.单位精度计算(价格),3.List之Iterator. 部分代码: public class MainActivity extends AppCompatActivity implements FoodAdapter.FoodActionCa

  • Android制作简单的普通购物车

    本文实例为大家分享了Android普通购物车制作过程,供大家参考,具体内容如下 1.最新项目新增了类似购物车功能,如下图所示: 当时刚看到此页面的时候,第一反应是利用 ListView嵌套Listview,经过一番操作最终也实现了此功能.当时也没有考虑性能问题,只考虑能写出来.后来嵌套数据,当数据量较大时,滑动Listview可以明显感觉到卡顿,这对用户来说是很难忍受的,所以才有了找到替代方案的想法,看到网上主流的是用ExpandableListView来实现此功能,所以我也用此方案来写一下.

  • Android中实现淘宝购物车RecyclerView或LIstView的嵌套选择的逻辑

    使用了RecyclerView嵌套RecyclerView的方案. 购物车的第一个界面为RecyclerView,每个Item里面包含一个店铺.在Item中使用RecyclerView包含店铺和店铺的多个商品. 实现思路: 使用接口回调将第二个adapter的商品选择的监听事件回调给第一个adapter后再在第一个adapter中回调给MainActivity. 使用接口回调将第一个adapter的商品选择的监听事件回调给MainActivity. 在MainActivity中处理第一个adap

  • Android实现购物车功能

    最近看了一些淘宝购物车的demo,于是也写了一个. 效果图如下: 主要代码如下: actvity中的代码: public class ShoppingCartActivity extends BaseActivity { private List<Test> data; private ListView mListView; private ShoppingCartAdapter adapter; private RelativeLayout rlRefresh; private TextVi

  • Android实现购物车及其他功能的角标

    1.先来张效果图 2.自定义一个角标工具类BottomBarView . ** * Created by Administrator on 2016/12/27. * 角标工具类 */ public class BottomBarView extends RelativeLayout { private Context context; private TextView bar_num; private int count = 0; public BottomBarView(Context co

  • Android仿饿了么加入购物车旋转控件自带闪转腾挪动画的按钮效果(实例详解)

    概述 在上文,酷炫Path动画已经预告了,今天给大家带来的是利用 纯自定义View,实现的仿饿了么加入购物车控件,自带闪转腾挪动画的按钮. 效果图如下: 图1 项目中使用的效果,考虑到了View的回收复用, 并且可以看到在RecyclerView中使用,切换LayoutManager也是没有问题的, 图2 Demo效果,测试各种属性值 注意,本控件非继承自ViewGroup,而是纯自定义View实现.理由如下: 1 减少布局层级,从而提高性能 2 文字和图形纯draw,用到什么draw什么,没有

  • Android把商品添加到购物车的动画效果(贝塞尔曲线)

    当我们写商城类的项目的时候,一般都会有加入购物车的功能,加入购物车的时候会有一些抛物线动画,具体代码如下: 实现效果如图: 思路: 确定动画的起终点 在起终点之间使用二次贝塞尔曲线填充起终点之间的点的轨迹 设置属性动画,ValueAnimator插值器,获取中间点的坐标 将执行动画的控件的x.y坐标设为上面得到的中间点坐标 开启属性动画 当动画结束时的操作 难点: PathMeasure的使用 - getLength() - boolean getPosTan(float distance, f

  • Android实现的仿淘宝购物车demo示例

    本文实例讲述了Android实现的仿淘宝购物车.分享给大家供大家参考,具体如下: 夏的热情渐渐退去,秋如期而至,丰收的季节,小编继续着实习之路,走着走着,就走到了购物车,逛过淘宝或者是京东的小伙伴都知道购物车里面的宝贝可不止一件,对于爱购物的姑娘来说,购物车里面的商品恐怕是爆满,添加不进去了,以前逛淘宝的时候,小编没有想过要怎么样实现购物车,就知道在哪儿一个劲儿的逛,但是现在不一样了,小编做为一个开发者,想的就是该如何实现,捣鼓了两天的时间,用listview来实现,已经有模有样了,现在小编就来

随机推荐