# UI 层封装方案

## 一、设计目标

将 UI 层的通用功能封装成基类和扩展函数，统一处理公共逻辑，业务层只需简单调用，减少重复代码。

## 二、架构设计

### 2.1 现有组件

- ✅ `BaseActivity` - 基础 Activity 类（已存在）
- ✅ `BaseFragment` - 基础 Fragment 类（已存在）
- ✅ `ActivityExtensions` - Activity 扩展函数（已存在）
- ✅ `FragmentExtensions` - Fragment 扩展函数（已存在）

### 2.2 核心原则

- **职责分离**：UI 层只负责显示，不处理网络请求
- **MVVM 架构**：网络请求在 ViewModel 中处理，Activity 只观察状态
- **统一封装**：通用 UI 功能统一封装，减少重复代码
- **易于扩展**：子类可重写方法自定义实现

## 三、目录结构

```
base-common/src/main/java/com/narutohuo/xindazhou/common/
└── ui/                      # UI 层封装
    ├── BaseActivity.kt       # 基础 Activity 类
    ├── BaseFragment.kt      # 基础 Fragment 类
    ├── ActivityExtensions.kt    # Activity 扩展函数
    └── FragmentExtensions.kt    # Fragment 扩展函数
```

## 四、功能说明

### 4.1 BaseActivity - 基础 Activity 类

**职责**：封装 Activity 通用 UI 功能

**功能**：
- ✅ **ViewBinding 支持**：自动处理 ViewBinding 初始化
- ✅ **统一的加载状态管理**：`showLoading()` / `hideLoading()`
- ✅ **统一的错误提示**：`showError()` / `showSuccess()`
- ✅ **生命周期管理**：自动调用 `initView()` 和 `initObserver()`
- ✅ **网络请求封装**：`executeRequest()` 方法（可选，简单场景使用）

**使用方式**：
```kotlin
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
    
    private val viewModel: LoginViewModel by viewModels()
    
    override fun getViewBinding(): ActivityLoginBinding {
        return ActivityLoginBinding.inflate(layoutInflater)
    }
    
    override fun initView() {
        binding.btnLogin.setOnClickListener {
            viewModel.login(mobile, password)
        }
    }
    
    override fun initObserver() {
        observeStateFlow(viewModel.loginState) { state ->
            when (state) {
                is LoginState.Loading -> showLoading()
                is LoginState.Success -> {
                    hideLoading()
                    showSuccess(state.message)
                }
                is LoginState.Error -> {
                    hideLoading()
                    showError(state.message)
                }
            }
        }
    }
}
```

### 4.2 BaseFragment - 基础 Fragment 类

**职责**：封装 Fragment 通用 UI 功能

**功能**：
- ✅ **ViewBinding 支持**：自动处理 ViewBinding 初始化
- ✅ **统一的加载状态管理**：`showLoading()` / `hideLoading()`
- ✅ **统一的错误提示**：`showError()` / `showSuccess()`
- ✅ **生命周期管理**：自动调用 `initView()` 和 `initObserver()`
- ✅ **网络请求封装**：`executeRequest()` 方法（可选，简单场景使用）

**使用方式**：
```kotlin
class LoginFragment : BaseFragment<FragmentLoginBinding>() {
    
    private val viewModel: LoginViewModel by viewModels()
    
    override fun getViewBinding(
        inflater: LayoutInflater,
        container: ViewGroup?
    ): FragmentLoginBinding {
        return FragmentLoginBinding.inflate(inflater, container, false)
    }
    
    override fun initView() {
        binding.btnLogin.setOnClickListener {
            viewModel.login(mobile, password)
        }
    }
    
    override fun initObserver() {
        observeStateFlow(viewModel.loginState) { state ->
            when (state) {
                is LoginState.Loading -> showLoading()
                is LoginState.Success -> {
                    hideLoading()
                    showSuccess(state.message)
                }
                is LoginState.Error -> {
                    hideLoading()
                    showError(state.message)
                }
            }
        }
    }
}
```

### 4.3 ActivityExtensions - Activity 扩展函数

**职责**：提供便捷的扩展函数，简化业务层代码

**功能**：
- ✅ **观察 StateFlow**：`observeStateFlow()` 简化状态观察

**使用方式**：
```kotlin
// 之前：繁琐
override fun initObserver() {
    lifecycleScope.launch {
        viewModel.loginState.collect { state ->
            when (state) { ... }
        }
    }
}

// 现在：简洁
override fun initObserver() {
    observeStateFlow(viewModel.loginState) { state ->
        when (state) { ... }
    }
}
```

### 4.4 FragmentExtensions - Fragment 扩展函数

**职责**：提供便捷的扩展函数，简化业务层代码

**功能**：
- ✅ **观察 StateFlow**：`observeStateFlow()` 简化状态观察

**使用方式**：同 ActivityExtensions

## 五、完整 MVVM 架构流程

### 5.1 数据流向

```
用户操作（点击按钮）
    ↓
Activity/Fragment（UI 层）
    ↓ 调用
ViewModel（业务逻辑层）
    ↓ 调用
Repository（数据管理层）
    ↓ 调用
RemoteDataSource（网络请求层）
    ↓ 返回数据
Repository
    ↓ 返回数据
ViewModel（更新 UI 状态）
    ↓ 状态变化
Activity/Fragment（观察状态，调用 BaseActivity 方法更新 UI）
```

### 5.2 完整示例

#### 1. ViewModel 层（处理业务逻辑）

```kotlin
// LoginViewModel.kt
class LoginViewModel(
    application: Application,
    private val repository: AuthRepository
) : AndroidViewModel(application) {
    
    private val _loginState = MutableStateFlow<LoginState>(LoginState.Idle)
    val loginState: StateFlow<LoginState> = _loginState
    
    fun login(mobile: String, password: String) {
        viewModelScope.launch {
            _loginState.value = LoginState.Loading
            
            repository.login(mobile, password)
                .onSuccess {
                    _loginState.value = LoginState.Success("登录成功")
                }
                .onFailure { e ->
                    _loginState.value = LoginState.Error(e.message ?: "登录失败")
                }
        }
    }
}
```

#### 2. Activity 层（只负责显示）

```kotlin
// LoginActivity.kt
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
    
    private val viewModel: LoginViewModel by viewModels()
    
    override fun getViewBinding(): ActivityLoginBinding {
        return ActivityLoginBinding.inflate(layoutInflater)
    }
    
    override fun initView() {
        binding.btnLogin.setOnClickListener {
            val mobile = binding.etMobile.text.toString()
            val password = binding.etPassword.text.toString()
            viewModel.login(mobile, password)  // 触发 ViewModel
        }
    }
    
    override fun initObserver() {
        // 使用扩展函数观察状态
        observeStateFlow(viewModel.loginState) { state ->
            when (state) {
                is LoginState.Loading -> showLoading()      // BaseActivity 提供
                is LoginState.Success -> {
                    hideLoading()
                    showSuccess(state.message)              // BaseActivity 提供
                    // 跳转到主页
                }
                is LoginState.Error -> {
                    hideLoading()
                    showError(state.message)                // BaseActivity 提供
                }
            }
        }
    }
}
```

## 六、核心功能详解

### 6.1 ViewBinding 支持

**自动处理**：
- `BaseActivity` 自动初始化 `binding`
- `BaseFragment` 自动初始化 `binding`
- 子类只需实现 `getViewBinding()` 方法

**优势**：
- ✅ 不需要 `findViewById`
- ✅ 类型安全
- ✅ 自动空检查

### 6.2 加载状态管理

**方法**：
- `showLoading()` - 显示加载提示（子类可重写自定义 UI）
- `hideLoading()` - 隐藏加载提示（子类可重写自定义 UI）

**使用场景**：
- 网络请求开始时显示加载
- 网络请求结束时隐藏加载

### 6.3 错误提示管理

**方法**：
- `showError(message: String)` - 显示错误提示（默认 Toast，子类可重写）
- `showSuccess(message: String)` - 显示成功提示（默认 Toast，子类可重写）

**使用场景**：
- 网络请求失败时显示错误
- 操作成功时显示成功提示

### 6.4 状态观察（扩展函数）

**方法**：
- `observeStateFlow(stateFlow, action)` - 观察 StateFlow 状态变化

**优势**：
- ✅ 简化代码：不需要每次都写 `lifecycleScope.launch { flow.collect { } }`
- ✅ 自动生命周期管理：Activity/Fragment 销毁时自动取消观察
- ✅ 一行代码搞定状态观察

### 6.5 网络请求封装（可选）

**方法**：
- `executeRequest(request, onSuccess, onError, showLoading)` - 统一执行网络请求

**使用场景**：
- 简单业务场景，不需要 ViewModel
- 直接调用 Repository 的场景

**注意**：
- ⚠️ 推荐使用 ViewModel 方式（符合 MVVM 架构）
- ⚠️ 简单场景可以使用此方法

## 七、使用示例

### 7.1 推荐方式：使用 ViewModel（MVVM 架构）

```kotlin
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
    
    private val viewModel: LoginViewModel by viewModels()
    
    override fun getViewBinding(): ActivityLoginBinding {
        return ActivityLoginBinding.inflate(layoutInflater)
    }
    
    override fun initView() {
        binding.btnLogin.setOnClickListener {
            viewModel.login(mobile, password)
        }
    }
    
    override fun initObserver() {
        observeStateFlow(viewModel.loginState) { state ->
            when (state) {
                is LoginState.Loading -> showLoading()
                is LoginState.Success -> {
                    hideLoading()
                    showSuccess(state.message)
                }
                is LoginState.Error -> {
                    hideLoading()
                    showError(state.message)
                }
            }
        }
    }
}
```

### 7.2 简单方式：直接使用 Repository（简单场景）

```kotlin
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
    
    private val repository = AuthRepository()
    
    override fun initView() {
        binding.btnLogin.setOnClickListener {
            executeRequest(
                request = { repository.login(mobile, password) },
                onSuccess = { response ->
                    showSuccess("登录成功")
                    // 处理成功逻辑
                }
            )
        }
    }
}
```

### 7.3 自定义 UI 实现

```kotlin
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
    
    // 自定义加载 UI
    override fun showLoading() {
        binding.progressBar.visibility = View.VISIBLE
        binding.btnLogin.isEnabled = false
    }
    
    override fun hideLoading() {
        binding.progressBar.visibility = View.GONE
        binding.btnLogin.isEnabled = true
    }
    
    // 自定义错误提示（使用 Dialog 而不是 Toast）
    override fun showError(message: String) {
        AlertDialog.Builder(this)
            .setTitle("错误")
            .setMessage(message)
            .setPositiveButton("确定", null)
            .show()
    }
}
```

## 八、优势

### 8.1 代码简化

**之前**：
```kotlin
class LoginActivity : AppCompatActivity() {
    private lateinit var binding: ActivityLoginBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        // 初始化视图
        binding.btnLogin.setOnClickListener { ... }
        
        // 观察状态
        lifecycleScope.launch {
            viewModel.loginState.collect { state ->
                when (state) {
                    is LoginState.Loading -> {
                        // 显示加载
                        binding.progressBar.visibility = View.VISIBLE
                    }
                    is LoginState.Success -> {
                        // 隐藏加载
                        binding.progressBar.visibility = View.GONE
                        Toast.makeText(this@LoginActivity, state.message, Toast.LENGTH_SHORT).show()
                    }
                    is LoginState.Error -> {
                        // 隐藏加载
                        binding.progressBar.visibility = View.GONE
                        Toast.makeText(this@LoginActivity, state.message, Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }
    }
}
```

**现在**：
```kotlin
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
    
    override fun getViewBinding() = ActivityLoginBinding.inflate(layoutInflater)
    
    override fun initView() {
        binding.btnLogin.setOnClickListener { ... }
    }
    
    override fun initObserver() {
        observeStateFlow(viewModel.loginState) { state ->
            when (state) {
                is LoginState.Loading -> showLoading()
                is LoginState.Success -> { hideLoading(); showSuccess(state.message) }
                is LoginState.Error -> { hideLoading(); showError(state.message) }
            }
        }
    }
}
```

**代码减少**：约 60%+

### 8.2 统一 UI 风格

- ✅ 所有 Activity 的加载、错误提示保持一致
- ✅ 统一的生命周期管理
- ✅ 统一的代码结构

### 8.3 易于维护

- ✅ 通用功能修改只需改基类
- ✅ 子类可重写方法自定义实现
- ✅ 职责清晰，易于理解

### 8.4 符合 MVVM 架构

- ✅ UI 层只负责显示
- ✅ 业务逻辑在 ViewModel 中处理
- ✅ 网络请求在 Repository 中处理
- ✅ 职责分离，解耦清晰

## 九、注意事项

### 9.1 必须实现的方法

- `getViewBinding()` - 必须实现，返回 ViewBinding 实例

### 9.2 可选实现的方法

- `initView()` - 初始化视图（可选）
- `initObserver()` - 初始化观察者（可选）
- `showLoading()` / `hideLoading()` - 自定义加载 UI（可选）
- `showError()` / `showSuccess()` - 自定义提示 UI（可选）

### 9.3 推荐使用方式

- ✅ **推荐**：使用 ViewModel + StateFlow + `observeStateFlow()` 扩展函数
- ⚠️ **可选**：简单场景可以使用 `executeRequest()` 方法

### 9.4 网络请求处理

- ✅ **推荐**：网络请求在 ViewModel 中处理，Activity 只观察状态
- ⚠️ **可选**：简单场景可以在 Activity 中使用 `executeRequest()` 方法

## 十、完整架构层次

```
UI 层（View）
├── BaseActivity / BaseFragment          # UI 基类
│   ├── ViewBinding 支持
│   ├── 加载状态管理
│   ├── 错误提示
│   ├── 生命周期管理
│   └── executeRequest() 便捷方法（可选）
│
├── ActivityExtensions / FragmentExtensions  # 扩展函数
│   └── observeStateFlow() 简化状态观察
│
└── Activity / Fragment                  # 业务 UI 组件
    └── 继承 BaseActivity / BaseFragment

业务逻辑层（ViewModel）
└── ViewModel                           # 处理业务逻辑
    └── 使用 Repository

数据层（Data）
├── Repository                          # 数据仓库
│   └── 继承 ApiBaseRepository
│
└── RemoteDataSource                    # 远程数据源
    └── 继承 ApiBaseRemoteDataSource
```

## 十一、参考文档

- [网络请求 MVVM 封装方案](../network/网络请求MVVM封装方案.md) - 网络层封装方案
- [Android MVVM 架构指南](https://developer.android.com/topic/architecture)

