参考网上基于邻接矩阵的商品规格选择器算法的java实现,在安卓app里用的,没有包括adapter的部分,就光是矩阵工具类的部分
原理和效果图 都参考这篇博客
数据结构定义
// 1.某类规格
public static class Spec {
public String type;
public List<String> values;
}
例如new Spec("color",["red","blue"])
// 2.某个规格值
public static class OneSpec {
public String type;
public String value;
public int idx = -1;
}
例如new OneSpec("color","red")
// 3.sku对应的规格值
public static class ItemSpecs {
public String sku;
public List<OneSpec> specs = new ArrayList<>();
}
例如new ItemSpecs(skuCode,["red","128G"])
// 4.规格选择器工具类
public class ItemSpecsAdjoinMatrix {
private List<OneSpec> flattenSpecs = new ArrayList<>(); // 所有规格值(构成邻接矩阵的边)
private int[][] matrix; // 邻接矩阵
private Map<String, String> skuMap = new HashMap<>(); // 根据规格值的组合来映射sku
private OneSpec[] selectedSpecArray; // 保存各类规格当前选择的值(每一类至多选一个)
}
构造邻接矩阵
根据可选的sku列表、总的规格列表,构造邻接矩阵
设置行号、设置规格组合sku映射表的时候 感觉自己好机智哈哈
public ItemSpecsAdjoinMatrix(List<ItemSpecs> itemSpecs, List<Spec> specs) {
// 1.初始化为全都不选
selectedSpecArray = new OneSpec[specs.size()];
for (int i = 0; i < specs.size(); i++) {
selectedSpecArray[i] = new OneSpec(specs.get(i).type, "");
}
// 2.把规格值“拍扁”保存,并设置在矩阵中的行号
int idx = 0;
for (Spec spec : specs) {
for (String s : spec.values) {
flattenSpecs.add(new OneSpec(spec.type, s, idx));
idx++;
}
}
// 3.给商品规格值标记行号,便于通过坐标设置矩阵节点的连通性
for (ItemSpecs item : itemSpecs) {
for (OneSpec oneSpec : item.specs) {
setOneSpecIdx(oneSpec);
}
}
// 4.保存规格组合字符串与sku的映射表
String[] skuKey = new String[selectedSpecArray.length];
for (ItemSpecs item : itemSpecs) {
for (OneSpec oneSpec : item.specs) {
for (int i = 0; i < specs.size(); i++) {
if (TextUtils.equals(specs.get(i).type, oneSpec.type)) {
skuKey[i] = oneSpec.value;
}
}
}
StringBuilder str = new StringBuilder();
for (String key : skuKey) str.append(key);
skuMap.put(str.toString(), item.sku);
}
// 5.初始化矩阵,默认同类型的点联通,其他各点都不连通
int n = flattenSpecs.size();
matrix = new int[n][n];
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
matrix[r][c] = 0;
if (TextUtils.equals(flattenSpecs.get(r).type, flattenSpecs.get(c).type)) matrix[r][c] = 1;
}
}
// 6.标记sku对应的联通点
for (ItemSpecs item : itemSpecs) {
for (OneSpec rowSpec : item.specs) {
int r = rowSpec.idx;
for (OneSpec colSpec : item.specs) {
int c = colSpec.idx;
matrix[r][c] = 1;
}
}
}
}
// 其中setOneSpecIdx是根据type和value在flattenSpec找到属性idx
private void setOneSpecIdx(OneSpec oneSpec) {
for (OneSpec oneSpec1 : flattenSpecs) {
if (!TextUtils.equals(oneSpec.type, oneSpec1.type)) continue;
if (!TextUtils.equals(oneSpec.value, oneSpec1.value)) continue;
oneSpec.idx = oneSpec1.idx;
break;
}
}
选择器点击事件处理
// 1.更新已选数组
public String refreshSelectedSpec(OneSpec oneSpec) {
setOneSpecIdx(oneSpec);
for (OneSpec spec : selectedSpecArray) {
if (TextUtils.equals(spec.type, oneSpec.type)) {
if (TextUtils.equals(spec.value, oneSpec.value)) {
// 取消选择
spec.value = "";
spec.idx = -1;
} else {
// 更新选中的规格值
spec.value = oneSpec.value;
spec.idx = oneSpec.idx;
}
break;
}
}
String str = "";
for (OneSpec spec : selectedSpecArray) {
str += spec.value;
}
return skuMap.get(str); //返回选中的skucode
}
// 2.根据sku算法获得每个规格值的enabled状态(如上图)
public int[] getEnabledSpecs() {
int n = flattenSpecs.size();
int[] tmp = new int[n];
for (int i = 0; i < n; i++) tmp[i] = 1;
for (OneSpec spec : selectedSpecArray) {
int c = spec.idx;
if (c == -1) continue;
for (int r = 0; r < n; r++) {
if (matrix[r][c] == 0) {
tmp[r] = 0;
}
}
}
return tmp;
}
然后就给可以根据enabled、selected状态显示控件颜色,根据sku显示商品价格了