首先,众所周知,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. 【PRML读书笔记-Chapter1-Introduction】1.6 Information Theory

    熵 给定一个离散变量,我们观察它的每一个取值所包含的信息量的大小,因此,我们用来表示信息量的大小,概率分布为.当p(x)=1时,说明这个事件一定会发生,因此,它带给我的信息为0.(因为一定会发生,毫无 ...

  2. Inkpad绘图原理浅析

    本文新版本已转移到开源中国,欢迎前往指导. Inkpad是一款非常优秀的iPad矢量绘图软件,保管你一看见就忘不了.我的感觉是“一览众山小”.“相见甚晚”,以至于我写的TouchVG就是“小巫见大巫” ...

  3. .NET中TextBox控件设置ReadOnly=true后台取不到值的解决方法

    在.NET 2.0 下,当页面上的某个TextBox 设置了属性ReadOnly="True"时,通过客户端脚本给其赋值后,在后台代码中访问其Text属性却无法获得该值.经过尝试, ...

  4. position 属性和 z-index 属性对页面节点层级影响的例子

    转:http://www.neoease.com/tutorials/z-index/ 不设 z-index 属性 单层节点 双层节点 多层节点

  5. java中getBytes方法可能使图片文件产生的问题

    InputStream is = new FileInputStream(fl); ImageInputStream iis = ImageIO.createImageInputStream(is); ...

  6. ASP.NET MVC数组模型绑定

    在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例: <input type="text" name ...

  7. HashMap的实现原理

    1.HashMap的数据结构 数组的特点是:寻址容易,插入和删除困难:而链表的特点是:寻址困难,插入和删除容易.那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的, ...

  8. Angular系列----AngularJS入门教程00:引导程序(转载)

    我们现在开始准备编写AngularJS应用——phonecat.这一步骤(步骤0),您将会熟悉重要的源代码文件,学习启动包含AngularJS种子项目的开发环境,并在浏览器端运行应用. 进入angul ...

  9. Python+Selenium进行UI自动化测试项目中,常用的小技巧3:写入excel表(python,xlsxwriter)

    我们在项目中可能用到excel表生成,下面的代码就是对excel表的操作: import xlsxwriter import datetime class write_excel(): def __i ...

  10. 购买SSL证书到部署网站遇到的若干问题

    作为一个菜鸟,对于SSL证书,我了解不多,只知道用了它网站更安全,所以这次使用SSL证书途中遇到了各方面的各种问题,到今天为止终于全部解决. 一.证书格式 前两天在那什么云上面买了个SSL证书,是Wo ...