| | |
| | | 2. 点击事件监听 |
| | | ```kotlin |
| | | // 方式1:使用类型常量(推荐) |
| | | titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE) { finish() } |
| | | titleBar.setOnItemclickListner(TitleBar.ClickType_RIGHT_TEXT) { showMenu() } |
| | | titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE) { |
| | | finish() |
| | | } |
| | | titleBar.setOnItemclickListner(TitleBar.ClickType_RIGHT_TEXT) { |
| | | showMenu() |
| | | } |
| | | |
| | | // 或者使用完整的OnClickListener |
| | | titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE, View.OnClickListener { |
| | | finish() |
| | | }) |
| | | |
| | | // 方式2:使用位置和类型常量(已废弃) |
| | | titleBar.setOnItemclickListner(TitleBar.IMAGE, TitleBar.LEFT) { finish() } |
| | |
| | | 2. 设置文本或图片时,如果传入null或0,对应的视图将被隐藏 |
| | | 3. 组件默认使用垂直线性布局,确保在布局文件中设置合适的高度 |
| | | |
| | | #### 常见使用示例 |
| | | ```kotlin |
| | | // 在Activity的initView方法中设置 |
| | | private fun initView() { |
| | | // 设置返回按钮点击事件 |
| | | binding.titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE) { |
| | | finish() |
| | | } |
| | | |
| | | // 设置右侧文本按钮点击事件 |
| | | binding.titleBar.setOnItemclickListner(TitleBar.ClickType_RIGHT_TEXT) { |
| | | // 处理右侧按钮点击逻辑 |
| | | handleRightButtonClick() |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## 支付方式动态获取功能 |
| | | |
| | | 项目中实现了支付方式的动态获取和显示功能,支持从服务器获取支付方式列表并动态创建RadioButton。 |
| | | |
| | | ### 数据结构 |
| | | |
| | | ```kotlin |
| | | // 支付方式数据类 |
| | | data class PaymentMethod( |
| | | val id: Long, |
| | | val name: String, |
| | | val remarks: String, |
| | | val deleted: Int |
| | | ) |
| | | |
| | | // 支付方式接口返回数据类 |
| | | data class PaymentMethodResponse( |
| | | val itemTotal: Any?, |
| | | val obj: List<PaymentMethod>, |
| | | val pageCurr: Any?, |
| | | val pageSize: Any?, |
| | | val pageTotal: Any? |
| | | ) |
| | | ``` |
| | | |
| | | ### 使用方式 |
| | | |
| | | #### 1. 在Activity中添加支付方式相关属性 |
| | | |
| | | ```kotlin |
| | | class YourActivity : AppCompatActivity() { |
| | | // 支付方式相关属性 |
| | | private var paymentMethod: String = "现金" |
| | | private var paymentId: Long = 0 |
| | | private var paymentMethodList: List<PaymentMethod> = listOf() |
| | | |
| | | // ... 其他代码 |
| | | } |
| | | ``` |
| | | |
| | | #### 2. 获取支付方式列表 |
| | | |
| | | ```kotlin |
| | | /** |
| | | * 获取支付方式列表 |
| | | */ |
| | | private fun getPaymentMethods() { |
| | | ApiManager.getInstance().requestGetLoading( |
| | | this, |
| | | "sell/paymentmethod/get", |
| | | PaymentMethodResponse::class.java, |
| | | null, |
| | | object : SubscriberListener<BaseResponse<PaymentMethodResponse>>() { |
| | | override fun onNext(response: BaseResponse<PaymentMethodResponse>) { |
| | | if (response.success) { |
| | | val paymentMethods = response.content?.obj ?: listOf() |
| | | if (paymentMethods.isNotEmpty()) { |
| | | paymentMethodList = paymentMethods |
| | | updatePaymentMethodRadioGroup() |
| | | } |
| | | } else { |
| | | Toast.makeText(this@YourActivity, "获取支付方式失败: ${response.msg}", Toast.LENGTH_SHORT).show() |
| | | } |
| | | } |
| | | |
| | | override fun onError(e: Throwable?) { |
| | | super.onError(e) |
| | | Toast.makeText(this@YourActivity, "获取支付方式失败: ${e?.message ?: "网络异常"}", Toast.LENGTH_SHORT).show() |
| | | } |
| | | } |
| | | ) |
| | | } |
| | | ``` |
| | | |
| | | #### 3. 动态创建RadioButton |
| | | |
| | | ```kotlin |
| | | /** |
| | | * 更新支付方式RadioGroup |
| | | */ |
| | | private fun updatePaymentMethodRadioGroup() { |
| | | // 清空原有RadioButton |
| | | binding.paymentMethodRadioGroup.removeAllViews() |
| | | |
| | | // 动态添加RadioButton |
| | | paymentMethodList.forEachIndexed { index, method -> |
| | | val radioButton = RadioButton(this) |
| | | radioButton.id = View.generateViewId() |
| | | radioButton.layoutParams = LinearLayout.LayoutParams(0, resources.getDimensionPixelSize(R.dimen.dimen_40), 1.0f) |
| | | |
| | | // 设置样式 |
| | | radioButton.text = method.name |
| | | radioButton.background = resources.getDrawable(R.drawable.radio_selector) |
| | | radioButton.buttonDrawable = null |
| | | radioButton.gravity = Gravity.CENTER |
| | | radioButton.setTextColor(resources.getColorStateList(R.color.radio_button_text_color)) |
| | | radioButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f) |
| | | |
| | | // 添加到RadioGroup |
| | | binding.paymentMethodRadioGroup.addView(radioButton) |
| | | |
| | | // 默认选中第一个 |
| | | if (index == 0) { |
| | | radioButton.isChecked = true |
| | | paymentMethod = method.name |
| | | paymentId = method.id |
| | | } |
| | | } |
| | | |
| | | // 设置选择监听 |
| | | binding.paymentMethodRadioGroup.setOnCheckedChangeListener { group, checkedId -> |
| | | for (i in 0 until group.childCount) { |
| | | val radioButton = group.getChildAt(i) as RadioButton |
| | | if (radioButton.id == checkedId) { |
| | | paymentMethod = radioButton.text.toString() |
| | | paymentId = paymentMethodList[i].id |
| | | break |
| | | } |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### 4. 布局文件配置 |
| | | |
| | | ```xml |
| | | <RadioGroup |
| | | android:id="@+id/paymentMethodRadioGroup" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="horizontal"> |
| | | <!-- 动态添加RadioButton,不需要预定义 --> |
| | | </RadioGroup> |
| | | ``` |
| | | |
| | | ### 功能特点 |
| | | |
| | | 1. **动态获取**: 从服务器动态获取支付方式列表,支持后台配置 |
| | | 2. **自动布局**: 根据支付方式数量自动调整RadioButton布局 |
| | | 3. **样式统一**: 所有动态创建的RadioButton使用统一的样式 |
| | | 4. **默认选择**: 自动选中第一个支付方式作为默认选项 |
| | | 5. **事件处理**: 支持选择变化监听,实时更新当前选中的支付方式 |
| | | |
| | | ### 注意事项 |
| | | |
| | | 1. 确保在调用`getPaymentMethods()`前已经初始化了相关的UI组件 |
| | | 2. 动态创建的RadioButton需要设置唯一的ID,使用`View.generateViewId()` |
| | | 3. 在Activity销毁时注意清理相关资源,避免内存泄漏 |
| | | 4. 网络请求失败时要有相应的错误处理机制 |
| | | |
| | | ## 注意事项 |
| | | |
| | | 1. 数据库迁移 |
| | |
| | | - 确保异常信息被正确记录和上报 |
| | | - 避免异常信息泄露敏感数据 |
| | | |
| | | 5. API异常处理最佳实践 |
| | | - 针对特定错误码提供友好的用户提示 |
| | | - 区分网络异常、业务异常和系统异常 |
| | | - 异常发生后及时重置UI状态,允许用户重试 |
| | | - 提供明确的操作指引,如"请先进行开卡操作" |
| | | |
| | | ### API异常处理示例 |
| | | |
| | | ```kotlin |
| | | // 在API回调中处理不同类型的异常 |
| | | object : SubscriberListener<BaseResponse<YourDataType>>() { |
| | | override fun onNext(response: BaseResponse<YourDataType>) { |
| | | if (response.success) { |
| | | // 处理成功情况 |
| | | handleSuccess(response.content) |
| | | } else { |
| | | // 处理业务异常 |
| | | handleBusinessError(response.code, response.msg) |
| | | } |
| | | } |
| | | |
| | | override fun onError(e: Throwable?) { |
| | | super.onError(e) |
| | | // 处理网络异常 |
| | | handleNetworkError(e) |
| | | // 重置UI状态 |
| | | resetViewState() |
| | | } |
| | | } |
| | | |
| | | // 业务异常处理方法 |
| | | private fun handleBusinessError(code: String?, msg: String?) { |
| | | when (code) { |
| | | "1081" -> ToastUtil.show("该卡片未在系统中注册,请先进行开卡操作") |
| | | "1001" -> ToastUtil.show("权限不足,请联系管理员") |
| | | "1002" -> ToastUtil.show("账户余额不足") |
| | | else -> { |
| | | val errorMsg = when { |
| | | msg.isNullOrBlank() -> "操作失败,请重试" |
| | | msg.contains("数据不存在") -> "数据不存在,请检查输入信息" |
| | | msg.contains("网络") -> "网络连接异常,请检查网络后重试" |
| | | msg.contains("超时") -> "请求超时,请重试" |
| | | else -> "操作失败: $msg" |
| | | } |
| | | ToastUtil.show(errorMsg) |
| | | } |
| | | } |
| | | // 重置界面状态 |
| | | resetViewState() |
| | | } |
| | | |
| | | // 网络异常处理方法 |
| | | private fun handleNetworkError(e: Throwable?) { |
| | | val errorMsg = when { |
| | | e?.message?.contains("timeout") == true -> "网络请求超时,请检查网络连接" |
| | | e?.message?.contains("network") == true -> "网络连接失败,请检查网络设置" |
| | | e?.message?.contains("host") == true -> "服务器连接失败,请稍后重试" |
| | | else -> "网络异常: ${e?.message ?: "未知错误"}" |
| | | } |
| | | ToastUtil.show(errorMsg) |
| | | } |
| | | |
| | | ### Dialog弹窗使用最佳实践 |
| | | |
| | | 项目中提供了多种Dialog组件,用于不同的交互场景。推荐使用项目已有的Dialog组件来保持UI风格的一致性。 |
| | | |
| | | #### 常用Dialog组件 |
| | | |
| | | 1. **ConfirmDialog**: 确认对话框,用于重要操作的二次确认 |
| | | 2. **TipDialog**: 提示对话框,用于显示提示信息 |
| | | 3. **EdtDialog**: 输入对话框,用于获取用户输入 |
| | | 4. **自定义Dialog**: 继承Dialog类实现特定功能 |
| | | |
| | | #### ConfirmDialog使用示例 |
| | | |
| | | ```kotlin |
| | | // 基本用法 - 只显示消息 |
| | | val dialog = ConfirmDialog(context, "操作成功") |
| | | dialog.show() |
| | | |
| | | // 带标题的用法 |
| | | val dialog = ConfirmDialog(context, "提示", "确认要删除这条记录吗?") { |
| | | // 点击确认按钮的回调 |
| | | deleteRecord() |
| | | dialog.dismiss() |
| | | } |
| | | dialog.show() |
| | | |
| | | // 在异常处理中使用 |
| | | private fun handleError(title: String, message: String) { |
| | | activity?.let { activity -> |
| | | val confirmDialog = ConfirmDialog(activity, title, message) { |
| | | // 点击确认后的操作 |
| | | resetViewState() |
| | | } |
| | | confirmDialog.show() |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### TipDialog使用示例 |
| | | |
| | | ```kotlin |
| | | // 简单提示 |
| | | val tipDialog = TipDialog(context, "操作完成") |
| | | tipDialog.show() |
| | | |
| | | // 带回调的提示 |
| | | val tipDialog = TipDialog(context, "确认退出应用?", object : TipUtil.TipListener { |
| | | override fun onCancle() { |
| | | // 取消操作 |
| | | } |
| | | }) |
| | | tipDialog.show() |
| | | ``` |
| | | |
| | | #### 自定义Dialog最佳实践 |
| | | |
| | | ```kotlin |
| | | class CustomDialog(context: Context) : Dialog(context, R.style.ws_pay_showSelfDialog) { |
| | | |
| | | private lateinit var binding: DialogCustomBinding |
| | | |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | binding = DialogCustomBinding.inflate(layoutInflater) |
| | | setContentView(binding.root) |
| | | |
| | | // 设置对话框属性 |
| | | setupDialog() |
| | | |
| | | // 初始化视图 |
| | | initViews() |
| | | } |
| | | |
| | | private fun setupDialog() { |
| | | // 设置对话框宽度为屏幕宽度的85% |
| | | window?.apply { |
| | | val params = attributes |
| | | params.width = (context.resources.displayMetrics.widthPixels * 0.85).toInt() |
| | | params.height = ViewGroup.LayoutParams.WRAP_CONTENT |
| | | params.gravity = Gravity.CENTER |
| | | attributes = params |
| | | setBackgroundDrawableResource(android.R.color.transparent) |
| | | } |
| | | |
| | | // 设置点击外部不取消 |
| | | setCanceledOnTouchOutside(false) |
| | | } |
| | | |
| | | private fun initViews() { |
| | | binding.btnConfirm.setOnClickListener { |
| | | // 处理确认逻辑 |
| | | dismiss() |
| | | } |
| | | |
| | | binding.btnCancel.setOnClickListener { |
| | | dismiss() |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | #### Dialog使用注意事项 |
| | | |
| | | 1. **内存泄漏防护**: 确保在Activity销毁时关闭Dialog |
| | | ```kotlin |
| | | override fun onDestroy() { |
| | | super.onDestroy() |
| | | dialog?.dismiss() |
| | | } |
| | | ``` |
| | | |
| | | 2. **生命周期管理**: 在Fragment中使用Dialog时注意生命周期 |
| | | ```kotlin |
| | | // 在Fragment中安全显示Dialog |
| | | activity?.let { activity -> |
| | | if (!activity.isFinishing && !activity.isDestroyed) { |
| | | dialog.show() |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | 3. **样式一致性**: 使用项目统一的Dialog样式 |
| | | ```kotlin |
| | | // 使用项目定义的Dialog样式 |
| | | super(context, R.style.ws_pay_showSelfDialog) |
| | | ``` |
| | | |
| | | 4. **用户体验优化**: |
| | | - 重要操作使用ConfirmDialog进行二次确认 |
| | | - 错误信息使用带标题的Dialog,提供清晰的错误分类 |
| | | - 长时间操作显示加载Dialog,避免用户误操作 |
| | | |
| | | ## 贡献指南 |
| | | |
| | | 1. Fork 项目 |