Android 购物车功能的实现

首先,众所周知,ListView是Android最常用的控件,可以说是最简单的控件,也可以说是最复杂的控件。

作为一个Android初级开发者,可能会简单的ListView展示图文信息。

作为一个有一定项目开发经验的Android开发者来说,可能会遇到ListView的列表项中存在各种按钮的需求。

需求最多的就是购物车功能。想必大家都用过某宝某东客户端APP吧 ,就是那个购物车的功能。

-------------------------------------------------------------------------------------------------------------

曾经做过购物车功能,今天项目需求也用到了差不多效果的购物车功能,刚好园友问了这个问题,便帮忙解答了。

之后,想了想还是写一下关于购物车效果的博客吧。

--------------------------------------------------------------------------------------------------------------

那么现在就学习一下购物车功能的实现原理

首先让我们分析下实现购物车功能需要解决的问题:

1、在哪里处理按钮的点击响应事件,是适配器 还是 Activity或者Fragment

2、如何知道你点击的按钮是哪一个列表项中的

3、点击某个按钮的时候,如果列表项所需的数据改变了,如何更新UI

4、列表项中存在会获取焦点的各种按钮,会导致列表项无法点击,只能点击按钮,这种情况怎么解决

首先,我们必须要了解:

1、自定义适配器,不会的看下博客:安卓开发_浅谈ListView(自定义适配器)

2、接口回调,不会接口回调的可以看下博客:Android接口回调机制

一个ListView数据展示的实现,必须要有的 自定义适配器,数据源,ListView,列表项布局

做一个Demo,看下效果

(1)、效果一,点击商品添加删除数量,后面的商品总价随之变化

(2)、效果二,一个列表项发生变化,滑出界面,在滑回来,该列表项的数据依然存在,列表项的复用不存在问题

一、创建布局文件

1、主布局

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3               android:orientation="vertical"
 4               android:layout_width="fill_parent"
 5               android:layout_height="fill_parent"
 6         >
 7     <ListView
 8             android:layout_width="fill_parent"
 9             android:layout_height="wrap_content"
10             android:id="@+id/listView"
11             />
12 </LinearLayout>

main.xml

2、列表项布局

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3               android:layout_width="match_parent"
 4               android:layout_height="match_parent"
 5               android:background="#fff"
 6               android:descendantFocusability="blocksDescendants"
 7         >
 8     <TextView
 9             android:id="@+id/item_product_name"
10             android:layout_width="wrap_content"
11             android:layout_height="wrap_content"
12             android:textSize="20sp"
13             android:layout_margin="10dp"
14             android:text="商品名称"
15             android:textColor="#000"
16             />
17     <ImageButton
18             android:id="@+id/item_btn_add"
19             android:layout_width="wrap_content"
20             android:layout_height="wrap_content"
21             android:background="#0000"
22             android:src="@drawable/add"
23             android:layout_below="@+id/item_product_name"
24             android:layout_marginLeft="10dp"
25             />
26     <TextView
27             android:id="@+id/item_product_num"
28             android:text="1"
29             android:layout_width="wrap_content"
30             android:layout_height="wrap_content"
31             android:textSize="25sp"
32             android:textColor="#000"
33             android:layout_margin="5dp"
34             android:layout_toRightOf="@id/item_btn_add"
35             android:layout_below="@id/item_product_name"
36             />
37     <ImageButton
38             android:id="@+id/item_btn_sub"
39             android:layout_width="wrap_content"
40             android:layout_height="wrap_content"
41             android:background="#0000"
42             android:src="@drawable/sub"
43             android:layout_below="@id/item_product_name"
44             android:layout_toRightOf="@id/item_product_num"
45             />
46     <TextView
47             android:id="@+id/item_product_price"
48             android:layout_width="wrap_content"
49             android:layout_height="wrap_content"
50             android:textSize="20sp"
51             android:layout_margin="10dp"
52             android:text="0"
53             android:textColor="#000"
54             android:layout_alignParentRight="true"
55             />
56
57 </RelativeLayout>

item_cart.xml

这里解决问题:列表项中存在会获取焦点的各种按钮,会导致列表项无法点击,只能点击按钮,这种情况怎么解决

解决方法,在item列表项布局的最外层父容器中 设置一个属性:

              android:descendantFocusability="blocksDescendants"

二、创建实体类

看上图,只需要三个属性,名称,总价格,数量

 1 package com.xqx.ShopDemo;
 2
 3 /**
 4  * 购物车实体类
 5  * 测试
 6  */
 7 public class Product {
 8     //商品名称
 9     private String name;
10     // 商品数量
11     private int num;
12     // 该商品总价
13     private int price;
14
15     @Override
16     public String toString() {
17         return "Product{" +
18                 "name=‘" + name + ‘\‘‘ +
19                 ", num=" + num +
20                 ", price=" + price +
21                 ‘}‘;
22     }
23
24     public void setName(String name) {
25         this.name = name;
26     }
27
28     public void setNum(int num) {
29         this.num = num;
30     }
31
32     public void setPrice(int price) {
33         this.price = price;
34     }
35
36     public String getName() {
37         return name;
38     }
39
40     public int getNum() {
41         return num;
42     }
43
44     public int getPrice() {
45         return price;
46     }
47 }

Product.java

三、创建适配器(关键!!)

1、创建适配器成员变量

   //集合 ,存放ListView的商品实体类数据
    private List<Product> products;
    //上下文
    private Context context;

    //第一步,设置接口
    private View.OnClickListener onAddNum;  //加商品数量接口
    private View.OnClickListener onSubNum;  //减商品数量接口

 接口看你具体需求,我这里是ImageButton ,所以是 View.OnClickListener

具体看情况,举三个列子,当然还有很多接口,比如单选按钮的

2、创建构造方法:

  public ShopAdapter(List<Product> products, Context context) {
        this.products = products;
        this.context = context;
    }

3、创建接口方法

    public void setOnAddNum(View.OnClickListener onAddNum){
        this.onAddNum = onAddNum;
    }

    public void setOnSubNum(View.OnClickListener onSubNum){
        this.onSubNum = onSubNum;
    }

4、重写自定义适配器的除了getView()的三个方法

@Override
    public int getCount() {
        int ret = 0;
        if (products != null) {
            ret = products.size();
        }
        return ret;
    }

    @Override
    public Object getItem(int i) {
        return products.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

5、接下来就是重点了

定义内部类

private static class ViewHolder{
        //商品名称,数量,总价
        private TextView item_product_name;
        private TextView item_product_num;
        private TextView item_product_price;
        //增减商品数量按钮
        private ImageButton item_btn_add;
        private ImageButton item_btn_sub;

    }

重写最重要的getView()方法,主要看红色颜色部分

@Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        View v = null;
        if (view != null) {
            v = view;
        }else{
            v = LayoutInflater.from(context).inflate(R.layout.item_cart,viewGroup,false);
        }

        ViewHolder holder = (ViewHolder) v.getTag();
        if (holder == null) {
            holder = new ViewHolder();
            holder.item_product_name = (TextView) v.findViewById(R.id.item_product_name);
            holder.item_product_num = (TextView) v.findViewById(R.id.item_product_num);
            holder.item_product_price = (TextView) v.findViewById(R.id.item_product_price);

            //设置接口回调,注意参数不是上下文,它需要ListView所在的Activity或者Fragment处理接口回调方法
            holder.item_btn_add = (ImageButton) v.findViewById(R.id.item_btn_add);
            holder.item_btn_add.setOnClickListener(onAddNum);

            holder.item_btn_sub = (ImageButton) v.findViewById(R.id.item_btn_sub);
            holder.item_btn_sub.setOnClickListener(onSubNum);

        }

        holder.item_product_name.setText(products.get(i).getName());
        holder.item_product_num.setText(products.get(i).getNum()+"");
        holder.item_product_price.setText(products.get(i).getPrice() + "");
      
        //设置Tag,用于判断用户当前点击的哪一个列表项的按钮,解决问题:如何知道你点击的按钮是哪一个列表项中的
        holder.item_btn_add.setTag(i);
        holder.item_btn_sub.setTag(i);

        v.setTag(holder);
        return v;
    }

至此,自定义适配器部分完成了。

适配器完整代码:

  1 import android.content.Context;
  2 import android.view.LayoutInflater;
  3 import android.view.View;
  4 import android.view.ViewGroup;
  5 import android.widget.BaseAdapter;
  6 import android.widget.ImageButton;
  7 import android.widget.ImageView;
  8 import android.widget.TextView;
  9
 10 import java.util.List;
 11
 12 /**
 13  * 购物车功能
 14  * 适配器
 15  */
 16 public class ShopAdapter extends BaseAdapter{
 17
 18     //集合 ,存放ListView的商品实体类数据
 19     private List<Product> products;
 20     //上下文
 21     private Context context;
 22
 23     //第一步,设置接口
 24     private View.OnClickListener onAddNum;
 25     private View.OnClickListener onSubNum;
 26
 27     //第二步,设置接口方法
 28     public void setOnAddNum(View.OnClickListener onAddNum){
 29         this.onAddNum = onAddNum;
 30     }
 31
 32     public void setOnSubNum(View.OnClickListener onSubNum){
 33         this.onSubNum = onSubNum;
 34     }
 35     public ShopAdapter(List<Product> products, Context context) {
 36         this.products = products;
 37         this.context = context;
 38     }
 39
 40     @Override
 41     public int getCount() {
 42         int ret = 0;
 43         if (products != null) {
 44             ret = products.size();
 45         }
 46         return ret;
 47     }
 48
 49     @Override
 50     public Object getItem(int i) {
 51         return products.get(i);
 52     }
 53
 54     @Override
 55     public long getItemId(int i) {
 56         return i;
 57     }
 58
 59     @Override
 60     public View getView(int i, View view, ViewGroup viewGroup) {
 61         View v = null;
 62         if (view != null) {
 63             v = view;
 64         }else{
 65             v = LayoutInflater.from(context).inflate(R.layout.item_cart,viewGroup,false);
 66         }
 67
 68         ViewHolder holder = (ViewHolder) v.getTag();
 69         if (holder == null) {
 70             holder = new ViewHolder();
 71             holder.item_product_name = (TextView) v.findViewById(R.id.item_product_name);
 72             holder.item_product_num = (TextView) v.findViewById(R.id.item_product_num);
 73             holder.item_product_price = (TextView) v.findViewById(R.id.item_product_price);
 74
 75             //第三步,设置接口回调,注意参数不是上下文,它需要ListView所在的Activity或者Fragment处理接口回调方法
 76             holder.item_btn_add = (ImageButton) v.findViewById(R.id.item_btn_add);
 77             holder.item_btn_add.setOnClickListener(onAddNum);
 78
 79             holder.item_btn_sub = (ImageButton) v.findViewById(R.id.item_btn_sub);
 80             holder.item_btn_sub.setOnClickListener(onSubNum);
 81
 82         }
 83
 84         holder.item_product_name.setText(products.get(i).getName());
 85         holder.item_product_num.setText(products.get(i).getNum()+"");
 86         holder.item_product_price.setText(products.get(i).getPrice() + "");
 87
 88         //第四步,设置Tag,用于判断用户当前点击的哪一个列表项的按钮
 89         holder.item_btn_add.setTag(i);
 90         holder.item_btn_sub.setTag(i);
 91
 92         v.setTag(holder);
 93         return v;
 94     }
 95     private static class ViewHolder{
 96         //商品名称,数量,总价
 97         private TextView item_product_name;
 98         private TextView item_product_num;
 99         private TextView item_product_price;
100         //增减商品数量按钮
101         private ImageButton item_btn_add;
102         private ImageButton item_btn_sub;
103
104     }
105 }

适配器代码

四、主Activity

public class MainActivity extends Activity implements View.OnClickListener, AdapterView.OnItemClickListener {

    private List<Product> datas; //数据源
    private ShopAdapter adapter; //自定义适配器
    private ListView listView;   //ListView控件
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        listView = (ListView) findViewById(R.id.listView);

        // 模拟数据
        datas = new ArrayList<Product>();
        Product product = null;
        for (int i = 0; i < 30; i++) {
            product = new Product();
            product.setName("商品:"+i+":单价:"+i);
            product.setNum(1);
            product.setPrice(i);
            datas.add(product);
        }
        adapter = new ShopAdapter(datas,this);
        listView.setAdapter(adapter);

        //以上就是我们常用的自定义适配器ListView展示数据的方法了
//解决问题:在哪里处理按钮的点击响应事件,是适配器 还是 Activity或者Fragment,这里是在Activity本身处理接口
        //执行添加商品数量,减少商品数量的按钮点击事件接口回调
        adapter.setOnAddNum(this);
        adapter.setOnSubNum(this);
        listView.setOnItemClickListener(this);
    }
//


3、点击某个按钮的时候,如果列表项所需的数据改变了,如何更新UI

    @Override
    public void onClick(View view) {
        Object tag = view.getTag();
        switch (view.getId()){
            case R.id.item_btn_add: //点击添加数量按钮,执行相应的处理
                // 获取 Adapter 中设置的 Tag
                if (tag != null && tag instanceof Integer) { //解决问题:如何知道你点击的按钮是哪一个列表项中的,通过Tag的position
                    int position = (Integer) tag;
                    //更改集合的数据
                    int num = datas.get(position).getNum();
                    num++;
                    datas.get(position).setNum(num); //修改集合中商品数量
                    datas.get(position).setPrice(position*num); //修改集合中该商品总价 数量*单价            //解决问题:点击某个按钮的时候,如果列表项所需的数据改变了,如何更新UI
                    adapter.notifyDataSetChanged();
                }
                break;
            case R.id.item_btn_sub: //点击减少数量按钮 ,执行相应的处理
                // 获取 Adapter 中设置的 Tag
                if (tag != null && tag instanceof Integer) {
                    int position = (Integer) tag;
                    //更改集合的数据
                    int num = datas.get(position).getNum();
                    if (num>0) {
                        num--;
                        datas.get(position).setNum(num); //修改集合中商品数量
                        datas.get(position).setPrice(position * num); //修改集合中该商品总价 数量*单价
                        adapter.notifyDataSetChanged();
                    }
                }
                break;
        }
    }
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
        Toast.makeText(MainActivity.this,"点击了第"+i+"个列表项",Toast.LENGTH_SHORT).show();
    }
}

----------------------------------------------------------------------------------------------------

总结下:

1、有人说列表项中最好不要用ImageButton,而尽可能的用ImageView替代,目前没有发现使用ImageButton会发生什么错误

2、有人说列表项中 解决焦点问题需要两步:

(1)、最外层父容器需要加属性:

android:descendantFocusability="blocksDescendants"

(2)、能获取焦点的控件,Button,ImageButton等等  需要 有属性:android:focusable="false"

但是我实际测试 发现子空间不需要设置focusable属性也不会产生问题,当然加上也没有问题

3、没有做过列表项中存在EditText控件的情况,可能会有焦点冲突。毕竟购物车中加一个编辑框也很少见

最后,一个实际的购物车,当然还需要显示当前的总金额,包含“去结算”按钮的功能的那一个框,这不属于ListView

如图:

那么怎么处理当你操作列表项中的按钮,不仅列表项中的数据发生变哈,而且不属于列表项的下面部分的“合计”数据也发生变化呢,

这就要学习Adapter中观察者模式的应用 了。

---------------------------------------------------------------------------------------------

以上内容,如有错误,欢迎指出!

时间: 2024-10-13 09:25:48

Android 购物车功能的实现的相关文章

Android倒计时功能的实现(CountDownTimer)

以前编程的时候,遇到倒计时的功能时,经常自己去写,但其实Android已经帮封装好了一个倒计时类CountDownTimer,其实是将后台线程的创建和Handler队列封装成为了一个方便的类调用. 说明: CountDownTimer timer = new CountDownTimer(30000, 1000)中,第一个参数表示总时间,第二个参数表示间隔时间. 意思就是每隔一秒会回调一次方法onTick,然后30秒之后会回调onFinish方法. package com.androidcoun

简单3步 js使用cookie实现的购物车功能[原创]

引入JQuery.js支持 加入JQuery.Cookie.js,代码如下 1 jQuery.cookie = function(name, value, options) { 2 if (typeof value != 'undefined') { // name and value given, set cookie 3 options = options || {}; 4 if (value === null) { 5 value = ''; 6 options.expires = -1;

jQuery实现一个简单的购物车功能

最近由于工作需要的原因,开始系统学习jQuery的知识,然后跟着一个视频教程做了一个购物车的功能,现总结如下. 第一步:准备HTML页面,代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.

QtAndroid详解(3):startActivity实战Android拍照功能

在"QtAndroid详解(1):QAndroidJniObject"中,我们介绍了 QAndroidJniObject 这个 Qt JNI 的核心类,在""中我们介绍了 startActivity 以及与它配套的一些 Android 背景知识,这次我们来看一个实例,演示如何使用 startActivity 来调用 Android 系统功能,同时也演示 QAndroidJniObject 的常见用法. 实例介绍 先看下实例效果,然后再论. 我们只是演示 API 用法

android购物车遇到的问题

最近 做购物车的时候 ,遇到几个问题,现在 总结如下: 1:不让listview复用组件(购物车,或者有特殊操作的时候): 自己保存所有的view对象 public View getView(final int position, View convertView, ViewGroup parent) { final DaydayCouponBean bean = list.get(position); <span style="color:#ff0000;">View v

session实现购物车功能

购物车功能是生活中经常使用的,这里以fruit表为例,做一个简单的购物车: 首先要将fruit表显示出来: <?php session_start();?> <table width="100%" border="1" cellpadding="0" cellspacing="0"> <tr> <td>代号</td> <td>名称</td> &

Delphi xe7 up1 调用android振动功能

Delphi xe7 up1 调用android振动功能 振动用到以下4个单元: Androidapi.JNI.App,Androidapi.JNIBridge,Androidapi.JNI.Os,Androidapi.Helpers 使用方法: var LVibrator: JVibrator; begin LVibrator := TJVibrator.Wrap ((SharedActivity.getSystemService(TJActivity.JavaClass.VIBRATOR_S

Android -- 分享功能和打开指定程序

打开指定程序                                                                                Intent intent = new Intent(); ComponentName cmp = new ComponentName("com.sina.weibo","com.sina.weibo.EditActivity"); intent.setAction(Intent.ACTION_M

Android 照相功能

使用内置的Camera 应用程序捕获图像            探索Android 所提供的内置功能,内置的图像捕获与存储功能为Android 上所有媒体功能提供了一个很好的切入点,为我们在以后的章节中处理音频和视频奠定了基础.            现在介绍如何利用内置的Camera(摄像头)应用程序.    package com.example; import java.io.File; import android.app.Activity; import android.content