package com.xw.common.utils import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.View import androidx.databinding.DataBindingUtil import androidx.databinding.ObservableField import androidx.databinding.OnRebindCallback import androidx.databinding.ViewDataBinding import androidx.lifecycle.LiveData import com.xw.common.BuildConfig import com.xw.common.R import com.xw.common.databinding.LayoutEmptyView2Binding import com.xw.common.databinding.LayoutEmptyViewBinding import com.xw.common.databinding.LayoutFootViewBinding import org.json.JSONArray import org.json.JSONObject import java.lang.reflect.Field import java.lang.reflect.Modifier import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter @SuppressLint("DefaultLocale") object BindUtils { fun getEmptyView2(context: Context, tips: String = "空空如也~"): View { val itemBind = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.layout_empty_view2, null, false ) itemBind.tips.text = tips return itemBind.root } fun getEmptyView(context: Context, tips: String = "空空如也~"): View { val itemBind = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.layout_empty_view, null, false ) itemBind.tips.text = tips return itemBind.root } fun getFootView(context: Context, tips: String = "已加载全部任务~"): View { val itemBind = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.layout_foot_view, null, false ) itemBind.tips.text = tips return itemBind.root } @JvmStatic fun getResColor(color: Int): Int { return AppGlobals.getApplication().resources.getColor(color) } fun getNoNullValue(input: Double?): String { return "${input ?: "0"}" } fun getNoNullValue(input: Float?): String { return "${input ?: "0"}" } fun getNoNullValue(input: Int?): String { return "${input ?: "0"}" } fun getNoNullValue(input: String?, default: String): String { return input ?: default } fun showYMD(input: String?): String { return try { formatDate(input).substring(0, 11) } catch (e: Exception) { input ?: "" } } fun showDay(input: String?): String { return try { input?.substring(8, 10)?.toIntOrNull()?.toString() ?: "" } catch (e: Exception) { input ?: "" } } fun formatDate(input: String?): String { return try { val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val outputFormatterDate = DateTimeFormatter.ofPattern("yyyy年MM月dd日") val outputFormatterDateTime = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") when (input?.length) { 10 -> { LocalDate.parse(input, dateFormatter).format(outputFormatterDate) } 19 -> { LocalDateTime.parse(input, dateTimeFormatter).format(outputFormatterDateTime) } else -> input ?: "" } } catch (_: Exception) { input ?: "" } } fun String.getUrl(): String { return this.getUrls().getOrNull(0) ?: "" } // fun String.getUrls(): List { // val urls = mutableListOf() // try { // val trimmed = this.trim() // if (trimmed.startsWith("[")) { // // 多个:JSONArray // val jsonArray = JSONArray(trimmed) // for (i in 0 until jsonArray.length()) { // val obj = jsonArray.getJSONObject(i) // urls.add(obj.optString("url").trim()) // } // } else { // // 单个:JSONObject // val jsonObject = JSONObject(trimmed) // urls.add(jsonObject.optString("url").trim()) // } // } catch (e: Exception) { // // } // return urls // } fun String.getUrls() = getValueList("url") fun String.getValueList(key: String): List { val urls = mutableListOf() try { val trimmed = this.trim() if (trimmed.startsWith("[")) { // 多个:JSONArray val jsonArray = JSONArray(trimmed) for (i in 0 until jsonArray.length()) { val item = jsonArray.get(i) val jsonObj = when (item) { is JSONObject -> item is String -> JSONObject(item) // 处理转义的JSON字符串 else -> continue } urls.add(jsonObj.optString(key).trim()) } } else { // 单个:JSONObject 或 转义的JSON字符串 val jsonObj = if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) { // 是被引号包裹的JSON字符串,先去掉外层引号再解析 JSONObject(trimmed.substring(1, trimmed.length - 1).replace("\\\"", "\"")) } else { JSONObject(trimmed) } urls.add(jsonObj.optString(key).trim()) } } catch (e: Exception) { e.printStackTrace() } return urls } /** * 为 ViewDataBinding 添加日志回调,在每次绑定执行后打印 ViewModel 中所有 LiveData 的当前值 * 仅在 Debug 构建中启用 */ fun ViewDataBinding.enableBindingLogging(tag: String = "zmc-DataBinding") { if (BuildConfig.DEBUG) { addOnRebindCallback(object : OnRebindCallback() { override fun onBound(binding: ViewDataBinding) { super.onBound(binding) logViewModelFields(binding, tag) } }) } } /** * 通过反射获取 Binding 中的 ViewModel,并打印其内部所有 LiveData 字段的当前值 */ private fun logViewModelFields(binding: ViewDataBinding, tag: String) { try { // 获取包括父类在内的所有字段 val allFields = getAllFields(binding.javaClass) // 查找 ViewModel 字段(支持多种命名方式) val viewModelField = allFields.firstOrNull { field -> field.name == "mViewModel" || field.name == "viewModel" || field.type.simpleName.contains("ViewModel") } ?: run { LogUtils.d(tag, "No ViewModel field found in binding") return } viewModelField.isAccessible = true val viewModel = viewModelField.get(binding) ?: run { LogUtils.d(tag, "ViewModel is null") return } LogUtils.d(tag, "---------- DataBinding Variables ----------") // 获取 ViewModel 的所有字段(包括父类) val viewModelFields = getAllFields(viewModel.javaClass) viewModelFields.forEach { field -> // 跳过静态字段、合成字段 if (Modifier.isStatic(field.modifiers) || field.isSynthetic) return@forEach field.isAccessible = true val value = field.get(viewModel) when (value) { is LiveData<*> -> { val currentValue = value.value LogUtils.d(tag, "LiveData [${field.name}] = $currentValue") } is ObservableField<*> -> { LogUtils.d(tag, "LiveData [${field.name}] = $value") } } } } catch (e: Exception) { LogUtils.e(tag, "Failed to log binding variables", e) } } /** * 递归获取包括父类在内的所有字段 */ private fun getAllFields(clazz: Class<*>): List { val fields = mutableListOf() var currentClass: Class<*>? = clazz while (currentClass != null && currentClass != Any::class.java) { fields.addAll(currentClass.declaredFields) currentClass = currentClass.superclass } return fields } }