效果如下:
说明:
实现sku的方式一般采用在获取到数据后拆分所有条件的可能性,实现方式参考js的实现,代码如下:
SkuHelp.kt
class SkuHelp {
private val skuArray = "skuArray"
fun transformToBean(goodsSpecArray: List<SpecificationsBean>): Pair<Map<String, String>, List<Map<String, String>>> {
val rootArray = mutableListOf<MutableMap<String, String>>()
val tagImgMap = mutableMapOf<String, String>()
goodsSpecArray.forEach { bean ->
if (bean.id != null && bean.goodsSpecValue != null) {
val valueMap = bean.goodsSpecValue.mapFromJson<String, String>()
valueMap["id"] = bean.id
valueMap.forEach { (k, v) ->
if (v.contains(",")) {
val splitArray = v.split(',')
valueMap[k] = splitArray[0]
tagImgMap[splitArray[0]] = splitArray[1]
}
}
rootArray.add(valueMap)
}
}
return Pair(tagImgMap, rootArray)
}
fun initData(rootArray: List<Map<String, String>>): Triple<Map<String, List<String>>, Map<String, Map<String, List<String>>>, List<String>> {
val keyArray = getSkuKey(rootArray)
val (allKeyArray, resultMap) = combineAttr(rootArray, keyArray)
val conditionMap = buildResult(allKeyArray)
return Triple(resultMap, conditionMap, keyArray)
}
fun trimSplit(trim: String): String {
val reLeft = Regex("^$spliter+")
val reRight = Regex("$spliter+\$")
val reSplit = Regex("$spliter+")
return trim.replace(reLeft, "")
.replace(reRight, "")
.replace(reSplit, spliter)
}
fun getSkuArray(condition: String, conditionMap: Map<String, Map<String, List<String>>>): List<String>? {
val newMap = conditionMap[condition]
return newMap?.get(skuArray)
}
private fun getSkuKey(rootArray: List<Map<String, String>>): List<String> {
val keyArray = mutableListOf<String>()
if (rootArray.isNotEmpty()) {
val valueMap = rootArray[0]
valueMap.forEach { (k, _) ->
if (k == "id") {
return@forEach
}
keyArray.add(k)
}
}
return keyArray
}
val spliter = "\u2299"
private fun combineAttr(
rootArray: List<Map<String, String>>,
keyArray: List<String>
): Pair<List<MutableMap<String, String>>, MutableMap<String, MutableList<String>>> {
val resultMap = mutableMapOf<String, MutableList<String>>()
val allKeyArray = mutableListOf<MutableMap<String, String>>()
for (itemMap in rootArray) {
val valueArray = mutableListOf<String>()
for (key in keyArray) {
val array = mutableListOf<String>()
if (resultMap[key] != null) {
array.addAll(resultMap[key]!!)
}
if (!array.contains(itemMap[key])) {
array.add(itemMap[key]!!)
}
resultMap[key] = array
valueArray.add(itemMap[key]!!)
}
allKeyArray.add(
mutableMapOf(
"path" to valueArray.joinToString(separator = spliter),
"sku" to itemMap["id"]!!
)
)
}
return Pair(allKeyArray, resultMap)
}
private fun getAllKeys(allKeyArray: List<Map<String, String>>): List<String> {
val keyArray = mutableListOf<String>()
allKeyArray.forEach { keyMap ->
keyArray.add(keyMap["path"]!!)
}
return keyArray
}
private fun buildResult(allKeyArray: List<Map<String, String>>): MutableMap<String, MutableMap<String, MutableList<String>>> {
val allKeys = getAllKeys(allKeyArray)
val resMap = mutableMapOf<String, MutableMap<String, MutableList<String>>>()
allKeys.forEachIndexed { index, allKey ->
val sku = allKeyArray[index]["sku"]
val values = allKey.split(spliter)
val allSets = powerSet(values)
allSets.forEachIndexed { _, set ->
val key = set.joinToString(separator = spliter)
if (resMap[key] != null) {
resMap[key]?.get(skuArray)?.add(sku!!)
} else {
resMap[key] = mutableMapOf(
skuArray to mutableListOf(sku!!)
)
}
}
}
return resMap
}
private fun powerSet(set: List<String>): List<List<String>> {
val size = 2 shl set.size
val powerSet: MutableList<List<String>> = ArrayList(size)
powerSet.add(Collections.emptyList())
for (element in set) {
val preSize = powerSet.size
for (i in 0 until preSize) {
val combineSubset: MutableList<String> = ArrayList(powerSet[i])
combineSubset.add(element)
powerSet.add(combineSubset)
}
}
return powerSet
}
}
SpecificationsDialog.kt
class SpecificationsDialog(private val contextX: Context) :
AlertDialog(contextX, R.style.DialogWindowStyle_Shop_bg) {
private val binding: PdDialogLayoutSpecificationsBinding =
PdDialogLayoutSpecificationsBinding.inflate(layoutInflater)
private val adapter: SpecificationsDialogAdapter = SpecificationsDialogAdapter()
private val mapModel = mutableMapOf<String, SpecificationsBean>()
private var totalConditions = 0
private val skuBeanArray = mutableListOf<ShowSKUBean>()
private val cacheValueMap = LinkedHashMap<String, String>()
private val cacheConditionMap: MutableMap<String, Map<String, List<String>>> = mutableMapOf()
private val keysArray: MutableList<String> = mutableListOf()
private var cacheModel: SpecificationsBean? = null
private val skuHelp = SkuHelp()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initDefWindows()
initView()
initTestData()
binding.pdTvStock.text = "1000"
}
private fun resetData() {
cacheModel = null
totalConditions = 0
keysArray.clear()
cacheConditionMap.clear()
cacheValueMap.clear()
skuBeanArray.clear()
mapModel.clear()
}
fun initData(beanArray: List<SpecificationsBean>) {
resetData()
beanArray.forEachIndexed { index, bean ->
if (index == 0) {
cacheModel = bean
}
mapModel[bean.id!!] = bean
}
val (tagImgMap, goodsSpecMap) = skuHelp.transformToBean(beanArray)
val (uiMap, conditionMap, keysArray) = skuHelp.initData(goodsSpecMap)
this.keysArray.addAll(keysArray)
totalConditions = uiMap.size
uiMap.forEach { (k, vList) ->
val dvList = vList.map { v ->
if (tagImgMap[v] == null) {
DialogSpecsValue(value = v)
} else {
DialogSpecsValue(value = v, img = tagImgMap[v])
}
}
skuBeanArray.add(ShowSKUBean(k, dvList))
}
cacheConditionMap.putAll(conditionMap)
defSelected()
adapter.setList(skuBeanArray)
}
private fun defSelected() {
cacheModel?.let { bean ->
if (bean.goodsSpecValue == null) return@let
val valueMap = bean.goodsSpecValue.mapFromJson<String, String>()
keysArray.forEach { key ->
val value = valueMap[key] ?: return@forEach
if (value.contains(",")) {
val splitArray = value.split(',')
cacheValueMap[key] = splitArray[0]
} else {
cacheValueMap[key] = value
}
}
defSettingSelected()
}
}
private fun initTestData() {
val goodsSpecArray = mutableListOf(
SpecificationsBean(
"1",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 85.6f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"蓝色\",\"尺码\":\"X\",\"风格\":\"时尚\"}"
),
SpecificationsBean(
"2",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 87.2f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"蓝色\",\"尺码\":\"X\",\"风格\":\"简约\"}"
),
SpecificationsBean(
"3",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 87.2f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"蓝色\",\"尺码\":\"X\",\"风格\":\"欧式\"}"
),
SpecificationsBean(
"4",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 67.2f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"蓝色\",\"尺码\":\"M\",\"风格\":\"简约\"}"
),
SpecificationsBean(
"5",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 57.2f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"蓝色\",\"尺码\":\"M\",\"风格\":\"欧式\"}"
),
SpecificationsBean(
"6",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 87.2f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"红色\",\"尺码\":\"X\",\"风格\":\"时尚\"}"
),
SpecificationsBean(
"7",
"0",
goodsDefaluePrice = 100f,
goodsPrice = 83.2f,
goodsNums = 10000,
goodsSpecValue = "{\"颜色\":\"红色\",\"尺码\":\"M\",\"风格\":\"时尚\"}"
),
)
resetData()
goodsSpecArray.forEachIndexed { index, bean ->
if (index == 0) {
cacheModel = bean
}
mapModel[bean.id!!] = bean
}
val (tagImgMap, goodsSpecMap) = skuHelp.transformToBean(goodsSpecArray)
val (uiMap, conditionMap, keysArray) = skuHelp.initData(goodsSpecMap)
this.keysArray.addAll(keysArray)
totalConditions = uiMap.size
uiMap.forEach { (k, vList) ->
val dvList = vList.map { v ->
if (tagImgMap[v] == null) {
DialogSpecsValue(value = v)
} else {
DialogSpecsValue(value = v, img = tagImgMap[v])
}
}
skuBeanArray.add(ShowSKUBean(k, dvList))
}
cacheConditionMap.putAll(conditionMap)
defSelected()
adapter.setList(skuBeanArray)
}
private fun initView() {
binding.pdItvBack.setOnClickListener { dismiss() }
binding.pdRvSpecs.adapter = adapter
val layoutManager = LinearLayoutManager(contextX)
layoutManager.orientation = LinearLayoutManager.VERTICAL
binding.pdRvSpecs.layoutManager = layoutManager
adapter.setItemClick { holder, p, dialogSpecsValue, key ->
if (!dialogSpecsValue.isSelect) {
cacheValueMap[key] = dialogSpecsValue.value
} else {
cacheValueMap.remove(key)
}
settingSelected(key, dialogSpecsValue)
adapter.notifyDataSetChanged()
if (this.totalConditions == cacheValueMap.size) {
val synthesisKey = obtainConditionSplicing()
val valueArray = skuHelp.getSkuArray(synthesisKey, cacheConditionMap)
var id = ""
if (valueArray.isNullOrEmpty()) {
"无此属性搭配".showToast()
return@setItemClick
} else {
id = valueArray[0]
}
cacheModel = mapModel[id]
Log.e("fyc", " bean >>>> $cacheModel")
} else {
cacheModel = null
}
}
}
private fun initDefWindows() {
setCanceledOnTouchOutside(true)
setCancelable(true)
val localWindow = this.window
localWindow?.setWindowAnimations(R.style.DialogWindowStyle)
localWindow?.setGravity(Gravity.BOTTOM)
localWindow?.setBackgroundDrawableResource(android.R.color.transparent)
val lp = localWindow!!.attributes
val wh =
intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
lp.width = wh[0]
lp.height = wh[1]
localWindow.attributes = lp
}
private fun updateStatus() {
val cacheValueArray = mutableListOf<String>()
cacheValueMap.forEach { (_, v) ->
cacheValueArray.add(v)
}
skuBeanArray.forEachIndexed { i, bean ->
val copyArray = Array(totalConditions){""}
for (z in 0..totalConditions) {
if (z < cacheValueArray.size) {
copyArray[z] = cacheValueArray[z]
}
}
bean.valueList.forEach valueTag@ { valueBean ->
if (cacheValueMap[bean.key] == valueBean.value) {
return@valueTag
}
Log.e("fyc", "${bean.key} : ${valueBean.value}")
copyArray[i] = valueBean.value
val conditionKey = skuHelp.trimSplit(copyArray.joinToString(separator = skuHelp.spliter))
if (cacheConditionMap[conditionKey] == null) {
valueBean.isCanSelect = false
valueBean.isSelect = false
} else {
valueBean.isCanSelect = true
valueBean.isSelect = false
}
}
}
}
private fun settingSelected(key: String, bean: DialogSpecsValue) {
skuBeanArray.forEach { model ->
val selectionVal = cacheValueMap[model.key]
model.valueList.forEach { dialogSpecsValue ->
if (selectionVal == dialogSpecsValue.value) {
dialogSpecsValue.isSelect = true
}
if (selectionVal == dialogSpecsValue.value && !dialogSpecsValue.isCanSelect) {
cacheValueMap.clear()
cacheValueMap[key] = bean.value
dialogSpecsValue.isSelect = true
dialogSpecsValue.isCanSelect = true
}
}
}
val selectMap = LinkedHashMap<String, String>()
keysArray.forEach { dataKey ->
if (cacheValueMap[dataKey] != null) {
selectMap[dataKey] = cacheValueMap[dataKey]!!
}
}
cacheValueMap.clear()
cacheValueMap.putAll(selectMap)
updateStatus()
}
private fun defSettingSelected() {
skuBeanArray.forEach { model ->
val selectionVal = cacheValueMap[model.key]
model.valueList.forEach { dialogSpecsValue ->
if (selectionVal == dialogSpecsValue.value) {
dialogSpecsValue.isSelect = true
}
}
}
updateStatus()
}
private fun obtainConditionSplicing(): String {
val splicingArray = mutableListOf<String>()
cacheValueMap.forEach { (_, v) ->
splicingArray.add(v)
}
return splicingArray.joinToString(separator = skuHelp.spliter)
}
}
SpecificationsBean.kt
@JsonClass(generateAdapter=true)
data class SpecificationsBean(
val id: String?,
val goodsId: String?,
val goodsSpecValue: String?,
val goodsPrice: Float?,
val goodsDefaluePrice: Float?,
val goodsNums: Int?
)
ShowSKUBean.kt
data class ShowSKUBean(
val key: String,
val valueList: List<DialogSpecsValue>
)
DialogSpecsValue.kt
data class DialogSpecsValue(
var isCanSelect: Boolean = true,
var isSelect: Boolean = false,
var value: String = "",
var img: String? = null
)
总结:代码比较多,需要给位耐心看完,可以实现图片中一样的效果,懒得废话太多,sku实现的思路并不复杂,麻烦的是如何写算法处理,此处只展示一种sku实现方式,这种方式的弊端是吃内存,但至少效果可以实现,一般来讲后端会对应作出限制,理论上内存这块不用太过于担心,由于我的数据中如:‘颜色:蓝色,/static/x/x.jpg’ value可能携带图片此处处理方式是针对于需要图片的方式,如果不需要处理图片,直接将图片处理的代码删除即可,希望本文对大家有所帮助。
|