最近将appBase实战于各种项目中,也发现了不少问题,并优化了很多功能。今天带给大家一个实战–《购物车》。购物车,在商城app中是必不可少的一部分,也是使用的比较多的,这里简单的做一个效果。
先来看看效果图
1、创建项目
第一种、引用appBase项目即可
第二种、将appBase的jar文件copy到libs下
我用的第二种,如上图所示。
2、代码生成
通过代码生成器生成Activity、Presenter、Adapter
1、生成Activity(默认生成Presenter)
2、生成Adapter
3、网络请求数据
这里网络数据使用的是APICloud,那么就需要对于APICloudSDK进行配置。最新的appBase讲配置放了出来,只要在applcation中进行代码配置就可以了。
package com.example.shopcartdemo; import com.apicloud.sdk.APICloudSDK; import com.snicesoft.Application; import com.snicesoft.http.HttpReq; public class MyApplication extends Application { final String APP_ID = "A6960031839242"; final String APP_KEY = "3F248D5F-50DB-782A-F437-E13796238B9E"; @Override public void onCreate() { super.onCreate(); APICloudSDK.getInstance().init(APP_ID, APP_KEY); APICloudSDK.getInstance().init(hu()); HttpReq.debug = true; } }
这里添加了DialogPresenter,作用就是为了请求的时候对dialog的控制
DialogPresenter
package com.example.shopcartdemo.presenter; import android.app.ProgressDialog; import android.content.Context; import com.snicesoft.presenter.BasePresenter; import com.snicesoft.util.DialogUtil; public class DialogPresenter<C extends BasePresenter.Callback> extends BasePresenter<C> { public DialogPresenter(Context context) { super(context); progressDialog = DialogUtil.getProgressDialog(context); } ProgressDialog progressDialog; protected void showDialog(CharSequence message, boolean... flag) { if (flag != null) { if (flag.length > 0) progressDialog.setCancelable(flag[0]); if (flag.length > 1) progressDialog.setCanceledOnTouchOutside(flag[1]); } progressDialog.setMessage(message); if (!progressDialog.isShowing()) progressDialog.show(); } protected void closeDialog() { if (progressDialog.isShowing()) progressDialog.dismiss(); } }
MainPresenter
package com.example.shopcartdemo.presenter; import java.util.ArrayList; import java.util.List; import android.content.Context; import com.alibaba.fastjson.JSON; import com.apicloud.sdk.APICloudSDK; import com.example.shopcartdemo.adapter.ShopCartAdapter; import com.lidroid.xutils.exception.HttpException; import com.snicesoft.http.HttpCallback; import com.snicesoft.presenter.BasePresenter; import com.snicesoft.util.CommonUtils; public class MainPresenter extends DialogPresenter<MainPresenter.Callback> { public MainPresenter(Context context) { super(context); } public interface Callback extends BasePresenter.Callback { void setShopCartList(List<ShopCartAdapter.Data> list); } public static class ShopCart { String title; int price; int count; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } } public void getShopCartList() { showDialog("正在加载"); APICloudSDK .getInstance() .GET("/mcm/api/ShopCart?filter=%7B%22where%22%3A%7B%7D%2C%22skip%22%3A0%2C%22limit%22%3A20%7D", null, new HttpCallback() { @Override public void onSuccess(String result) { closeDialog(); List<ShopCart> array = JSON.parseArray(result, ShopCart.class); List<ShopCartAdapter.Data> list = new ArrayList<ShopCartAdapter.Data>(); for (ShopCart cart : array) { list.add(new ShopCartAdapter.Data( 0, cart.title, cart.price, "http://ck.haier.com/UpLoad/2015-05-15/a5e8cac4-2671-4aa0-83a7-66c64e051f95.jpg", cart.count)); } if (callback != null) callback.setShopCartList(list); } @Override public void onFailure(HttpException arg0) { closeDialog(); CommonUtils.showToast(getContext(), "请求失败,请稍后重试"); } }); } }
在这个类中,网络请求和网络解析实体对象都用内部类来定义,注意:内部类定义一定要用static class,否则fastjson无法正常解析,会导致无法反射创建对象。
4、数据绑定和交互
首先看下Activity
package com.example.shopcartdemo; import java.util.List; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.CheckBox; import android.widget.ListView; import android.widget.TextView; import com.example.shopcartdemo.adapter.ShopCartAdapter; import com.example.shopcartdemo.adapter.ShopCartAdapter.ViewCallback; import com.example.shopcartdemo.presenter.MainPresenter; import com.snicesoft.avlib.annotation.DataBind; import com.snicesoft.avlib.annotation.DataType; import com.snicesoft.avlib.annotation.Id; import com.snicesoft.avlib.annotation.Layout; import com.snicesoft.avlib.rule.IData; import com.snicesoft.avlib.rule.IHolder; import com.snicesoft.base.BaseActivity; @Layout(R.layout.activity_main) public class MainActivity extends BaseActivity<MainActivity.Holder, MainActivity.Data> implements MainPresenter.Callback, ViewCallback { class Holder extends IHolder { @Id(R.id.lvShopCard) ListView lvShopCard; @Id(R.id.cbAll) CheckBox cbAll; @Id(R.id.tvPrice) TextView tvPrice; @Override public void initViewParams() { cbAll.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { _data.shopCartAdapter.setAll(cbAll.isChecked()); tvPrice.setText("¥" + _data.shopCartAdapter.calc()); } }); } } class Data extends IData { @DataBind(id = R.id.lvShopCard, dataType = DataType.ADAPTER) ShopCartAdapter shopCartAdapter = new ShopCartAdapter(getBaseContext()); } @Override public Holder newHolder() { return new Holder(); } @Override public Data newData() { return new Data(); } MainPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter = new MainPresenter(this); presenter.setCallback(this); _data.shopCartAdapter.setCallback(this); } @Override protected void onResume() { super.onResume(); presenter.getShopCartList(); } @Override public void onClick(View v) { super.onClick(v); switch (v.getId()) { case R.id.btnGoPay: break; case R.id.btnDelete: break; default: break; } } boolean isNormal = true; @Override public void setShopCartList(List<ShopCartAdapter.Data> list) { _data.shopCartAdapter.setDataList(list); refreshView(); } @Override public void refreshView() { _holder.cbAll.setChecked(_data.shopCartAdapter.isAll()); _holder.tvPrice.setText("¥" + _data.shopCartAdapter.calc()); } }
基本的Holder和Data将组件和数据进行简单的管理,清晰可见。
presenter将业务进行分离,将传统的activity中请求数据进行分离。
ViewCallback这个类是为了解决Adapter与Activity直接的交互定义的接口。
接下来看看Adapter
package com.example.shopcartdemo.adapter; import android.content.Context; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageView; import com.example.shopcartdemo.R; import com.snicesoft.avlib.AVLib; import com.snicesoft.avlib.annotation.DataBind; import com.snicesoft.avlib.annotation.DataType; import com.snicesoft.avlib.annotation.Id; import com.snicesoft.avlib.annotation.Layout; import com.snicesoft.avlib.rule.IData; import com.snicesoft.avlib.rule.IHolder; import com.snicesoft.avlib.view.ViewFinder; import com.snicesoft.avlib.widget.AvAdapter; @Layout(R.layout.item_shopcart) public class ShopCartAdapter extends AvAdapter<ShopCartAdapter.Holder, ShopCartAdapter.Data> { public ShopCartAdapter(Context context) { super(context); } class Holder extends IHolder { @Id(R.id.btnAdd) Button btnAdd; @Id(R.id.btnDelete) Button btnDelete; @Id(R.id.cbSelect) CheckBox cbSelect; @Id(R.id.img) ImageView img; @Override public void initViewParams() { } } public static class Data extends IData { long gid; boolean isChecked = true; public long getGid() { return gid; } @DataBind(id = R.id.tvTitle) String title; @DataBind(id = R.id.tvPrice, prefix = "¥") int price; @DataBind(id = R.id.img, dataType = DataType.IMG) String image; @DataBind(id = R.id.edtCount) int count; public Data(long gid, String title, int price, String image, int count) { super(); this.gid = gid; this.title = title; this.price = price; this.image = image; this.count = count; } } public interface ViewCallback { void refreshView(); } ViewCallback callback; public void setCallback(ViewCallback callback) { this.callback = callback; } @Override public Holder newHolder() { return new Holder(); } @Override public void bindAfter(int position, final View view, Holder holder, final Data data) { holder.cbSelect.setOnCheckedChangeListener(null); holder.cbSelect.setChecked(data.isChecked); holder.btnAdd.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { data.count++; AVLib.dataBindTo(data, new ViewFinder(view), "count"); if (callback != null) callback.refreshView(); } }); holder.btnDelete.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (data.count > 1) { data.count--; AVLib.dataBindTo(data, new ViewFinder(view), "count"); if (callback != null) callback.refreshView(); } } }); holder.cbSelect .setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { data.isChecked = isChecked; if (callback != null) callback.refreshView(); } }); } public void setAll(boolean isChecked) { for (Data data : getDataList()) { data.isChecked = isChecked; } notifyDataSetChanged(); } public int calc() { int total = 0; for (Data data : getDataList()) { if (data.isChecked) total += data.count * data.price; } return total; } public boolean isAll() { for (Data data : getDataList()) { if (!data.isChecked) return false; } return true; } }
Holder的组件定义原则:需要在当前所在类中调用即可定义
Data的DataBind使用原则:需要绑定的的组件都可以。
当然通过上面可以看出Data中可以定义不用DataBind注解的字段,这个替代了传统的辅助Map或者List解决一些问题。
在上面的Data中用isChecked来表示CheckBox是否选中。这个问题,在我刚开始写android的时候,常常会用Map集合将position对应的boolean值记录下来,然后在getView中去检测。现在可以简单的通过Data几种管理,使得Adapter的字段不需要那么繁琐。
对于isChecked的使用产生了下面3个业务方法
setAll:这个方法用来设置全部选中(Activity中的全选按钮事件)
calc:这个方法用来计算购物车总额(Activity中的总计)
isAll:这个方法用来判断是否全部被选中(Activity的全选按钮设置checked的依据)
开发的思路就到这里,代码比较简单,下面送上源码。
作者:JFlex