以上是效果图
主要实现的功能: 二级列表的展示 ExpandableListView (加减数量的 功能使用自定义View组合控件实现)
实现复选框的联动,底部的全选,反选, 列表内,分组与成员之间的联动
请求数据使用MVP+OkHttp3实现,在上一篇博客里已有代码,这里就不上传了,
个人做的时候是用到什么方法定义什么方法的,顺着功能实现的步骤写的
接下来是具体实现过程,以及逻辑,具体的注释在代码里以经标明,这里就是大概的过程
///、首先
在清单文件中添加权限, 这里用到了网络权限
<uses-permission android:name="android.permission.INTERNET"/>
//要导入的依赖
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.android.support:design:28.0.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.github.bumptech.glide:glide:4.8.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
//接下来是布局文件,以及自定义View
//主页面布局 - 二级列表 组布局 子布局 - 自定义Veiw 布局 ,自定义View 布局添加在子布局中
//主页面布局--activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/Lin"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ccc"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<CheckBox
android:id="@+id/CheckAll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="全选"/>
</LinearLayout>
<TextView
android:id="@+id/Price_Sum"
android:layout_width="wrap_content"
android:layout_weight="1"
android:textSize="24sp"
android:layout_height="wrap_content"
android:text="总价"/>
<TextView
android:id="@+id/Close_Sum"
android:layout_width="wrap_content"
android:layout_weight="1"
android:textSize="24sp"
android:layout_height="wrap_content"
android:text="结算"/>
</LinearLayout>
<ExpandableListView
android:id="@+id/Expandable_View"
android:layout_above="@id/Lin"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ExpandableListView>
</RelativeLayout>
//二级列表布局 组布局与子布局
组布局 groupview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<CheckBox
android:id="@+id/Group_Box"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/Group_Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"/>
</LinearLayout>
子布局 childview.xml
<?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:paddingLeft="20dp">
<CheckBox
android:id="@+id/Child_Box"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/Child_Img"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/Child_Box"
android:src="@mipmap/ic_launcher"/>
<LinearLayout
android:id="@+id/lin"
android:layout_width="wrap_content"
android:layout_centerVertical="true"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/Child_Img"
android:orientation="vertical">
<TextView
android:id="@+id/Child_Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="名字"/>
<TextView
android:id="@+id/Child_Price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="价格"/>
</LinearLayout>
<com.example.shopdemo1.weight.Add_Delete_View
android:layout_alignParentRight="true"
android:id="@+id/Add_Delete_View"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</com.example.shopdemo1.weight.Add_Delete_View>
</RelativeLayout>
//自定义View布局以及Java代码
布局add_delete.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000"
android:layout_margin="3dp"
android:orientation="horizontal">
<TextView
android:id="@+id/Delete"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#fff"
android:layout_margin="1dp"
android:textSize="24sp"
android:gravity="center"
android:text="--"/>
<TextView
android:id="@+id/Number"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#fff"
android:layout_margin="1dp"
android:textSize="24sp"
android:gravity="center"
android:text="0"/>
<TextView
android:id="@+id/Add"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#fff"
android:layout_margin="1dp"
android:textSize="24sp"
android:gravity="center"
android:text="+"/>
</LinearLayout>
//Java代码 Add_Delete_View 这里要与后面的代码有联动所以有接口回调
package com.example.shopdemo1.weight;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.shopdemo1.R;
/*
* 自定义view组合控件 加+ 减-
* */
public class Add_Delete_View extends LinearLayout implements View.OnClickListener {
private TextView mAdd,mDelete,mNumber;
//初始化计数
private int counts;
public Add_Delete_View(Context context, AttributeSet attrs) {
super(context, attrs);
//加载布局
LayoutInflater.from(context).inflate(R.layout.add_delete,this);
//加载控件
initView();
}
private void initView() {
mAdd = findViewById(R.id.Add);
mDelete=findViewById(R.id.Delete);
mNumber=findViewById(R.id.Number);
//注册点击的监听事件
mAdd.setOnClickListener(this);
mDelete.setOnClickListener(this);
}
//先给初始化赋值
public void setNumber(int number) {
this.counts = number;
mNumber.setText(number + "");
}
//点击监听事件
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.Add:
//点击添加数量
counts++;
mNumber.setText( counts+"");
//通过接口设置值
if (monCountChange!=null){
monCountChange.setNumber(counts);
}
break;
case R.id.Delete:
//点击减少数量 判断数量是否为零
if (counts>0){
counts--;
mNumber.setText(counts+"");
//通过接口设置值
if (monCountChange!=null){
monCountChange.setNumber(counts);
}
}else{
Toast.makeText(getContext(),"请添加购买数量",Toast.LENGTH_SHORT).show();
}
break;
}
}
//因为在适配器和Activity中要用到加和减的功能,通过接口回调完成
//自定义接口
public interface OnCountChange{
//定义一个方法
void setNumber(int count);
}
//实例化
private OnCountChange monCountChange;
//方法
public void setOnChange(OnCountChange onChange){
this.monCountChange=onChange;
}
}
//接下来,要写的东西是网络请求工具类,以及Mvp模式的搭建,这里就不写了,在之前的博客里有,
//接下来是最重要的部分 二级列表的适配器 以及 MainActivtty
建议两个类结合起来看,明白适配器里的接口,在activity里的调用
我习惯从activity里来看,方便找到调用的方法,具体的功能以及写在了代码里
先是适配器,需要在里面先写好回调的接口,以及要调用的方法 在最下面
package com.example.shopdemo1.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.example.shopdemo1.R;
import com.example.shopdemo1.bean.GoodsBean;
import com.example.shopdemo1.weight.Add_Delete_View;
import java.util.ArrayList;
import java.util.List;
/*
* 适配器
* */
public class MyAdapter extends BaseExpandableListAdapter {
private Context mContext;
private ArrayList<GoodsBean.DataBean>mData;
public MyAdapter(Context mContext, ArrayList<GoodsBean.DataBean> mData) {
this.mContext = mContext;
this.mData = mData;
}
//组布局的数量
@Override
public int getGroupCount() {
return mData.size();
}
//子布局的数量
@Override
public int getChildrenCount(int groupPosition) {
return mData.get(groupPosition).getSpus().size();
}
@Override
public Object getGroup(int groupPosition) {
return null;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return null;
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
@Override
public boolean hasStableIds() {
return false;
}
//组布局的视图
@Override
public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
GroupHoudler groupHoudler=null;
if (convertView==null){
groupHoudler=new GroupHoudler();
convertView=View.inflate(mContext, R.layout.groupview,null);
groupHoudler.groupCheckbox=convertView.findViewById(R.id.Group_Box);
groupHoudler.groupName=convertView.findViewById(R.id.Group_Name);
convertView.setTag(groupHoudler);
}else{
groupHoudler= (GroupHoudler) convertView.getTag();
}
GoodsBean.DataBean dataBean = mData.get(groupPosition);
groupHoudler.groupName.setText(dataBean.getName()+"");
//通过方法判断子布局是否都选中
final boolean childCheckedAll =isChildAllCheck(groupPosition);
groupHoudler.groupCheckbox.setChecked(childCheckedAll);
//设置监听事件
groupHoudler.groupCheckbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (adaptercallback!=null){
adaptercallback.setGroupCheck(groupPosition);
}
}
});
return convertView;
}
//子布局的视图
@Override
public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ChildHoudle childHoudle=null;
if (convertView==null){
childHoudle=new ChildHoudle();
convertView=View.inflate(mContext, R.layout.childview,null);
childHoudle.childCheckbox=convertView.findViewById(R.id.Child_Box);
childHoudle.childImage=convertView.findViewById(R.id.Child_Img);
childHoudle.childName=convertView.findViewById(R.id.Child_Name);
childHoudle.childPrice=convertView.findViewById(R.id.Child_Price);
childHoudle.add_delete_view=convertView.findViewById(R.id.Add_Delete_View);
convertView.setTag(childHoudle);
}else{
childHoudle= (ChildHoudle) convertView.getTag();
}
GoodsBean.DataBean.SpusBean spusBean = mData.get(groupPosition).getSpus().get(childPosition);
childHoudle.childCheckbox.setChecked(spusBean.isChildChecked());
Glide.with(mContext).load(spusBean.getPic_url()).into(childHoudle.childImage);
childHoudle.childName.setText(spusBean.getName());
childHoudle.childPrice.setText("¥:"+spusBean.getSkus().get(0).getPrice());
//给自定义view赋值
childHoudle.add_delete_view.setNumber(spusBean.getPraise_num());
//复选框的联动
childHoudle.childCheckbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (adaptercallback!=null){
adaptercallback.setChlidCheck(groupPosition,childPosition);
}
}
});
//调用自定义View,动态改变数量
childHoudle.add_delete_view.setOnChange(new Add_Delete_View.OnCountChange() {
@Override
public void setNumber(int count) {
if (adaptercallback!=null){
adaptercallback.setNumber(groupPosition,childPosition,count);
}
}
});
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
//判断子布局是否全部被选中
public boolean isChildAllCheck(int groupPosition) {
//初始花一个boolean变量
boolean boo=true;
//获取每个子布局中的复选框的状态 首先获取到复选框所在的集合
GoodsBean.DataBean dataBean = mData.get(groupPosition);
List<GoodsBean.DataBean.SpusBean> spus = dataBean.getSpus();
//循环遍历
for (int i = 0; i < spus.size(); i++) {
//获取集合中的每一个元素
GoodsBean.DataBean.SpusBean spusBean = spus.get(i);
//判断复选框标识的状态 如果为选中状态跳过判断继续循环,如果未选中则返回false
if (!spusBean.isChildChecked()){
return false;
}
}
return boo;
}
//点击组复选框让旗下所有子布局选中
public void childAllCheck(int groupPosition, boolean isCheck) {
GoodsBean.DataBean dataBean = mData.get(groupPosition);
List<GoodsBean.DataBean.SpusBean> spus = dataBean.getSpus();
//循环遍历
for (int i = 0; i < spus.size(); i++) {
//获取集合中的每一个元素
GoodsBean.DataBean.SpusBean spusBean = spus.get(i);
//将子布局中的复选框选中
spusBean.setChildChecked(isCheck);
}
}
//查看子布局中的商品是否被选中
public boolean isChildAllChecked(int groupPosition, int childPosition) {
//获取当前的子集合 并返回当前的状态
GoodsBean.DataBean.SpusBean spusBean = mData.get(groupPosition).getSpus().get(childPosition);
if (spusBean.isChildChecked()){
return true;
}
return false;
}
//点击给他赋值 切换状态
public void setChildChecked(int groupPosition, int childPosition, boolean b) {
GoodsBean.DataBean.SpusBean spusBean = mData.get(groupPosition).getSpus().get(childPosition);
spusBean.setChildChecked(b);
}
//判断集合中的所有元素,如果都选中,则返回一个true,否则返回一个true
public boolean isAllGoods() {
//初始化一个变量
boolean boos=true;
//循环嵌套,遍历所有数据
for (int i = 0; i <mData.size() ; i++) {
GoodsBean.DataBean dataBean = mData.get(i);
for (int j = 0; j < dataBean.getSpus().size(); j++) {
GoodsBean.DataBean.SpusBean spusBean = dataBean.getSpus().get(j);
//只要有一个未选中就返回false
if (!spusBean.isChildChecked()){
boos=false;
}
}
}
return boos;
}
//全选反选
public void setAllGoodsIsChecked(boolean b) {
//给所有的元素复选框赋值
for (int i = 0; i <mData.size() ; i++) {
GoodsBean.DataBean dataBean = mData.get(i);
for (int j = 0; j < dataBean.getSpus().size(); j++) {
GoodsBean.DataBean.SpusBean spusBean = dataBean.getSpus().get(j);
//只要有一个未选中就返回false
spusBean.setChildChecked(b);
}
}
}
//设置数量以及价格
public void setShopnumber(int groupPosition, int childPosition, int number) {
GoodsBean.DataBean.SpusBean spusBean = mData.get(groupPosition).getSpus().get(childPosition);
spusBean.setPraise_num(number);
}
//获取选中的商品价格总价
public float getAllGoodsPrice() {
float allPrice = 0;
for (int i = 0; i < mData.size(); i++) {
GoodsBean.DataBean dataBean = mData.get(i);
for (int j = 0; j < dataBean.getSpus().size(); j++) {
GoodsBean.DataBean.SpusBean spusBean = dataBean.getSpus().get(j);
if (spusBean.isChildChecked()){
allPrice = allPrice + spusBean.getPraise_num() * Float.parseFloat(spusBean.getSkus().get(0).getPrice());
}
}
}
return allPrice;
}
//或者选中的商品数量
public int getAllGoodsNumber() {
int allNumber = 0;
for (int i = 0; i < mData.size(); i++) {
GoodsBean.DataBean dataBean = mData.get(i);
for (int j = 0; j < dataBean.getSpus().size(); j++) {
GoodsBean.DataBean.SpusBean spusBean = dataBean.getSpus().get(j);
if (spusBean.isChildChecked()){
allNumber = allNumber + spusBean.getPraise_num();
}
}
}
return allNumber;
}
class GroupHoudler{
private CheckBox groupCheckbox;
private TextView groupName;
}
class ChildHoudle{
private CheckBox childCheckbox;
private ImageView childImage;
private TextView childName;
private TextView childPrice;
private Add_Delete_View add_delete_view;
}
//定义接口,接口回调
public interface Adaptercallback{
void setGroupCheck(int groupPosition);
void setChlidCheck(int groupPosition,int childPosition);
void setNumber(int groupPosition, int childPosition, int number);
}
//实例化
private Adaptercallback adaptercallback;
//设置回调的数据
public void setCallback(Adaptercallback adaptercallback) {
this.adaptercallback = adaptercallback;
}
}
//Activity.这里调用P层,以及回调的接口,请求数据, 然后通过适配器调用适配器里的接口方法实现功能的联动
package com.example.shopdemo1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ExpandableListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.shopdemo1.adapter.MyAdapter;
import com.example.shopdemo1.bean.GoodsBean;
import com.example.shopdemo1.presenter.PresenterImpl;
import com.example.shopdemo1.view.Iview;
import java.util.ArrayList;
public class MainActivity<T> extends AppCompatActivity implements Iview<T> {
//网址
private String mUrl="http://www.wanandroid.com/tools/mockapi/6523/restaurant-list";
//集合
private ArrayList<GoodsBean.DataBean> mList=new ArrayList<>();
private CheckBox mCheckAll;
private ExpandableListView mExpand;
private TextView mPriceSum;
private TextView mCloseSum;
private PresenterImpl presenter;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//查找组件
initView();
//调用P层 请求数据
presenter = new PresenterImpl(this);
presenter.StartRequestX(mUrl);
adapter = new MyAdapter(this, mList);
mExpand.setAdapter(adapter);
//通过接口调用适配器的方法 完成复选框的联动,以及数值的变更
adapter.setCallback(new MyAdapter.Adaptercallback() {
//组布局 实现功能,点击复选框,该组中的商品全部选中,反之亦然,
@Override
public void setGroupCheck(int groupPosition) {
//1.判断复选框中的子布局是否全部被选中
boolean childAllCheck=adapter.isChildAllCheck(groupPosition);
//操作,点击组,让所有的子布局中的复选框都被选中
adapter.childAllCheck(groupPosition, !childAllCheck);
//刷新适配器
adapter.notifyDataSetChanged();
flushBottomLayout();
}
//子布局
@Override
public void setChlidCheck(int groupPosition, int childPosition) {
//1.获取当前商品的状态
boolean childChecked=adapter.isChildAllChecked(groupPosition,childPosition);
//点击操作,选中当前商品的布局
adapter.setChildChecked(groupPosition,childPosition,!childChecked);
adapter.notifyDataSetChanged();
flushBottomLayout();
}
//数值 设置数量 设置 价格
@Override
public void setNumber(int groupPosition, int childPosition, int number) {
adapter.setShopnumber(groupPosition,childPosition,number);
adapter.notifyDataSetChanged();
flushBottomLayout();
}
});
//全选,反选的点击事件
mCheckAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取当前状态
boolean allGoods = adapter.isAllGoods();
//重新赋值
adapter.setAllGoodsIsChecked(!allGoods);
//刷新适配器
adapter.notifyDataSetChanged();
flushBottomLayout();
}
});
}
//刷新底部数据
private void flushBottomLayout() {
boolean allGoods = adapter.isAllGoods();
mCheckAll.setChecked(allGoods);
//获取选中商品的价格
float allGoodsPrice = adapter.getAllGoodsPrice();
//获取选中商品的数量
int allGoodsNumber = adapter.getAllGoodsNumber();
mPriceSum.setText("价格:" + allGoodsPrice);
mCloseSum.setText("去结算(" + allGoodsNumber + ")");
}
private void initView() {
//复选框,二级列表,textview
mCheckAll = findViewById(R.id.CheckAll);
mExpand = findViewById(R.id.Expandable_View);
mPriceSum = findViewById(R.id.Price_Sum);
mCloseSum = findViewById(R.id.Close_Sum);
//去掉小三角
mExpand.setGroupIndicator(null);
}
//成功
@Override
public void success(T data) {
GoodsBean goodsBean= (GoodsBean) data;
//将网络请求到的数据添加到集合中
mList.addAll(goodsBean.getData());
adapter.notifyDataSetChanged();
}
//失败
@Override
public void error(T error) {
Toast.makeText(this,error+"",Toast.LENGTH_SHORT).show();
}
}