#####前言 其實作一個電商購物車,還真不是一個輕鬆的活。可是隻要掌握思路,一步一步來作,就會發現也就這樣。廢物很少說,直接上效果圖 完整代碼,github連接,但願能給個星,謝謝git
#####效果圖github
#####主要思路 整一個佈局就是ExpandableListView,而後自定義一個ActionBar,ActionBar上面顯示購物車數量,經過ActionBar上面的編輯狀態,店鋪佈局,全部商品佈局,底部佈局要進行相應的變化,編輯狀態下須要改變商品的數量,刪除商品,全選商品,隱藏店鋪的編輯。非編輯狀態能夠顯示店鋪的編輯,顯示結算,商品的信息。經過每個店鋪上面的編輯狀態,該店鋪旗下的全部商品佈局都要進行相應的變化。編輯狀態下,須要改變商品的數量和刪除商品。非編輯狀態下只須要顯示商品的信息。當該店鋪下全部商品都被勾選,對應的店鋪也要被勾選。反之,該店鋪下只要有一個商品沒有被勾選,那麼店鋪就不用勾選。其實邏輯挺簡單的,複雜的邏輯其實就是不少簡單邏輯組成的,咱們只須要把複雜的邏輯簡單化成不少簡單的邏輯,咱們就能完成一個大概的思路數組
#####代碼教學app
咱們第一步要作就是自定義一個ActionBar,幾行代碼就能解決。dom
private void initActionBar() {
//隱藏標題欄
if (getSupportActionBar() != null) {
//去掉陰影
getSupportActionBar().setElevation(0);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayShowCustomEnabled(true);
View view = getLayoutInflater().inflate(R.layout.acitonbar, null);
findView(view);
getSupportActionBar().setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
ActionBar.LayoutParams lp = (ActionBar.LayoutParams) view.getLayoutParams();
lp.gravity = Gravity.HORIZONTAL_GRAVITY_MASK | Gravity.CENTER_HORIZONTAL;
getSupportActionBar().setCustomView(view, lp);
}
複製代碼
完成以後效果圖:ide
第二步就是寫整個佈局xml文件了,基礎知識也沒有什麼好說的。佈局
完成以後效果圖: ui
第三步 就是寫店鋪的xml,商品的xml,商品爲空xml,此處略。第四步 重點講代碼this
case R.id.actionBar_edit: flag = !flag; setActionBarEditor(); setVisiable(); break;
private void setActionBarEditor() { for (int i = 0; i < groups.size(); i++) { StoreInfo group = groups.get(i); if (group.isActionBarEditor()) { group.setActionBarEditor(false); } else { group.setActionBarEditor(true); } } adapter.notifyDataSetChanged(); } private void setVisiable() { if (flag) { orderInfo.setVisibility(View.GONE); shareInfo.setVisibility(View.VISIBLE); actionBarEdit.setText("完成"); } else { orderInfo.setVisibility(View.VISIBLE); shareInfo.setVisibility(View.GONE); actionBarEdit.setText("編輯"); } }
checkBox的多選,勾選店鋪,勾選商品的回調spa
public interface CheckInterface { void checkGroup(int groupPosition, boolean isChecked); void checkChild(int groupPosition, int childPosition, boolean isChecked); }
經過監聽checkBox的勾選狀態,便於計算商品金額和刪除商品,計算購物車數量等操做。
店鋪對應的商品的數量增長,減小,刪除,更新的回調
public interface ModifyCountInterface { void doIncrease(int groupPosition, int childPosition, View showCountView, boolean isChecked); void doDecrease(int groupPosition, int childPosition, View showCountView, boolean isChecked); void doUpdate(int groupPosition,int childPosition,View showCountView, boolean isChecked); void childDelete(int groupPosition, int childPosition); }
經過該店鋪下的商品數量變化,來計算計算金額和購物車數量,當該店鋪的商品刪除完時,便把該店鋪從購物車中刪除掉。
增長商品數量
@Override public void doIncrease(int groupPosition, int childPosition, View showCountView, boolean isChecked) { GoodsInfo good = (GoodsInfo) adapter.getChild(groupPosition, childPosition); int count = good.getCount(); count++; good.setCount(count); ((TextView) showCountView).setText(String.valueOf(count)); adapter.notifyDataSetChanged(); calulate(); }
減小商品數量
@Override public void doDecrease(int groupPosition, int childPosition, View showCountView, boolean isChecked) { GoodsInfo good = (GoodsInfo) adapter.getChild(groupPosition, childPosition); int count = good.getCount(); if (count == 1) { return; } count--; good.setCount(count); ((TextView) showCountView).setText("" + count); adapter.notifyDataSetChanged(); calulate(); }
刪除商品
@Override public void childDelete(int groupPosition, int childPosition) { StoreInfo group = groups.get(groupPosition); List child = childs.get(group.getId()); child.remove(childPosition); if (child.size() == 0) { groups.remove(groupPosition); } adapter.notifyDataSetChanged(); calulate(); }
更新商品數量
public void doUpdate(int groupPosition, int childPosition, View showCountView, boolean isChecked) { GoodsInfo good = (GoodsInfo) adapter.getChild(groupPosition, childPosition); int count = good.getCount(); UtilsLog.i("進行更新數據,數量" + count + ""); ((TextView) showCountView).setText(String.valueOf(count)); adapter.notifyDataSetChanged(); calulate(); }
勾選店鋪,商品
@Override public void checkChild(int groupPosition, int childPosition, boolean isChecked) { boolean allChildSameState = true; //判斷該組下面的全部子元素是否處於同一狀態 StoreInfo group = groups.get(groupPosition); List child = childs.get(group.getId()); for (int i = 0; i < child.size(); i++) { //不選全中 if (child.get(i).isChoosed() != isChecked) { allChildSameState = false; break; } } if (allChildSameState) { group.setChoosed(isChecked);//若是子元素狀態相同,那麼對應的組元素也設置成這一種的同一狀態 } else { group.setChoosed(false);//不然一概視爲未選中 } if (isCheckAll()) { allCheckBox.setChecked(true);//全選 } else { allCheckBox.setChecked(false);//反選 } adapter.notifyDataSetChanged(); calulate(); } @Override public void checkGroup(int groupPosition, boolean isChecked) { StoreInfo group = groups.get(groupPosition); List child = childs.get(group.getId()); for (int i = 0; i < child.size(); i++) { child.get(i).setChoosed(isChecked); } if (isCheckAll()) { allCheckBox.setChecked(true);//全選 } else { allCheckBox.setChecked(false);//反選 } adapter.notifyDataSetChanged(); calulate(); }
底部的遍歷刪除商品
刪除操做 1.不要邊遍歷邊刪除,容易出現數組越界的狀況 2.把將要刪除的對象放進相應的容器中,待遍歷完,用removeAll的方式進行刪除 private void doDelete() { List toBeDeleteGroups = new ArrayList(); //待刪除的組元素 for (int i = 0; i < groups.size(); i++) { StoreInfo group = groups.get(i); if (group.isChoosed()) { toBeDeleteGroups.add(group); } List toBeDeleteChilds = new ArrayList();//待刪除的子元素 List child = childs.get(group.getId()); for (int j = 0; j < child.size(); j++) { if (child.get(j).isChoosed()) { toBeDeleteChilds.add(child.get(j)); } } child.removeAll(toBeDeleteChilds); } groups.removeAll(toBeDeleteGroups); //從新設置購物車 setCartNum(); adapter.notifyDataSetChanged(); }
底部的全選商品和反選商品
private boolean isCheckAll() { for (StoreInfo group : groups) { if (!group.isChoosed()) { return false; } } return true; } private void doCheckAll() { for (int i = 0; i < groups.size(); i++) { StoreInfo group = groups.get(i); group.setChoosed(allCheckBox.isChecked()); List child = childs.get(group.getId()); for (int j = 0; j < child.size(); j++) { child.get(j).setChoosed(allCheckBox.isChecked());//這裏出現過錯誤 } } adapter.notifyDataSetChanged(); calulate(); }
計算商品價格
private void calulate() { mtotalPrice = 0.00; mtotalCount = 0; for (int i = 0; i < groups.size(); i++) { StoreInfo group = groups.get(i); List child = childs.get(group.getId()); for (int j = 0; j < child.size(); j++) { GoodsInfo good = child.get(j); if (good.isChoosed()) { mtotalCount++; mtotalPrice += good.getPrice() * good.getCount(); } } } totalPrice.setText("¥" + mtotalPrice + ""); goPay.setText("去支付(" + mtotalCount + ")"); if (mtotalCount == 0) { setCartNum(); } else { shoppingcatNum.setText("購物車(" + mtotalCount + ")"); } }
設置購物車數量和清空購物車
private void setCartNum() { int count = 0; for (int i = 0; i < groups.size(); i++) { StoreInfo group = groups.get(i); group.setChoosed(allCheckBox.isChecked()); List Childs = childs.get(group.getId()); for (GoodsInfo childs : Childs) { count++; } } //購物車已經清空 if (count == 0) { clearCart(); } else { shoppingcatNum.setText("購物車(" + count + ")"); } } private void clearCart() { shoppingcatNum.setText("購物車(0)"); actionBarEdit.setVisibility(View.GONE); llCart.setVisibility(View.GONE); empty_shopcart.setVisibility(View.VISIBLE);//這裏發生過錯誤 }
初始化店鋪和商品信息
*模擬數據
遵循適配器的數據列表填充原則,組元素被放在一個list中,對應着組元素的下轄子元素被放在Map中 其Key是組元素的Id private void initData() { mcontext = this; groups = new ArrayList(); childs = new HashMap>(); for (int i = 0; i < 5; i++) { groups.add(new StoreInfo(i + "", "小馬的第" + (i + 1) + "號當鋪")); List goods = new ArrayList<>(); for (int j = 0; j <= i; j++) { int[] img = {R.drawable.cmaz, R.drawable.cmaz, R.drawable.cmaz, R.drawable.cmaz, R.drawable.cmaz, R.drawable.cmaz}; //i-j 就是商品的id, 對應着第幾個店鋪的第幾個商品,1-1 就是第一個店鋪的第一個商品 goods.add(new GoodsInfo(i + "-" + j, "商品", groups.get(i).getName() + "的第" + (j + 1) + "個商品", 255.00 + new Random().nextInt(1500), 1555 + new Random().nextInt(3000), "第一排", "出頭天者", img[j], new Random().nextInt(100))); } childs.put(groups.get(i).getId(), goods); } }
public class ShopcatAdapter extends BaseExpandableListAdapter { private List groups; //這個String對應着StoreInfo的Id,也就是店鋪的Id private Map> childrens; private Context mcontext; private CheckInterface checkInterface; private ModifyCountInterface modifyCountInterface; private GroupEditorListener groupEditorListener; private int count = 0; private boolean flag=true; //組的編輯按鈕是否可見,true可見,false不可見 public ShopcatAdapter(List groups, Map> childrens, Context mcontext) { this.groups = groups; this.childrens = childrens; this.mcontext = mcontext; } @Override public int getGroupCount() { return groups.size(); } @Override public int getChildrenCount(int groupPosition) { String groupId = groups.get(groupPosition).getId(); return childrens.get(groupId).size(); } @Override public Object getGroup(int groupPosition) { return groups.get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { List childs = childrens.get(groups.get(groupPosition).getId()); return childs.get(childPosition); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public boolean hasStableIds() { return false; } @Override public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { final GroupViewHolder groupViewHolder; if (convertView == null) { convertView = View.inflate(mcontext, R.layout.item_shopcat_group, null); groupViewHolder = new GroupViewHolder(convertView); convertView.setTag(groupViewHolder); } else { groupViewHolder = (GroupViewHolder) convertView.getTag(); } final StoreInfo group = (StoreInfo) getGroup(groupPosition); groupViewHolder.storeName.setText(group.getName()); groupViewHolder.storeCheckBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { group.setChoosed(((CheckBox) v).isChecked()); checkInterface.checkGroup(groupPosition, ((CheckBox) v).isChecked()); } }); groupViewHolder.storeCheckBox.setChecked(group.isChoosed()); /**【文字指的是組的按鈕的文字】 * 當咱們按下ActionBar的 "編輯"按鈕, 應該把全部組的文字顯示"編輯",而且設置按鈕爲不可見 * 當咱們完成編輯後,再把組的編輯按鈕設置爲可見 * 不懂,請本身操做淘寶,觀察一遍 */ if(group.isActionBarEditor()){ group.setEditor(false); groupViewHolder.storeEdit.setVisibility(View.GONE); flag=false; }else{ flag=true; groupViewHolder.storeEdit.setVisibility(View.VISIBLE); } /** * 思路:當咱們按下組的"編輯"按鈕後,組處於編輯狀態,文字顯示"完成" * 當咱們點擊「完成」按鈕後,文字顯示"編輯「,組處於未編輯狀態 */ if (group.isEditor()) { groupViewHolder.storeEdit.setText("完成"); } else { groupViewHolder.storeEdit.setText("編輯"); } groupViewHolder.storeEdit.setOnClickListener(new GroupViewClick(group, groupPosition, groupViewHolder.storeEdit)); return convertView; } @Override public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { final ChildViewHolder childViewHolder; if (convertView == null) { convertView = View.inflate(mcontext, R.layout.item_shopcat_product, null); childViewHolder = new ChildViewHolder(convertView); convertView.setTag(childViewHolder); } else { childViewHolder = (ChildViewHolder) convertView.getTag(); } /** * 根據組的編輯按鈕的可見與不可見,去判斷是組對下轄的子元素編輯 仍是ActionBar對組的下瞎元素的編輯 * 若是組的編輯按鈕可見,那麼確定是組對本身下轄元素的編輯 * 若是組的編輯按鈕不可見,那麼確定是ActionBar對組下轄元素的編輯 */ if(flag){ if (groups.get(groupPosition).isEditor()) { childViewHolder.editGoodsData.setVisibility(View.VISIBLE); childViewHolder.delGoods.setVisibility(View.VISIBLE); childViewHolder.goodsData.setVisibility(View.GONE); } else { childViewHolder.delGoods.setVisibility(View.VISIBLE); childViewHolder.goodsData.setVisibility(View.VISIBLE); childViewHolder.editGoodsData.setVisibility(View.GONE); } }else{ if(groups.get(groupPosition).isActionBarEditor()){ childViewHolder.delGoods.setVisibility(View.GONE); childViewHolder.editGoodsData.setVisibility(View.VISIBLE); childViewHolder.goodsData.setVisibility(View.GONE); }else{ childViewHolder.delGoods.setVisibility(View.VISIBLE); childViewHolder.goodsData.setVisibility(View.VISIBLE); childViewHolder.editGoodsData.setVisibility(View.GONE); } } final GoodsInfo child = (GoodsInfo) getChild(groupPosition, childPosition); if (child != null) { childViewHolder.goodsName.setText(child.getDesc()); childViewHolder.goodsPrice.setText("¥" + child.getPrice() + ""); childViewHolder.goodsNum.setText(String.valueOf(child.getCount())); childViewHolder.goodsImage.setImageResource(R.drawable.cmaz); childViewHolder.goods_size.setText("門票:" + child.getColor() + ",類型:" + child.getSize()); //設置打折前的原價 SpannableString spannableString = new SpannableString("¥" + child.getPrime_price() + ""); StrikethroughSpan span = new StrikethroughSpan(); spannableString.setSpan(span,0,spannableString.length()-1+1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); //避免無限次的append if (childViewHolder.goodsPrimePrice.length() > 0) { childViewHolder.goodsPrimePrice.setText(""); } childViewHolder.goodsPrimePrice.setText(spannableString); childViewHolder.goodsBuyNum.setText("x" + child.getCount() + ""); childViewHolder.singleCheckBox.setChecked(child.isChoosed()); childViewHolder.singleCheckBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { child.setChoosed(((CheckBox) v).isChecked()); childViewHolder.singleCheckBox.setChecked(((CheckBox) v).isChecked()); checkInterface.checkChild(groupPosition, childPosition, ((CheckBox) v).isChecked()); } }); childViewHolder.increaseGoodsNum.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { modifyCountInterface.doIncrease(groupPosition, childPosition, childViewHolder.goodsNum, childViewHolder.singleCheckBox.isChecked()); } }); childViewHolder.reduceGoodsNum.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { modifyCountInterface.doDecrease(groupPosition, childPosition, childViewHolder.goodsNum, childViewHolder.singleCheckBox.isChecked()); } }); childViewHolder.goodsNum.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showDialog(groupPosition,childPosition,childViewHolder.goodsNum,childViewHolder.singleCheckBox.isChecked(),child); } }); childViewHolder.delGoods.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new AlertDialog.Builder(mcontext) .setMessage("肯定要刪除該商品嗎") .setNegativeButton("取消",null) .setPositiveButton("肯定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { modifyCountInterface.childDelete(groupPosition,childPosition); } }) .create() .show();; } }); } return convertView; } private void showDialog(final int groupPosition, final int childPosition, final View showCountView,final boolean isChecked, final GoodsInfo child) { final AlertDialog.Builder alertDialog_Builder=new AlertDialog.Builder(mcontext); View view= LayoutInflater.from(mcontext).inflate(R.layout.dialog_change_num,null); final AlertDialog dialog=alertDialog_Builder.create(); dialog.setView(view); count=child.getCount(); final EditText num= (EditText) view.findViewById(R.id.dialog_num); num.setText(count+""); dialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { UtilTool.showKeyboard(mcontext,showCountView); } }); final TextView increase= (TextView) view.findViewById(R.id.dialog_increaseNum); final TextView DeIncrease=(TextView)view.findViewById(R.id.dialog_reduceNum); final TextView pButton= (TextView) view.findViewById(R.id.dialog_Pbutton); final TextView nButton= (TextView) view.findViewById(R.id.dialog_Nbutton); nButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); pButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int number=Integer.parseInt(num.getText().toString().trim()); if(number==0){ dialog.dismiss(); }else{ UtilsLog.i("數量="+number+""); num.setText(String.valueOf(number)); child.setCount(number); modifyCountInterface.doUpdate(groupPosition,childPosition,showCountView,isChecked); dialog.dismiss(); } } }); increase.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { count++; num.setText(String.valueOf(count)); } }); DeIncrease.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(count>1){ count--; num.setText(String.valueOf(count)); } } }); dialog.show(); } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return false; } public GroupEditorListener getGroupEditorListener() { return groupEditorListener; } public void setGroupEditorListener(GroupEditorListener groupEditorListener) { this.groupEditorListener = groupEditorListener; } public CheckInterface getCheckInterface() { return checkInterface; } public void setCheckInterface(CheckInterface checkInterface) { this.checkInterface = checkInterface; } public ModifyCountInterface getModifyCountInterface() { return modifyCountInterface; } public void setModifyCountInterface(ModifyCountInterface modifyCountInterface) { this.modifyCountInterface = modifyCountInterface; } static class GroupViewHolder { @BindView(R.id.store_checkBox) CheckBox storeCheckBox; @BindView(R.id.store_name) TextView storeName; @BindView(R.id.store_edit) TextView storeEdit; public GroupViewHolder(View view) { ButterKnife.bind(this, view); } } public interface CheckInterface { void checkGroup(int groupPosition, boolean isChecked); void checkChild(int groupPosition, int childPosition, boolean isChecked); } void doIncrease(int groupPosition, int childPosition, View showCountView, boolean isChecked); void doDecrease(int groupPosition, int childPosition, View showCountView, boolean isChecked); void doUpdate(int groupPosition,int childPosition,View showCountView, boolean isChecked); void childDelete(int groupPosition, int childPosition); } public interface GroupEditorListener { void groupEditor(int groupPosition); } private class GroupViewClick implements View.OnClickListener { private StoreInfo group; private int groupPosition; private TextView editor; public GroupViewClick(StoreInfo group, int groupPosition, TextView editor) { this.group = group; this.groupPosition = groupPosition; this.editor = editor; } @Override public void onClick(View v) { if (editor.getId() == v.getId()) { groupEditorListener.groupEditor(groupPosition); if (group.isEditor()) { group.setEditor(false); } else { group.setEditor(true); } notifyDataSetChanged(); } } } static class ChildViewHolder { @BindView(R.id.single_checkBox) CheckBox singleCheckBox; @BindView(R.id.goods_image) ImageView goodsImage; @BindView(R.id.goods_name) TextView goodsName; @BindView(R.id.goods_size) TextView goods_size; @BindView(R.id.goods_price) TextView goodsPrice; @BindView(R.id.goods_prime_price) TextView goodsPrimePrice; @BindView(R.id.goods_buyNum) TextView goodsBuyNum; @BindView(R.id.goods_data) RelativeLayout goodsData; @BindView(R.id.reduce_goodsNum) TextView reduceGoodsNum; @BindView(R.id.goods_Num) TextView goodsNum; @BindView(R.id.increase_goods_Num) TextView increaseGoodsNum; @BindView(R.id.goodsSize) TextView goodsSize; @BindView(R.id.del_goods) TextView delGoods; @BindView(R.id.edit_goods_data) LinearLayout editGoodsData; public ChildViewHolder(View view) { ButterKnife.bind(this, view); } } }
#####總結 其實代碼量仍是挺多的,主要的仍是思路。思路理明白了,天然水到渠成。代碼照着打一遍,思路天然明瞭。難點在於店鋪與商品,單選框,編輯按鈕之間的關係處理和佈局的改變。