# 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() { 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() { 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.Idle) val loginState: StateFlow = _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() { 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() { 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() { 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() { // 自定义加载 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() { 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)