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

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

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

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

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

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

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

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

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

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

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

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

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

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

首先,我们必须要了解:

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

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

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

做一个Demo,看下效果

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

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

一、创建布局文件

1、主布局

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/listView"
/>
</LinearLayout>

main.xml

2、列表项布局

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:descendantFocusability="blocksDescendants"
>
<TextView
android:id="@+id/item_product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_margin="10dp"
android:text="商品名称"
android:textColor="#000"
/>
<ImageButton
android:id="@+id/item_btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0000"
android:src="@drawable/add"
android:layout_below="@+id/item_product_name"
android:layout_marginLeft="10dp"
/>
<TextView
android:id="@+id/item_product_num"
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textColor="#000"
android:layout_margin="5dp"
android:layout_toRightOf="@id/item_btn_add"
android:layout_below="@id/item_product_name"
/>
<ImageButton
android:id="@+id/item_btn_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0000"
android:src="@drawable/sub"
android:layout_below="@id/item_product_name"
android:layout_toRightOf="@id/item_product_num"
/>
<TextView
android:id="@+id/item_product_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_margin="10dp"
android:text=""
android:textColor="#000"
android:layout_alignParentRight="true"
/> </RelativeLayout>

item_cart.xml

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

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

              android:descendantFocusability="blocksDescendants"

二、创建实体类

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

 package com.xqx.ShopDemo;

 /**
* 购物车实体类
* 测试
*/
public class Product {
//商品名称
private String name;
// 商品数量
private int num;
// 该商品总价
private int price; @Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", num=" + num +
", price=" + price +
'}';
} public void setName(String name) {
this.name = name;
} public void setNum(int num) {
this.num = num;
} public void setPrice(int price) {
this.price = price;
} public String getName() {
return name;
} public int getNum() {
return num;
} public int getPrice() {
return price;
}
}

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 = ;
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;
}

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

适配器完整代码:

 import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView; import java.util.List; /**
* 购物车功能
* 适配器
*/
public class ShopAdapter extends BaseAdapter{ //集合 ,存放ListView的商品实体类数据
private List<Product> products;
//上下文
private Context context; //第一步,设置接口
private View.OnClickListener onAddNum;
private View.OnClickListener onSubNum; //第二步,设置接口方法
public void setOnAddNum(View.OnClickListener onAddNum){
this.onAddNum = onAddNum;
} public void setOnSubNum(View.OnClickListener onSubNum){
this.onSubNum = onSubNum;
}
public ShopAdapter(List<Product> products, Context context) {
this.products = products;
this.context = context;
} @Override
public int getCount() {
int ret = ;
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;
} @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;
}
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; }
}

适配器代码

四、主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 = ; i < ; i++) {
product = new Product();
product.setName("商品:"+i+":单价:"+i);
product.setNum();
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>) {
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中观察者模式的应用 了。

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

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

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

  1. [译]:Xamarin.Android平台功能——位置服务

    返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...

  2. Android表情功能

    Android表情功能 标签(空格分隔): 未分类 转载自:android edittext插入表情(基于socket方式),并对文中不正确的内容进行整理和修正 [TOC] 涉及知识点: Androi ...

  3. 【JSP】Cookie的使用及保存中文,并用Cookie实现购物车功能

    Cookie是服务器存放在客户端的一些数据,比如密码,以及你曾经访问过的一些数据. 设置Cookie //设置cookie Cookie cookie = new Cookie("TOM&q ...

  4. 给destoon商城的列表中和首页添加购物车功能

    如何给destoon商城的列表中和首页添加购物车功能? 目前加入购物车的功能只存在商城的详细页面里,有时候我们需要批量购买的时候,希望在列表页就能够使用这个加入购物车的功能. 修改步骤见下: 例如在商 ...

  5. ASP.NET之电子商务系统开发-2(购物车功能)

    一.前言 继上次的首页数据列表后,这是第二篇.记录一下购物车这个比较庞大的功能,可能实现的方法跟其他人有点不一样,不过原理都差不多,是将cookie存数据库里面的. 二.开始 首先看一下购物车流程及对 ...

  6. Cocos2d-x使用android拍照功能加载照片内存过大,通过另存照片尺寸大小解决

    使用2dx调用android拍照功能,拍照结束后在2dx界面显示拍照照片,如果不对照片做处理,会出现内存过大的问题,导致程序崩溃,如果仅仅另存拍照照片,则照片质量大小均下降,导致照片不够清晰,后来发现 ...

  7. jQuery 复制节点的元素实现加入到购物车功能

    描写叙述: 用户点击左边div中的商品,相应商品会自己主动加入到右面的div中,类似电子商城中的加入到购物车功能. 主要用到了jquery中的复制节点功能,基本原理是首先获取点击的元素,然后将对应信息 ...

  8. Android定位功能

    不说废话,直接说说实现android定位有关的API吧. 这些API都在android.location包下,一共有三个接口和八个类.它们配合使用即可实现定位功能. 三个接口: GpsStatus.L ...

  9. Android定位功能(二)

    在前文Android定位功能(一)中,已经大致介绍了一下在Android平台中,和定位功能相关的类,并举例获取了位置信息.但是前文是基于Criteria定制了一个标准,通过getBestProvide ...

随机推荐

  1. Android应用中使用及实现系统“分享”接口

    为了应用的推广.传播,很多的应用中都有“分享”功能,一个按钮,点击后会出现短信.微博等等一切实现了分享功能的应用列表.这一篇文章主要介绍怎么调用分享功能和怎么实现分享接口让自己应用出现分享列表中.An ...

  2. linux2.6.24内核源代码分析(1)——扒一扒sk_buff

    最近研究了linux内核的网络子系统上的网络分组的接收与发送的流程,发现这个叫sk_buff的东西无处不在,内核利用了这个结构来管理分组,在各个层中传递这个结构,因此sk_buff可以说是linux内 ...

  3. ruby -- 基础学习(六)时间计算

    计算下一天的这个时刻, # 比如"2013-8-16 18:45:12" 的下一天的这个时刻 “2013-8-17 18:45:12” Time.now + 1.day 如果想得到 ...

  4. 后端码农谈前端(HTML篇)第二课:常见元素

    一.根元素 <doctype> 定义文档类型. <html> 定义 HTML 文档. 二.元数据元素 <head> 定义关于文档的信息. <meta> ...

  5. mysql按汉语拼音首字母排序

      如果数据表table的某字段name的字符编码是utf8_general_ci :   SELECT *  FROM `table` ORDER BY convert(name USING gbk ...

  6. 在eclipse中配置python插件

    最好离线下载python的离线包.名字叫——org.python.pydev.feature-1.6.3.2010100513 此包里面有两个文件夹 features 和 plugins,分别把2包中 ...

  7. 使用Eclipse调试PHP程序

    我安装的是PHP Version 5.3.26,按照网上提示在Eclipse中使用XDebug进行调试,不过配置了却使用不了,下面把解决方法简要说一下. XDebug老是加载不了 From PHP 5 ...

  8. SQL Server里强制参数化的痛苦

    几天前,我写了篇SQL Server里简单参数化的痛苦.今天我想继续这个话题,谈下SQL Server里强制参数化(Forced Parameterization). 强制参数化(Forced Par ...

  9. 《Out of control》阅读笔记(一)

    Out Of Control 说实话,当初买这本书起源于知乎诸位学问人的推荐,脑子一热就买了.为了不浪费这几十块钱,细致了看完了前三章,买来一看才发现原来这本书居然跟计算机有很深刻的关系.其实更准确地 ...

  10. C#类的继承相关总结

    1.子类继承父类,会拥有父类中所规范的所有成员,但是只能是使用其中的公共成员 2.实现了继承,可以做到代码的冗余,做到代码的重用 3.实现了继承,可以方便代码的扩展与修改 4,当子类拥有与父类相同签名 ...