Android电商类项目添加多组物品规格

前两年互联网行业火热的时候,大多数移动端的项目以电商为主,而电商中添加商品又是一个不可缺少的环节,前段时间做了一个小型的电商项目,其中有一个添加物品规格的需求,可以看下面的展示图。

nullpoint Exception

限于图片上传大小,尺寸略小,并只能展示添加和删除的部分逻辑,感兴趣的同学可以下载项目运行看下效果;
github地址:https://github.com/LzyChonger/CreateGoodsSpec

业务需求分析

业务需求是在界面上输入一个规格名称,下面再出现规格的具体值【举个例子:输入: 颜色 — 红色,黄色等】,并继续在下面展示出此规格的价钱和库存(ps:或者其他的任何参数),规格可以删除,并且对应的价格库存也要删除,点击保存将数据回传到前一个商品添加界面,再次点击进来数据要回显,点击返回按钮将数据清空,最后服务器要求将数据以json的形式传递过去。

难点在于添加两个及两个以上规格的时候,下面要求物品规格必须要去重组合起来,【举个例子: 尺寸(xl):颜色(red), 尺寸(xxl):颜色(yellow) ,尺寸(xxl):颜色(red),尺寸(xxl):颜色(yellow)】并输入相应的价钱和库存,并将规格的参数携带回到上一个界面。(也有可能是三组以上的参数 [手动滑稽])

demo中便于展示,省略了商品添加界面,只做了规格添加,并把数据转换成json传到前一个界面,放在TextView中展示。

技术分析

我的思路首先是想到,需要用两个RecylerView,上面添加商品的一个,下面展示组合的是一个,需要将上面手动输入的数据拿到,通过排列组合去重(技术有限只能想到有遍历的方式,大佬们有优秀的算法烦请指点[手动膜拜大佬]),展示到下面,一些对应删除操作和添加操作需要用到接口回调。

代码展示

限于篇幅这里只展示主要代码逻辑
首先MainActivity.java,没有复杂的东西,都是常规代码,JSON用的是fastjson,布局文件就不贴了,一个Button,一个TextView;

public class MainActivity extends AppCompatActivity {
    private static final int SELECT_SPEC = 102;

    private Button mBtn;
    private TextView mTv_goods_spec;
    private List mGuiges;
    private List mGood_spec;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn = (Button) findViewById(R.id.btn);
        mTv_goods_spec = (TextView) findViewById(R.id.tv_goods_spec);

        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, GoodsSpecActicity.class);
                //做非空判断,回显数据
                if (mGood_spec != null) {
                    intent.putExtra("good_spec", (Serializable) mGood_spec);
                }
                if(mGuiges !=null){
                    intent.putExtra("good_guige", (Serializable) mGuiges);
                }
                startActivityForResult(intent, SELECT_SPEC);
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case SELECT_SPEC:
                mGood_spec = (List) data.getSerializableExtra("good_spec");
                mGuiges = (List) data.getSerializableExtra("good_guige");
                if (mGood_spec == null || mGood_spec.size() == 0) {
                    mTv_goods_spec.setHint("未填写");
                } else {
                    mTv_goods_spec.setText("Guiges:" + JSON.toJSONString(mGuiges)
                            + "\nGood_spec:" + JSON.toJSONString(mGood_spec));
                }
                break;
            default:
        }
    }
}

然后是实体类,实体结构非常重要,所以贴一下代码,偷懒用了Serializable ,项目的话建议用Parcelable

public class StoreManagerListEntity implements Serializable {

    public static class SkuListEntity implements Serializable {
        /*
        "sku_id": "7",
        "spec": "绿色:10寸",
        "sku_name": "颜色,尺码",
        "price": "10.00",
        "stock": "0"库存
         */
        public String sku_id;
        public String spec;
        public String sku_name;
        public String price;
        public String stock;
    }

    public static class GuigesEntity implements Serializable {
        /*
        "title": "颜色",
        "guigeArray": ["绿色", "红色"]
         */
        public String title;
        public List guigeArray= new ArrayList<>();
    }

}

添加商品规格的主界面,交互了两个RecylerView的数据,布局文件顺便也贴出来,一些资源文件就不贴了。

public class GoodsSpecActicity extends AppCompatActivity implements View.OnClickListener {
    private RelativeLayout mRlBack;
    private TextView mTvTitle;
    private TextView mTvRight;
    private RelativeLayout mRlRight;
    private EditText mEtName;
    private TextView mTvAdd;
    private View mView01;
    private RecyclerView mRvSpec;
    private RecyclerView mRvPrice;

    private List mSpecNameList = new ArrayList<>();
    private List mSpecPriceList = new ArrayList<>();
    private GoodsSpecTypeAdapter mSpecTypeAdapter;
    private GoodsSpecTypeNumberAdapter mNumberAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_goods_spec);

        initView();
    }

    private void initView() {
        mRlBack = (RelativeLayout) findViewById(R.id.rl_back);
        mTvTitle = (TextView) findViewById(R.id.tv_title);
        mTvRight = (TextView) findViewById(R.id.tv_right);
        mRlRight = (RelativeLayout) findViewById(R.id.rl_right);
        mEtName = (EditText) findViewById(R.id.et_name);
        mTvAdd = (TextView) findViewById(R.id.tv_add);
        mView01 = (View) findViewById(R.id.view01);
        mRvSpec = (RecyclerView) findViewById(R.id.rv_spec);
        mRvPrice = (RecyclerView) findViewById(R.id.rv_price);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        mRvSpec.setLayoutManager(layoutManager);
        mRvSpec.setHasFixedSize(true);
        LinearLayoutManager layoutManager1 = new LinearLayoutManager(this);
        mRvPrice.setLayoutManager(layoutManager1);
        mRvPrice.setHasFixedSize(true);

        mRlBack.setOnClickListener(this);
        mTvAdd.setOnClickListener(this);
        mRlRight.setOnClickListener(this);

        mTvTitle.setText("商品规格");
        mRlRight.setVisibility(View.VISIBLE);
        mTvRight.setText("完成");
        mSpecTypeAdapter = new GoodsSpecTypeAdapter(this, mSpecNameList);
        mRvSpec.setAdapter(mSpecTypeAdapter);

        mNumberAdapter = new GoodsSpecTypeNumberAdapter(this, mSpecPriceList);
        mRvPrice.setAdapter(mNumberAdapter);
        mRvPrice.setNestedScrollingEnabled(false);
        mSpecTypeAdapter.setAddItem(new RecylerViewAddItemListener() {
            @Override
            public void onAddItemListener(List entity, int position) {
                mSpecPriceList.clear();
                String sku_name = "";
                for (int i = 0; i < mSpecNameList.size(); i++) {
                    if (i < mSpecNameList.size() - 1) {
                        sku_name = sku_name + mSpecNameList.get(i).title + ",";
                    } else {
                        sku_name = sku_name + mSpecNameList.get(i).title;
                    }
                }
                if (entity != null) {
                    for (int i = 0; i < entity.size(); i++) {
                        StoreManagerListEntity.SkuListEntity serverEntity = new StoreManagerListEntity.SkuListEntity();
                        serverEntity.spec = entity.get(i);
                        serverEntity.sku_name = sku_name;
                        mSpecPriceList.add(serverEntity);
                    }
                }
                mNumberAdapter.notifyDataSetChanged();
            }
        });

        List good_guige = (List) getIntent().getSerializableExtra("good_guige");
        if (good_guige != null) {
            mSpecNameList.addAll(good_guige);
        }
        List good_spec = (List) getIntent().getSerializableExtra("good_spec");
        if (good_spec != null) {
            mSpecPriceList.addAll(good_spec);
        }

    }


    private void toComplete() {
        for (int i = 0; i < mSpecPriceList.size(); i++) {
            if (TextUtils.isEmpty(mSpecPriceList.get(i).price)) {
                UIUtil.toastShort(this, "请输入价格");
                return;
            }
            if (TextUtils.isEmpty(mSpecPriceList.get(i).stock)) {
                UIUtil.toastShort(this, "请输入库存");
                return;
            }
        }
        Intent intent = new Intent();
        intent.putExtra("good_spec", (Serializable) mSpecPriceList);
        intent.putExtra("good_guige", (Serializable) mSpecNameList);
        setResult(RESULT_OK, intent);
        finish();
    }

    private void addList() {

        String name = mEtName.getText().toString().trim();
        if (!TextUtils.isEmpty(name)) {

            StoreManagerListEntity.GuigesEntity entity = new StoreManagerListEntity.GuigesEntity();
            entity.title = name;
            mSpecNameList.add(entity);
            mSpecTypeAdapter.notifyDataSetChanged();
            mEtName.setText("");
            ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(
                    this.getCurrentFocus().getWindowToken(),
                    InputMethodManager.HIDE_NOT_ALWAYS);
        } else {
            UIUtil.toastShort(this, "请输入规格名称");
        }
    }


    public void saveEditData(int position, String type, String str) {

        try {
            if ("1".equals(type)) {
                mSpecPriceList.get((position - 1) / 100).price = str;
            } else if ("2".equals(type)) {
                mSpecPriceList.get((position - 2) / 100).stock = str;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.rl_back:
                UIUtil.showConfirm(this, "返回将清空数据,是否确定?", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mSpecPriceList.clear();
                        mSpecNameList.clear();
                        Intent intent = new Intent();
                        intent.putExtra("good_spec", (Serializable) mSpecPriceList);
                        intent.putExtra("good_guige", (Serializable) mSpecNameList);
                        setResult(RESULT_OK, intent);
                        finish();
                    }
                });
                break;
            case R.id.tv_add:
                addList();
                break;
            case R.id.rl_right:
                toComplete();
                break;
            default:
                break;
        }

    }
}



    

        


            

                

                    
                

                

                

                    
                

            

            

                

                

                

            

            


            

            

            

                

                

                

            

            

            


                

            
        
    


后面重头戏来了,首先是上面的RecylerView的适配器

public class GoodsSpecTypeAdapter extends BaseAdapter {

    private RecylerViewAddItemListener mAddItemListener;
    public GoodsSpecTypeInfoAdapter mInfoAdapter;

    public GoodsSpecTypeAdapter(Context context, List list) {
        super(context, list);
    }

    @Override
    public GoodsSpecTypeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new GoodsSpecTypeViewHolder(mInflater.inflate(R.layout.item_goods_spec_type, null));
    }

    @Override
    public void onBindViewHolder(final GoodsSpecTypeViewHolder holder, final int position) {
        final StoreManagerListEntity.GuigesEntity item = getItem(position);
        holder.mTv_type_name.setText(item.title);
        mInfoAdapter = new GoodsSpecTypeInfoAdapter(mContext, item.guigeArray, position);
        holder.mRv_spec_size.setAdapter(mInfoAdapter);

        mInfoAdapter.setOnItemDeleteListener(new ImageRecylerReduceItemListener() {
            @Override
            public void onReduceItemListener(int position) {
                List list = bindAnotherRecyler1();
                mAddItemListener.onAddItemListener(list, position);
            }
        });

        holder.mEt_size.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    String size = holder.mEt_size.getText().toString().trim();
                    if (!TextUtils.isEmpty(size)) {
                        mInfoAdapter = new GoodsSpecTypeInfoAdapter(mContext, item.guigeArray /*mTypeList*/, position);
                        item.guigeArray.add(size);
                        List list = bindAnotherRecyler1();
                        holder.mRv_spec_size.setAdapter(mInfoAdapter);
                        mAddItemListener.onAddItemListener(list, position);
                        holder.mEt_size.setText("");

                        mInfoAdapter.setOnItemDeleteListener(new ImageRecylerReduceItemListener() {
                            @Override
                            public void onReduceItemListener(int position) {

                                List list = bindAnotherRecyler1();
                                mAddItemListener.onAddItemListener(list, position);
                            }
                        });
                    } else {
                        UIUtil.toastShort(mContext, "请输入规格参数");
                    }
                }
                return false;
            }
        });


    }

    private List bindAnotherRecyler1() {
        int b = 0;
        if (mItems.size() > 0) {
            List copylist = new ArrayList<>();
            for (int a = 0; a < mItems.size(); a++) {
                if (mItems.get(a).guigeArray.size() != 0) {
                    copylist.addAll(mItems.get(a).guigeArray);
                    b = a;
                    break;
                }
            }
            if (copylist.size() > 0) {
                List L0 = new ArrayList<>();
                L0.addAll(copylist);
                for (int i = b + 1; i < mItems.size(); i++) {
                    List L1 = mItems.get(i).guigeArray;

                    List list = new ArrayList<>();
                    for (int j = 0; j < L0.size(); j++) {
                        for (int z = 0; z < L1.size(); z++) {
                            String s = L0.get(j) + ":" + L1.get(z);
                            list.add(s);
                        }
                    }
                    if (list.size() != 0) {
                        L0 = list;
                    }
                }
                return L0;
            }
        }
        return null;
    }

    class GoodsSpecTypeViewHolder extends RecyclerView.ViewHolder {

        private final RecyclerView mRv_spec_size;
        private final EditText mEt_size;
        private final TextView mTv_type_name;

        public GoodsSpecTypeViewHolder(View itemView) {
            super(itemView);
            mTv_type_name = itemView.findViewById(R.id.tv_type_name);
            mEt_size = itemView.findViewById(R.id.et_size);
            mRv_spec_size = itemView.findViewById(R.id.rv_spec_size);
            GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, 3);
            gridLayoutManager.setOrientation(LinearLayout.VERTICAL);
            mRv_spec_size.setLayoutManager(gridLayoutManager);
            mRv_spec_size.setHasFixedSize(true);
        }
    }

    public void setAddItem(RecylerViewAddItemListener addItemListener) {
        mAddItemListener = addItemListener;
    }

重点就在于bindAnotherRecyler1()这个方法,在数据输入后,通过EditText的监听或者点击事件来组装数据,因为在这个适配器中,是数据的来源,所以在这里拿到数据并组装数据,再传到另一个适配器中可以直接显示,首先做一个判断增加程序的健壮性,第一次遍历中,新建一个copylist,遍历此适配器的规格数组,同事不影响适配器的数据展示;在用copylist的数据进行双重循环遍历组装数据,并做一些空数据处理,":"是为了后台存储数据做的规则。

   private List bindAnotherRecyler1() {
        int b = 0;
        if (mItems.size() > 0) {
            List copylist = new ArrayList<>();
            for (int a = 0; a < mItems.size(); a++) {
                if (mItems.get(a).guigeArray.size() != 0) {
                    copylist.addAll(mItems.get(a).guigeArray);
                    b = a;
                    break;
                }
            }
            if (copylist.size() > 0) {
                List L0 = new ArrayList<>();
                L0.addAll(copylist);
                for (int i = b + 1; i < mItems.size(); i++) {
                    List L1 = mItems.get(i).guigeArray;

                    List list = new ArrayList<>();
                    for (int j = 0; j < L0.size(); j++) {
                        for (int z = 0; z < L1.size(); z++) {
                            String s = L0.get(j) + ":" + L1.get(z);
                            list.add(s);
                        }
                    }
                    if (list.size() != 0) {
                        L0 = list;
                    }
                }
                return L0;
            }
        }
        return null;
    }

在这个适配器里面嵌套了一个新的适配器,是便于规格删除操作和展示用的,以前遇到这种问题都是用这种方式来写~~~(>_<)~~~,如果众大佬有更好的方式,希望能帮我指出来去改正。

public class GoodsSpecTypeInfoAdapter extends RecyclerView.Adapter {
    private final Context mContext;
    private final List getItem;
    protected LayoutInflater mInflater;
    private int mPosition;
    private ImageRecylerReduceItemListener mItemDeleteListener;

    public GoodsSpecTypeInfoAdapter(Context context,List list, int position) {
        mContext = context;
        getItem = list;
        mInflater = LayoutInflater.from(context);
        mPosition = position;
    }

    @Override
    public GoodsSpecTypeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new GoodsSpecTypeViewHolder(mInflater.inflate(R.layout.item_goods_spec_info, parent, false));
    }

    @Override
    public void onBindViewHolder(GoodsSpecTypeViewHolder holder, final int position) {
        holder.mRl_del.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                getItem.remove(position);
                notifyDataSetChanged();
                mItemDeleteListener.onReduceItemListener(position);
            }
        });
        String item = getItem.get(position);
        if (!TextUtils.isEmpty(item)) {
            holder.mTv_size.setVisibility(View.VISIBLE);
            holder.mTv_size.setText(item);
        } else {
            holder.mTv_size.setVisibility(View.GONE);
        }

    }

    @Override
    public int getItemCount() {
        try {
            if (getItem != null && getItem.size() > 0) {
                return getItem.size();
            } else {
                return 0;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    class GoodsSpecTypeViewHolder extends RecyclerView.ViewHolder {

        private final TextView mTv_size;
        private final RelativeLayout mRl_del;
        private final ImageView mIv_del;

        public GoodsSpecTypeViewHolder(View itemView) {
            super(itemView);
            mTv_size = itemView.findViewById(R.id.tv_size);
            mRl_del = itemView.findViewById(R.id.rl_del);
            mIv_del = itemView.findViewById(R.id.iv_del);
        }
    }

    public void setOnItemDeleteListener(ImageRecylerReduceItemListener itemDeleteListener) {
        mItemDeleteListener = itemDeleteListener;
    }

}

然后就是主界面第二个RecylerView的适配器了,主要记录了输入的数据并保证数据可以正确的携带回去

public class GoodsSpecTypeNumberAdapter extends BaseAdapter {


    public GoodsSpecTypeNumberAdapter(Context context, List list) {
        super(context, list);
    }

    @Override
    public GoodsSpecTypeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new GoodsSpecTypeViewHolder(mInflater.inflate(R.layout.item_goods_spec_number, parent, false));
    }

    @Override
    public void onBindViewHolder(GoodsSpecTypeViewHolder holder, int position) {
        StoreManagerListEntity.SkuListEntity item = getItem(position);


        holder.mEt_number_price.setTag(position * 100 + 1);
        holder.mEt_number_content.setTag(position * 100 + 2);

        holder.mTv_number_name.setText(item.spec);
        if (!TextUtils.isEmpty(item.stock)) {
            holder.mEt_number_content.setText(item.stock);
        }
        if (!TextUtils.isEmpty(item.price)) {
            holder.mEt_number_price.setText(item.price);
        }

        holder.mEt_number_price.addTextChangedListener(new TextSwitcher(holder, "1"));
        holder.mEt_number_content.addTextChangedListener(new TextSwitcher(holder, "2"));
    }

    class GoodsSpecTypeViewHolder extends RecyclerView.ViewHolder {


        private final TextView mTv_number_name;
        private final EditText mEt_number_price;
        private final EditText mEt_number_content;

        GoodsSpecTypeViewHolder(View itemView) {
            super(itemView);
            mTv_number_name = itemView.findViewById(R.id.tv_number_name);
            mEt_number_price = itemView.findViewById(R.id.et_number_price);
            mEt_number_content = itemView.findViewById(R.id.et_number_content);
        }
    }

    class TextSwitcher implements TextWatcher {
        private GoodsSpecTypeViewHolder mHolder;
        private String mType;

        TextSwitcher(GoodsSpecTypeViewHolder mHolder, String type) {
            this.mHolder = mHolder;
            mType = type;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

            if ("1".equals(mType)) {
                int position = (Integer) mHolder.mEt_number_price.getTag();
                ((GoodsSpecActicity) mContext).saveEditData(position, mType, s.toString());
            } else if ("2".equals(mType)) {
                int position = (Integer) mHolder.mEt_number_content.getTag();
                ((GoodsSpecActicity) mContext).saveEditData(position, mType, s.toString());
            }
        }
    }
}

最后贴出来两个挂件,自定义两个接口,主要为了讲清楚思路和逻辑,工具类和基类就不贴在这里了,有兴趣的同学可以下载项目看下。

public interface ImageRecylerReduceItemListener {
    void onReduceItemListener(int position);
}

public interface RecylerViewAddItemListener {
    void onAddItemListener(List entity, int position);
}

文章就到这里,欢迎指出错误或者修改优化意见。

你可能感兴趣的:(Android电商类项目添加多组物品规格)