Android商城App购物车规格联动选择
来源:互联网 发布:p2717h 知乎 编辑:程序博客网 时间:2024/03/29 03:43
1.概述
在商城类App中多属性商品的各规格联动选择的实现。
2.效果
商城类App中多属性商品规格选择的效果类型很多,本文实现的效果和淘宝相仿,效果如下:
3.思路
3.1规格属性联动
实现思路主要与后台接口的数据有关,本文的数据主要包括两个数组,商品的各规格属性的数组以及商品的规格属性组合的数组。此处题主在本地放了一个简单的json数据(可以去assets中修改数据,增加规格或者规格属性组合)。
如下:
{ "id": "1", "price": "3400~3500", "repertory": "22", "img": "http://www.ucicq.com/uploads/allimg/170304/03150NL5_0.jpeg", "specKey": [ { "id": "1", "spec_name": "颜色", "spec_key": [ "白色", "红色", "金色" ] }, { "id": "2", "spec_name": "霸气", "spec_key": [ "见闻色", "武装色", "霸王色" ] }, { "id": "3", "spec_name": "尺寸", "spec_key": [ "钻地", "爬行", "飞行" ] } ], "specsGroup": [ { "id": "1", "price": "90.00", "repertory": "25", "goods_spec": [ "白色", "见闻色", "钻地" ], "img": "http://img3.imgtn.bdimg.com/it/u=2247424153,1809959294&fm=11&gp=0.jpg" }, { "id": "2", "price": "100.00", "repertory": "4", "goods_spec": [ "红色", "见闻色", "钻地" ], "img": "http://www.005.tv/uploads/allimg/170208/1S92W191-19.jpg" }, { "id": "3", "price": "200.00", "repertory": "2", "goods_spec": [ "红色", "武装色", "飞行" ], "img": "http://imgsrc.baidu.com/forum/w=580/sign=92466e82763e6709be0045f70bc69fb8/71e24cd02f2eb938ae90e857dc628535e4dd6f98.jpg" }, { "id": "4", "price": "300.00", "repertory": "12", "goods_spec": [ "红色", "霸王色", "飞行" ], "img": "http://imgsrc.baidu.com/forum/w=580/sign=54bcb81c855494ee87220f111df4e0e1/768ca80f4bfbfbed0c060fdc71f0f736aec31fdc.jpg" }, { "id": "5", "price": "400.00", "repertory": "22", "goods_spec": [ "金色", "霸王色", "爬行" ], "img": "http://www.005.tv/uploads/allimg/170208/1S92S645-8.jpg" }, { "id": "6", "price": "500.00", "repertory": "22", "goods_spec": [ "金色", "霸王色", "飞行" ], "img": "http://zgsuixian.com/up/2017-2/201702051016251725888.jpg" } ]}
数据中该商品有3种规格,各规格下包含3种属性,各个规格属性组合应该为27种。但是在实际数据中,所给出的规格属性组合只有6种,说明还有21种规格属性组合的商品是没有的(此处暂不考虑库存)。所以在规格选择时,需要在UI上展现出来。本文思路是构建几个可供查找的数组,在每次选择时遍历查找,从而设置状态。
3.2规格中属性根据属性的字数 动态换行
此处用到了六六大侠 一篇博客中的jar包, 将jar包中的内容写成了代码中的三个类,其中自定义的控件customListView在listview中计算控件高度时仍存在问题,代码中使用的addView动态加载(之后会参考其中的逻辑,将此控件优化)。
4.实现代码
4.1项目结构
4.2xml布局此处只贴两处
4.2.1Dialog的布局 dialog_select_more
<?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"> <RelativeLayout android:id="@+id/rv_one" android:layout_width="match_parent" android:layout_height="80dp"> <View android:id="@+id/view" android:layout_width="match_parent" android:layout_height="20dp" android:background="@color/main_transparent" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="60dp" android:layout_below="@+id/view" android:background="@color/main_color_white"> <TextView android:id="@+id/tv_goods_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="93dp" android:layout_marginTop="16dp" android:text="¥438.00" android:textColor="@color/colorPrimary" android:textSize="@dimen/ui_26_sp" /> <ImageView android:id="@+id/iv_dismiss_dialog" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:padding="@dimen/margin_13dp" android:src="@mipmap/icon_cancel_dialog" /> </RelativeLayout> <ImageView android:id="@+id/iv_goods_pic" android:layout_width="70dp" android:layout_height="70dp" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginLeft="13dp" android:layout_marginStart="13dp" android:background="@drawable/shape_three" android:scaleType="centerCrop" android:src="@mipmap/mantis_shrimp" /> <View android:layout_width="match_parent" android:layout_height="@dimen/diver_line" android:layout_alignParentBottom="true" android:background="@color/main_line_grey" /> </RelativeLayout> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/rv_one" android:layout_marginBottom="49dp" android:background="@color/main_color_white"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/main_color_white" android:orientation="vertical"> <LinearLayout android:id="@+id/ln_add_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" /> <View android:id="@+id/div_view" android:layout_width="match_parent" android:layout_height="@dimen/diver_line" android:layout_marginLeft="@dimen/margin_13dp" android:layout_marginTop="10dp" android:background="@color/main_line_grey" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="45dp"> <TextView android:id="@+id/tv_number_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/margin_13dp" android:text="数量" android:textColor="@color/main_goods_name" android:textSize="@dimen/ui_24_sp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="@dimen/margin_13dp" android:background="@drawable/shape_four" android:orientation="horizontal"> <ImageView android:id="@+id/iv_minus_counts" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="8dp" android:src="@mipmap/icon_minus_light" /> <View android:layout_width="@dimen/diver_line" android:layout_height="match_parent" android:background="@color/main_goods_name" /> <TextView android:id="@+id/tv_counts" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:paddingLeft="10dp" android:paddingRight="10dp" android:text="1" android:textColor="@color/main_tv_black" android:textSize="@dimen/ui_24_sp" /> <View android:layout_width="@dimen/diver_line" android:layout_height="match_parent" android:background="@color/main_goods_name" /> <ImageView android:id="@+id/iv_plus_counts" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="8dp" android:src="@mipmap/icon_plus_deep" /> </LinearLayout> </RelativeLayout> </LinearLayout> </ScrollView> <TextView android:id="@+id/tv_sure" android:layout_width="match_parent" android:layout_height="49dp" android:layout_alignParentBottom="true" android:background="@color/colorPrimary" android:gravity="center" android:text="确定" android:textColor="@color/main_color_white" android:textSize="@dimen/ui_30_sp" /></RelativeLayout>
4.2.2Dialog的布局中需要addView布局 item_specs_key
<?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:orientation="vertical"> <TextView android:id="@+id/tv_spec_key" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/margin_13dp" android:text="数量" android:textColor="@color/main_goods_name" android:textSize="@dimen/ui_24_sp" /> <com.holdworld.specselectordemo.widgets.CustomListView android:id="@+id/custom_list_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/tv_spec_key" android:background="@color/main_color_white" /></RelativeLayout>
(此处注意,CustomListView默认父布局为RelativeLayout,若要修改需CustomListView的这段代码)
//这里customListView的父布局用的哪个LayoutParams 默认的是 RelativeLayout LayoutParams var14 = (LayoutParams) this.getLayoutParams();
4.3商品详情类 CommodityDetailsActivityActivity (assets中本地数据解析,一些商品详情基本UI,内容纯属虚构,并无此类商品)
package com.holdworld.specselectordemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.Toolbar;import android.util.Log;import android.view.View;import com.google.gson.Gson;import com.holdworld.specselectordemo.bean.Data;import java.io.InputStream;/** * Created by YangJane on 16/8/14. */public class CommodityDetailsActivityActivity extends AppCompatActivity { private Toolbar toolBar; private String jsonString; private Data data; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_commodity_details_activity); initView(); initJsonData(); getData(); } private void initView() { toolBar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolBar); } //本地数据测试专用 private void initJsonData() { try { InputStream is = getAssets().open("specs.json");//打开json数据 byte[] by = new byte[is.available()];//转字节 is.read(by); jsonString = new String(by, "utf-8"); is.close();//关闭流 } catch (Exception e) { e.printStackTrace(); } } //解析Json数据 private void getData() { Log.e("young", "jsonString==" + jsonString); Gson gson = new Gson(); data = gson.fromJson(jsonString, Data.class); Log.e("young", "data==" + data.toString()); } //加入购物车 public void AddShopCart(View view) { SelectMoreDialog selectMoreDialog = new SelectMoreDialog(this, data); selectMoreDialog.show(); } //立即购买 public void BuyNow(View view) { SelectMoreDialog selectMoreDialog = new SelectMoreDialog(this, data); selectMoreDialog.show(); }}
4.4 SelectMoreDialog,选择,联动逻辑都在此处。因为涉及多重遍历,此处需要多用心和仔细,尤其是类中的getSetting()这个方法。
package com.holdworld.specselectordemo;import android.app.Dialog;import android.content.Context;import android.os.Bundle;import android.util.Log;import android.view.Gravity;import android.view.LayoutInflater;import android.view.View;import android.view.Window;import android.widget.AdapterView;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import com.bumptech.glide.Glide;import com.holdworld.specselectordemo.adapter.SpecsGroupAdapter;import com.holdworld.specselectordemo.bean.Data;import com.holdworld.specselectordemo.widgets.CustomListView;import com.holdworld.specselectordemo.widgets.OnItemClickListener;import java.util.ArrayList;import java.util.List;/** * Created by YangJane on 16/8/14. */public class SelectMoreDialog extends Dialog implements View.OnClickListener { private Context context; private int w, h; private ImageView iv_dismiss_dialog; //商品参数 private ImageView iv_goods_pic; private TextView tv_goods_price; private String pic_url; private int repertory_counts; // private LinearLayout ln_add_view; private ArrayList<SpecsGroupAdapter> adapterList; private ArrayList<List<Integer>> selectList; //数量选择 private ImageView iv_minus_counts; private TextView tv_counts; private ImageView iv_plus_counts; private int counts = 1; //确定等 private TextView tv_sure; // private Data data; private List<Data.SpecKeyBean> specKeyList = new ArrayList<>(); private List<Data.SpecsGroupBean> specsGroupList = new ArrayList<>(); // private List<String> selectSpecsGroupList; private List<String> changeSpecsGroupList; private List<List<String>> allSpecsGroupList; public SelectMoreDialog(Context context, Data data) { super(context, R.style.SizeDialog); this.context = context; this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.data = data; specKeyList = data.getSpecKey(); specsGroupList = data.getSpecsGroup(); Window window = this.getWindow(); window.setGravity(Gravity.BOTTOM); w = (int) (context.getResources().getDisplayMetrics().widthPixels); h = (int) (context.getResources().getDisplayMetrics().heightPixels * 900 / 1280); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.dialog_select_more); initView(); initEvent(); this.getWindow().setLayout(w, h); } private void initView() { ln_add_view = (LinearLayout) findViewById(R.id.ln_add_view); iv_dismiss_dialog = (ImageView) findViewById(R.id.iv_dismiss_dialog); iv_goods_pic = (ImageView) findViewById(R.id.iv_goods_pic); tv_goods_price = (TextView) findViewById(R.id.tv_goods_price); //区分立即购买、加入购物车 tv_sure = (TextView) findViewById(R.id.tv_sure); //数量选择 iv_minus_counts = (ImageView) findViewById(R.id.iv_minus_counts); tv_counts = (TextView) findViewById(R.id.tv_counts); iv_plus_counts = (ImageView) findViewById(R.id.iv_plus_counts); counts = Integer.parseInt(tv_counts.getText().toString()); if (specsGroupList.size() > 0 || specsGroupList != null) { selectSpecsGroupList = specsGroupList.get(0).getGoods_spec(); Glide.with(context).load(specsGroupList.get(0).getImg()).into(iv_goods_pic); tv_goods_price.setText(specsGroupList.get(0).getPrice()); repertory_counts = Integer.parseInt(specsGroupList.get(0).getRepertory()); } addView(); } private void addView() { adapterList = new ArrayList<>(); //规格组合的第一个 为选择 if (specsGroupList.size() > 0 || specsGroupList != null) { selectSpecsGroupList = specsGroupList.get(0).getGoods_spec(); changeSpecsGroupList = new ArrayList<>(); changeSpecsGroupList.addAll(selectSpecsGroupList); } //后台所给的所有规格组合的集合 allSpecsGroupList = new ArrayList<>(); for (Data.SpecsGroupBean specsGroupBean : specsGroupList) { List<String> list = specsGroupBean.getGoods_spec(); allSpecsGroupList.add(list); } selectList = new ArrayList<>(); for (int i = 0; i < specKeyList.size(); i++) { List<Integer> list = new ArrayList<>(); //获取规格 List<String> specKeyString = specKeyList.get(i).getSpec_key(); for (int j = 0; j < specKeyString.size(); j++) { if (specKeyString.get(j).equals(selectSpecsGroupList.get(i))) { list.add(j, 1); } else { list.add(j, 0); } } selectList.add(i, list); } //添加View for (int i = 0; i < specKeyList.size(); i++) { //AddView View view = LayoutInflater.from(context).inflate(R.layout.item_specs_key, null); TextView tvSpecKey = (TextView) view.findViewById(R.id.tv_spec_key); CustomListView customListView = (CustomListView) view.findViewById(R.id.custom_list_view); // Data.SpecKeyBean specKeyBean = specKeyList.get(i); //各规格属性联动 getSetting(i, specKeyBean); //设置数据 tvSpecKey.setText(specKeyBean.getSpec_name()); SpecsGroupAdapter specGroupAdapter = new SpecsGroupAdapter(context, specKeyBean.getSpec_key(), selectList.get(i)); adapterList.add(specGroupAdapter); customListView.setAdapter(specGroupAdapter); final int finalI = i; specGroupAdapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { int clickFormat = finalI; Data.SpecKeyBean clickSpecKeyBean = specKeyList.get(clickFormat); //步骤1:先处理点击事件 if (selectList.get(clickFormat).get(position) != 2) { //遍历一下 for (int i = 0; i < selectList.get(clickFormat).size(); i++) { switch (selectList.get(clickFormat).get(i)) { case 0: if (i == position) { selectList.get(clickFormat).set(i, 1); } break; case 1: selectList.get(clickFormat).set(i, 0); break; case 2: break; } } } //点击之后的 各规格的选择状态 List<String> stringsList = new ArrayList<>(); stringsList = clickSpecKeyBean.getSpec_key(); if (selectList.get(clickFormat).contains(1)) { for (int g = 0; g < selectList.get(clickFormat).size(); g++) { if (selectList.get(clickFormat).get(g) == 1) { changeSpecsGroupList.set(clickFormat, stringsList.get(g)); } } } else { changeSpecsGroupList.set(clickFormat, "未选"); } //根据选择的组合 设置价格,库存,图片 if (!changeSpecsGroupList.contains("未选")) { StringBuilder sb = new StringBuilder(); for (String s : changeSpecsGroupList) {// sb.append(s).append(","); } String newString = sb.toString(); for (Data.SpecsGroupBean specBean : specsGroupList) { StringBuilder sbSpec = new StringBuilder(); for (String s : specBean.getGoods_spec()) { sbSpec.append(s).append(","); } //这样就可以把集合转化为字符串了 String specString = sbSpec.toString(); if (newString.contains(specString)) { pic_url = specBean.getImg(); Glide.with(context).load(pic_url).into(iv_goods_pic); tv_goods_price.setText(specBean.getPrice()); repertory_counts = Integer.parseInt(specBean.getRepertory()); counts = 1; tv_counts.setText(counts + ""); iv_minus_counts.setImageResource(R.mipmap.icon_minus_light); if (repertory_counts == 1) { iv_plus_counts.setImageResource(R.mipmap.icon_plus_light); } else { iv_plus_counts.setImageResource(R.mipmap.icon_plus_deep); } } } } for (int i = 0; i < specKeyList.size(); i++) { Data.SpecKeyBean specKeyBean = specKeyList.get(i); //各规格属性联动 getSetting(i, specKeyBean); adapterList.get(i).notifyDataSetChanged(); } } }); ln_add_view.addView(view); } } //各规格属性联动 private void getSetting(int position, Data.SpecKeyBean specKeyBean) { ArrayList<List<String>> list = new ArrayList<>(); list.addAll(allSpecsGroupList); //不含有已选规格属性的属性组合的数组 ArrayList<List<String>> remove_list = new ArrayList<>(); //遍历数据中 所有规则属性的组合 for (int goods_i = 0; goods_i < list.size(); goods_i++) { List<String> goodsList = list.get(goods_i); //遍历选中组合 for (int select_i = 0; select_i < changeSpecsGroupList.size(); select_i++) { //去掉一个规格 if (select_i == position) { } else { if (!changeSpecsGroupList.get(select_i).equals(goodsList.get(select_i))) { if (!changeSpecsGroupList.get(select_i).equals("未选")) { remove_list.add(list.get(goods_i)); } } } } } Log.e("young","remove_list==="+remove_list.toString()); //除掉不含有已选规格属性的属性组合的数组 得到在包含此规格外 其他规格已选属性的组合的数组 list.removeAll(remove_list); Log.e("young","list==="+list.toString()); //aloneString组合为 该规格下 所能有的全部属性 ArrayList<String> aloneString = new ArrayList<>(); for (int j = 0; j < list.size(); j++) { List<String> specialList = list.get(j); aloneString.add(specialList.get(position)); } Log.e("young","aloneString==="+aloneString.toString()); //通过这些字段组合 去对比此规格的所有样式 然后设置 是否有此规格组合 for (int m = 0; m < specKeyBean.getSpec_key().size(); m++) { if (!aloneString.contains(specKeyBean.getSpec_key().get(m))) { selectList.get(position).set(m, 2); } else { switch (selectList.get(position).get(m)) { case 0: selectList.get(position).set(m, 0); break; case 1: selectList.get(position).set(m, 1); break; case 2: selectList.get(position).set(m, 0); break; } } } } private void initEvent() { iv_dismiss_dialog.setOnClickListener(this); iv_minus_counts.setOnClickListener(this); iv_plus_counts.setOnClickListener(this); tv_sure.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_dismiss_dialog: dismiss(); break; case R.id.iv_minus_counts: MinusCounts(); break; case R.id.iv_plus_counts: PlusCounts(); break; case R.id.tv_sure: dismiss(); break; case R.id.tv_buy_now: dismiss(); break; } } private void MinusCounts() { if (counts == 1) { } else { if (counts == repertory_counts) { iv_plus_counts.setImageResource(R.mipmap.icon_plus_deep); } counts = counts - 1; tv_counts.setText(counts + ""); if (counts == 1) { iv_minus_counts.setImageResource(R.mipmap.icon_minus_light); } } } private void PlusCounts() { if (counts == repertory_counts) { iv_plus_counts.setImageResource(R.mipmap.icon_plus_light); } else { if (counts == 1) { iv_minus_counts.setImageResource(R.mipmap.icon_minus_deep); } counts = counts + 1; tv_counts.setText(counts + ""); if (counts == repertory_counts) { iv_plus_counts.setImageResource(R.mipmap.icon_plus_light); } } }}
4.5最后一个类,SpecsGroupAdapter 继承CustomAdapter
package com.holdworld.specselectordemo.adapter;import android.content.Context;import android.graphics.Color;import android.graphics.drawable.GradientDrawable;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import com.holdworld.specselectordemo.R;import com.holdworld.specselectordemo.widgets.CustomAdapter;import java.util.List;/** * Created by YangJane on 16/8/14. */public class SpecsGroupAdapter extends CustomAdapter { private LayoutInflater mInflater; private Context context; private List<String> spec_key; private List<Integer> hashMap; public SpecsGroupAdapter(Context context, List<String> spec_key, List<Integer> hashMap) { this.mInflater = LayoutInflater.from(context); this.context = context; this.spec_key = spec_key; this.hashMap=hashMap; } @Override public int getCount() { return spec_key.size(); } @Override public Object getItem(int position) { return spec_key.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Holder holder = null; if (convertView == null) { holder = new Holder(); convertView = mInflater.inflate(R.layout.item_specs_group, parent, false); holder.tv = (TextView) convertView.findViewById(R.id.tv); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); } holder.tv.setText(spec_key.get(position)); switch (hashMap.get(position)) { case 0: holder.tv.setBackgroundDrawable(setShape("#000000", "#ffffff")); holder.tv.setTextColor(Color.parseColor("#333333")); break; case 1: holder.tv.setBackgroundDrawable(setShape("#ff5000", "#ffffff")); holder.tv.setTextColor(Color.parseColor("#ff5000")); break; case 2: holder.tv.setBackgroundDrawable(setShape("#999999", "#ffffff")); holder.tv.setTextColor(Color.parseColor("#cccccc")); break; } return convertView; } class Holder { private TextView tv; } //设置 private GradientDrawable setShape(String stroke, String fill) { int strokeWidth = 2; // 2 边框宽度 int roundRadius = 5; // 5 圆角半径 int strokeColor = Color.parseColor(stroke);//边框颜色 int fillColor = Color.parseColor(fill);//内部填充颜色 GradientDrawable gd = new GradientDrawable();//创建drawable gd.setColor(fillColor); gd.setCornerRadius(roundRadius); gd.setStroke(strokeWidth, strokeColor); return gd; }}
4.6以上就是项目的主要代码,代码不足之处颇多,我会逐步优化。也希望各位多提宝贵意见,期待和大家的交流。
5.源码地址
http://download.csdn.net/download/holdworld8/9782492
6.参考文献
- http://blog.csdn.net/alovebtoc/article/details/17244111
- Android商城App购物车规格联动选择
- Android 商品详情中规格的联动选择实现
- Android实现ListView嵌套GridView实现购物规格选择
- iOS 走近商城 APP(三 WKWebView 商品规格选择框架封装)
- 自定义LinearLayout并搭配流式布局,实现商城app商品规格选择View
- Android 求 商城app中,物品加入购物车的特效代码,或者相关的例子。
- Android 求 商城app中,物品加入购物车的特效代码,或者相关的例子。
- 网上购物商城选择哪家好?
- Android-简单的商城购物车Demo
- 购物商城--商品详情多级联动
- 购物商城---购物车,结算
- 商城app_购物车
- 商城app_购物车
- 商城之购物车
- 商城购物车页面
- Sku算法--商城(品种,规格,参数等选择)
- Android商城购物车页面实现和逻辑实现
- 商城购物车逻辑思路
- 这款软件伪装成Flash侵入你的Mac电脑|恶意软件
- java spring Mail Api发送邮件
- mybatis --- 参数绑定问题
- MyEclipse 10.0破解安装教程
- 微信小程序用户数据的签名验证和加解密
- Android商城App购物车规格联动选择
- L1-012. 计算指数 java
- Memcached+magent集群安装及错误处理
- com.sun.faces.config.ConfigureListener
- 简单实现 Android M 指纹识别(附源码)
- JavaScript创建对象模式
- Vijos 1565 多边形 【区间DP】
- php 中的数组类型
- Vue路由开启keep-alive时的注意点