feat: update claude md file
This commit is contained in:
@@ -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
253
CLAUDE.md
@@ -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}" />
|
||||
<!-- 三元表达式会自动解包 -->
|
||||
```
|
||||
|
||||
#### 错误 3:import 类型错误
|
||||
|
||||
```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属性
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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="退回" />
|
||||
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user