Android 复杂UI界面分模块解耦的一次实践

/**

  • 向外暴露调用方法
  • 停止计时
    /
    fun stopTimer() {
    mTimer?.apply {
    mIsStart = false
    }
    }
    /
    *
  • 生命周期部分的处理
    /
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
    when (event) {
    Lifecycle.Event.ON_DESTROY -> {
    //页面销毁时计时器也取消和销毁
    lifecycleOwner.lifecycle.removeObserver(this)
    mTimer?.cancel()
    mTimer = null
    }
    else -> {}
    }
    }
    /
    *
  • 设定itemType
    */
    override val itemType: Int
    get() = MODULE_ONE_ITEM_TYPE
    }

模块一向外暴露了`startTimer()`和`stopTimer()`二个方法,并且让模块一具备了Activity的生命周期感知能力,用于在页面销毁时取消和销毁计时。具备页面生命周期感知能力是模块很重要的特性。


再看看模块二中的代码:



class ModuleTwoItemBinder(private val moduleTwoItemBinderInterface: ModuleTwoItemBinderInterface) :
QuickViewBindingItemBinder<ModuleTwoData, LayoutModuleTwoBinding>(), MultiItemEntity {
@SuppressLint(“SetTextI18n”)
override fun convert(
holder: BinderVBHolder,
data: ModuleTwoData
) {
holder.viewBinding.btStartTimer.setOnClickListener { //接口实现
moduleTwoItemBinderInterface.onStartTimer()
}
holder.viewBinding.btStopTimer.setOnClickListener { //接口实现
moduleTwoItemBinderInterface.onStopTimer()
}
holder.viewBinding.btGetTimerNumber.setOnClickListener { //接口实现
holder.viewBinding.tv.text =
“获取到的模块一的计时数据:” + moduleTwoItemBinderInterface.onGetTimerNumber()
}
}
/**

  • 可以做局部刷新
    */
    override fun convert(
    holder: BinderVBHolder,
    data: ModuleTwoData,
    payloads: List
    ) {
    super.convert(holder, data, payloads)
    if (payloads.isNullOrEmpty()) {
    convert(holder, data)
    } else {
    //TODO 根据具体的payloads做局部刷新
    }
    }
    override fun onCreateViewBinding(
    layoutInflater: LayoutInflater,
    parent: ViewGroup,
    viewType: Int
    ): LayoutModuleTwoBinding {
    return LayoutModuleTwoBinding.inflate(layoutInflater, parent, false)
    }
    override val itemType: Int
    get() = MODULE_TWO_ITEM_TYPE
    }

模块二中有一个`ModuleTwoItemBinderInterface`接口对象,用于调用接口方法,具体接口实现在外部。`convert`有全量刷新和局部刷新的方法,对于刷新也比较友好。


接着看看是如何把不同的模块拼接起来的:



class MultipleModuleTestAdapter(
private val lifecycleOwner: LifecycleOwner,
data: MutableList? = null
) : BaseBinderAdapter(data) {
override fun getItemViewType(position: Int): Int {
return position + 1
}
/**

  • 给类型一和类型二设置数据
    /
    fun setData(response: String) {
    val moduleOneData = ModuleOneData().apply { text = “模块一数据:KaTeX parse error: Expected 'EOF', got '}' at position 11: response" }̲ val moduleTwoD…response” }
    //给Adapter设置数据
    setList(arrayListOf(moduleOneData, moduleTwoData))
    }
    /
    *
  • 添加ItemType类型一
    /
    fun addItemOneBinder() {
    addItemBinder(
    ModuleOneData::class.java,
    ModuleOneItemBinder(lifecycleOwner)
    )
    }
    /
    *
  • 添加ItemType类型二
    */
    fun addItemTwoBinder(moduleTwoItemBinderInterface: ModuleTwoItemBinderInterface) {
    addItemBinder(
    ModuleTwoData::class.java,
    ModuleTwoItemBinder(moduleTwoItemBinderInterface)
    )
    }
    }`



---



class MainModuleManager(
private val activity: MainActivity,
private val viewModel: MainViewModel,
private val viewBinding: ActivityMainBinding
) {
private var multipleModuleTestAdapter: MultipleModuleTestAdapter? = null
/**

  • 监听请求数据的回调
    /
    fun observeData() {
    viewModel.requestDataLiveData.observe(activity) {
    //接口请求到的数据
    initAdapter(it)
    }
    }
    private fun initAdapter(response: String) {
    //创建Adapter
    multipleModuleTestAdapter = MultipleModuleTestAdapter(activity)
    //设置RecyclerView
    viewBinding.rcy.apply {
    layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
    adapter = multipleModuleTestAdapter
    }
    //创建ModuleTwoItemBinder的接口实现类
    val moduleTwoItemBinderImpl = ModuleTwoItemBinderImpl(multipleModuleTestAdapter)
    //添加Item类型,组装UI,可以根据后台数据动态化
    multipleModuleTestAdapter?.addItemOneBinder()
    multipleModuleTestAdapter?.addItemTwoBinder(moduleTwoItemBinderImpl)
    //给所有的Item添加数据
    multipleModuleTestAdapter?.setData(response)
    }
    /
    *
  • 刷新单个模块的数据,也可以刷新单个模块的某个部分,需要设置playload
    */
    fun refreshModuleData(position: Int, newData: Any?) {
    multipleModuleTestAdapter?.apply {
    newData?.let {
    data[position] = newData
    notifyItemChanged(position)
    }
    }
    }
    }

在`MultipleModuleTestAdapter`中定义了多种`ViewType`,通过`MainModuleManager`返回的数据,动态的组装添加`ViewType`。


最后就是在`MainActivity`中调用`MainModuleManager`,代码如下:



class MainActivity : AppCompatActivity() {
private val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityMainBinding: ActivityMainBinding =
ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
//请求数据
mainViewModel.requestData()
//拆分RecyclerView的逻辑
val mainModuleManager = MainModuleManager(this, mainViewModel, activityMainBinding)
//回调数据到MainModuleManager中
mainModuleManager.observeData()
//TODO 如果有其他控件编写其他控件的逻辑
}

}


这样我们通过定义不同的`ItemBinder`实现了模块的划分,通过定义接口实现了模块间的通讯,通过后台返回数据动态的组装了页面。


其他代码一并写在末尾,方便阅读和理解:


![image.png](https://img-blog.csdnimg.cn/img_convert/26de26b9a84aa953f753a05c41fe1106.webp?x-oss-process=image/format,png)


`ModuleConstant`



object ModuleConstant { //ItemType const val MODULE_ONE_ITEM_TYPE = 0 const val MODULE_TWO_ITEM_TYPE = 1 }


`ModuleOneData`和`ModuleTwoData`都是`data`类,内容完全一致,随便定义的:



`data class ModuleOneData(
var text: String? = “”
)


`ModuleTwoItemBinderImpl`是`ModuleTwoItemBinderInterface`的实现类,通过`Adapter`能轻松的获取到不同的`ItemBinder`,所以可以通过接口互相调用彼此的函数。



class ModuleTwoItemBinderImpl(private val multipleModuleTestAdapter: MultipleModuleTestAdapter?) :
ModuleTwoItemBinderInterface {
/**

  • 外部实现里面的方法
    */
    override fun onStartTimer() {
    //通过Adapter能轻松的获取到不同的ItemBinder,所以可以通过接口互相调用彼此的函数
    val moduleOneItemBinder =
    multipleModuleTestAdapter?.getItemBinder(ModuleConstant.MODULE_ONE_ITEM_TYPE + 1) as ModuleOneItemBinder
    moduleOneItemBinder.startTimer()
    }
    override fun onStopTimer() {
    //通过Adapter能轻松的获取到不同的ItemBinder,所以可以通过接口互相调用彼此的函数
    val moduleOneItemBinder =
    multipleModuleTestAdapter?.getItemBinder(ModuleConstant.MODULE_ONE_ITEM_TYPE + 1) as ModuleOneItemBinder
    moduleOneItemBinder.stopTimer()
    }
    override fun onGetTimerNumber(): String {
    multipleModuleTestAdapter?.apply {
    //通过Adapter可以轻松的拿到其他模块的数据
    return (data[0] as ModuleOneData).text ?: “0”
    }
    return “0”
    }

}




---



nterface ModuleTwoItemBinderInterface {
//开始计时
fun onStartTimer()
//停止计时
fun onStopTimer()
//获取计时数据
fun onGetTimerNumber():String
}


### 四、总结


通过定义不同的`ItemBinder`将页面划分为不同模块,实现UI和交互解耦,单个`ItemBinder`也可以在其他页面进行复用。通过后台数据动态的添加`ItemBinder`页面组装更灵活。任务分拆,提高开发效率。


### 五、注意事项


1、不要把太复杂的UI交互放在单一模块,处理起来费劲。  
 2、如果二个模块中间需要大量的通讯,写太多接口也费劲,最好看能不能放一个模块。  
 3、数据最好请求好后再塞进去给各个ItemBinder用,方便统一处理UI。当然如果各个模块想自己处理UI,那各个模块也可以自己去请求接口。毕竟模块隔离,彼此也互不影响。  


## 分享读者

> 作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

> 被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中...(img-sR4feMVS-1723743179402)] 

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。


在。

> 被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中...(img-sR4feMVS-1723743179402)] 

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。


  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值