feat: update claude md file

This commit is contained in:
2025-11-26 09:39:20 +08:00
parent 938f7dca32
commit 8a30f0079a
6 changed files with 269 additions and 9 deletions

View File

@@ -20,7 +20,8 @@
"Bash(xargs -I {} sh -c 'echo \"\"\"\"=== {} ===\"\"\"\" && jar tf {} 2>/dev/null | grep -i \"\"\"\"gprinter\"\"\"\" | head -5')",
"Bash(xmllint:*)",
"Bash(xargs cat:*)",
"mcp__chrome-devtools__evaluate_script"
"mcp__chrome-devtools__evaluate_script",
"WebSearch"
],
"deny": [],
"ask": []

253
CLAUDE.md
View File

@@ -173,6 +173,256 @@ suspend fun getXxxDetails(@Query("id") id: String): BaseResultBean<XxxBean>
suspend fun saveXxx(@Body data: RequestBody): BaseResultBean<SimpleResultBean>
```
## DataBinding + LiveData + ViewModel 核心知识
### 🎯 最关键的设置(最常见错误)
**必须在 Activity 中设置 lifecycleOwner否则 XML 中的 LiveData 不会自动更新 UI**
```kotlin
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("页面标题")
binding.viewModel = viewModel
// ⚠️ 关键:必须设置,否则 LiveData 无法自动更新 UI
binding.lifecycleOwner = this
}
```
**BaseBindingActivity 已自动设置**,但如果手动使用 DataBinding 时务必记住!
### 📖 ViewModel 中 LiveData 的定义规范
```kotlin
class XxxViewModel : BaseViewModel() {
// ✅ 推荐:对外暴露不可变的 LiveData
private val _dataBean = MutableLiveData<XxxBean>()
val dataBean: LiveData<XxxBean> = _dataBean
// ✅ 简化写法:直接使用 MutableLiveData项目常用
val searchText = MutableLiveData<String>()
val pageType = MutableLiveData(DetailsPageType.Add)
fun loadData() {
// 主线程更新
_dataBean.value = XxxBean()
// 子线程更新(协程中不需要,已在主线程)
// _dataBean.postValue(XxxBean())
}
}
```
### 📝 XML 中 LiveData 的绑定方式
#### 1. 单向绑定 `@{}`只显示ViewModel → UI
```xml
<layout>
<data>
<variable
name="viewModel"
type="com.lukouguoji.xxx.XxxViewModel" />
</data>
<!-- LiveData 自动解包:直接访问 value -->
<TextView
android:text="@{viewModel.dataBean.name}" />
<!-- 条件判断 -->
<View
android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" />
<!-- 空值处理 -->
<TextView
android:text="@{viewModel.dataBean.name ?? `默认值`}" />
<!-- 字符串拼接(使用反引号) -->
<TextView
android:text="@{`姓名:` + viewModel.dataBean.name}" />
</layout>
```
#### 2. 双向绑定 `@={}`可编辑UI ↔ ViewModel
```xml
<!-- EditText 双向绑定 -->
<EditText
android:text="@={viewModel.searchText}" />
<!-- PadSearchLayout 双向绑定 -->
<PadSearchLayout
type="@{SearchLayoutType.INPUT}"
value="@={viewModel.waybillNo}" />
<!-- PadDataLayout 双向绑定 -->
<PadDataLayout
type="@{DataLayoutType.INPUT}"
value="@={viewModel.dataBean.name}" />
```
**双向绑定要求**
- 字段必须是 `MutableLiveData`
- 用户输入时自动更新 ViewModel 的值
- ViewModel 更新值时自动更新 UI
#### 3. 点击事件绑定
```xml
<!-- Lambda 表达式(推荐) -->
<Button
android:onClick="@{() -> viewModel.submit()}" />
<!-- 带参数 -->
<Button
android:onClick="@{(v) -> viewModel.onItemClick(v, 1)}" />
<!-- 自定义监听器 -->
<PadSearchLayout
setOnIconClickListener="@{(v) -> viewModel.scanWaybill()}" />
```
### ⚠️ DataBinding 常见错误与解决方法
#### 错误 1忘记设置 lifecycleOwner
```kotlin
// ❌ 错误LiveData 变化但 UI 不更新
override fun initOnCreate(savedInstanceState: Bundle?) {
binding.viewModel = viewModel
// 忘记设置 lifecycleOwner
}
// ✅ 正确:必须设置
override fun initOnCreate(savedInstanceState: Bundle?) {
binding.viewModel = viewModel
binding.lifecycleOwner = this // 关键!
}
```
#### 错误 2在 XML 中传递 LiveData 而非值
```xml
<!-- ❌ 错误:某些属性只接受值,不接受 LiveData -->
<View
android:visibility="@{viewModel.isVisible}" />
<!-- 如果 isVisible 是 MutableLiveData<Boolean>,可能报错 -->
<!-- ✅ 正确DataBinding 会自动解包 LiveData -->
<View
android:visibility="@{viewModel.isVisible ? View.VISIBLE : View.GONE}" />
<!-- 三元表达式会自动解包 -->
```
#### 错误 3import 类型错误
```xml
<!-- ❌ 错误 -->
<import type="android.view.View.VISIBLE" />
<!-- ✅ 正确 -->
<import type="android.view.View" />
```
#### 错误 4字符串未使用反引号
```xml
<!-- ❌ 错误:普通引号会被识别为 XML 属性 -->
<TextView
android:text="@{"姓名:" + viewModel.name}" />
<!-- ✅ 正确:使用反引号 ` -->
<TextView
android:text="@{`姓名:` + viewModel.name}" />
```
#### 错误 5访问 LiveData 的 value 属性
```xml
<!-- ❌ 错误DataBinding 会自动解包,不需要 .value -->
<TextView
android:text="@{viewModel.dataBean.value.name}" />
<!-- ✅ 正确:直接访问 -->
<TextView
android:text="@{viewModel.dataBean.name}" />
```
#### 错误 6修改对象属性后 UI 不更新
```kotlin
// ❌ 错误修改对象内部属性LiveData 不会触发更新
val bean = dataBean.value
bean?.name = "新名称"
// UI 不会更新,因为 LiveData 的引用没变
// ✅ 正确:重新赋值 LiveData
val bean = dataBean.value?.copy(name = "新名称")
dataBean.value = bean
// ✅ 或者:使用 MutableLiveData + ObservableField
// 但项目中更推荐上面的方式
```
#### 错误 7在 XML 中调用 suspend 函数
```xml
<!-- ❌ 错误suspend 函数不能直接在 XML 中调用 -->
<Button
android:onClick="@{() -> viewModel.loadDataSuspend()}" />
<!-- ✅ 正确:在 ViewModel 中包装 -->
```
```kotlin
// ViewModel 中
fun loadData() { // 普通函数
launchLoadingCollect({ NetApply.api.getXxx() }) {
onSuccess = { dataBean.value = it.data }
}
}
```
### 🔍 XML DataBinding 调试技巧
#### 1. 检查 Binding 类是否生成
```bash
# 清理重新构建
./gradlew clean
./gradlew assembleDebug
```
#### 2. 查看 DataBinding 错误
- XML 中的错误可能不会立即显示
- 需要 Build 项目才能看到详细错误信息
- 错误信息通常在 Build Output 中
#### 3. 常见错误提示
```
Cannot find the setter for attribute 'android:text' with parameter type...
→ 检查属性类型是否匹配
Unresolved reference: viewModel
→ 检查 <variable> 声明和 import
cannot generate view binders
→ 检查 XML 语法错误,特别是 @{} 表达式
```
### 📋 DataBinding 开发检查清单
- ✅ Activity 中设置 `binding.lifecycleOwner = this`
- ✅ ViewModel 中需要双向绑定的字段使用 `MutableLiveData`
- ✅ XML 中字符串使用反引号 `` ` ``
- ✅ XML 中不访问 LiveData 的 `.value` 属性
- ✅ 修改对象属性后重新赋值 LiveData触发更新
- ✅ 点击事件使用 Lambda 表达式
- ✅ 正确 import 枚举和常量类
- ✅ XML 错误需要 Build 项目才能看到
## 核心UI组件
### PadSearchLayout - 搜索输入框
@@ -574,10 +824,13 @@ grep -A 5 "enum class DataLayoutType" module_base/src --include="*.kt"
## 开发原则
-**资源引用必须存在** - 创建/修改布局前确保drawable/color/string资源真实存在或主动创建
-**必须设置 lifecycleOwner** - Activity 中 `binding.lifecycleOwner = this`BaseBindingActivity 已自动设置)
- ✅ 优先使用项目现有基类和封装
- ✅ 充分利用PadDataLayout和PadSearchLayout组件
- ✅ 遵循统一命名规范
- ✅ pageType用LiveData不用普通变量
- ✅ XML中字符串拼接使用反引号不访问LiveData的.value属性
- ✅ 修改对象属性后重新赋值LiveData才能触发UI更新
- ✅ FlowBus.emit()必须在协程中调用
- ✅ 图片上传使用newName字段
- ✅ RecyclerView手动更新adapter不用items属性

View File

@@ -2,6 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_data_layout_s" android:state_enabled="true" />
<item android:drawable="@drawable/bg_data_layout_n" android:state_enabled="false" />
<item android:drawable="@drawable/bg_data_layout_readonly" android:state_enabled="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#F5F5F5"/>
<corners android:radius="4dp"/>
</shape>

View File

@@ -138,7 +138,7 @@
android:layout_weight="1"
enable="@{false}"
title='@{"特码"}'
titleLength="@{3}"
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.dataBean.spCode}' />
@@ -167,7 +167,7 @@
android:layout_weight="1"
enable="@{false}"
title='@{"航班号"}'
titleLength="@{4}"
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.dataBean.fno}' />
@@ -177,7 +177,7 @@
android:layout_weight="1"
enable="@{false}"
title='@{"航程"}'
titleLength="@{3}"
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.dataBean.range}' />
@@ -206,7 +206,7 @@
android:layout_weight="1"
enable="@{false}"
title='@{"代理人"}'
titleLength="@{4}"
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.dataBean.agentName}' />
@@ -226,7 +226,7 @@
android:layout_weight="1"
enable="@{false}"
title='@{"车牌号"}'
titleLength="@{4}"
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.dataBean.carNumber}' />
@@ -293,7 +293,7 @@
enable="@{false}"
inputHeight="@{80}"
title='@{"备注"}'
titleLength="@{3}"
titleLength="@{5}"
type="@{DataLayoutType.INPUT}"
value='@{viewModel.dataBean.remark}' />
@@ -317,7 +317,7 @@
<!-- 退回按钮 -->
<TextView
style="@style/tv_bottom_btn"
android:layout_marginEnd="10dp"
android:layout_marginEnd="20dp"
android:onClick="@{()-> viewModel.auditReject()}"
android:text="退回" />

View File

@@ -7,4 +7,5 @@
<color name="gjc_teal_700">#FF018786</color>
<color name="gjc_black">#FF000000</color>
<color name="gjc_white">#FFFFFFFF</color>
<color name="color_33">#333333</color>
</resources>