Android项目实战搭建 MVVM架构

打印 上一主题 下一主题

主题 1910|帖子 1910|积分 5730

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
View层

详细代码:

 activity:

  1. /**
  2. * @description:
  3. * 普通Activity基类,不带ViewModel,显示基本加载状态
  4. * 需要获取到子类的布局id用于databinding的绑定
  5. * @author YL Chen
  6. * @date 2024/9/4 21:34
  7. * @version 1.0
  8. */
  9. abstract class BaseActivity<VB : ViewDataBinding>(@LayoutRes layoutID: Int) :
  10.     AppCompatActivity() { //此处不能将layoutId传递进去,否则会导致fragment加载但不显示
  11.     open lateinit var mRefreshLayout: SmartRefreshLayout
  12.     //仅供直接继承本类的子类调用,继承于BaseVMActivity的调用此对象的方法无效,
  13.     // 因为setSuccessView方法先于getLoadView方法执行,导致加载状态无法被移除,继承于BaseVMActivity的想改变状态需使用mViewModel调用changeStateView方法
  14.     open lateinit var mMultiplyStateView: MultiplyStateView
  15.     //子view的布局id
  16.     private var mLayoutId: Int = layoutID
  17.     //子View的dataBinding
  18.     lateinit var mBinding: VB
  19.     override fun onCreate(savedInstanceState: Bundle?) {
  20.         super.onCreate(savedInstanceState)
  21.         //设置基础布局
  22.         setContentView(R.layout.base_load_more_layout)
  23.         //初始化界面
  24.         initView()
  25.         //初始化数据
  26.         initData()
  27.     }
  28.     //初始化界面
  29.     open fun initView() {
  30.         //获取加载成功DataViewBinding
  31.         mBinding = DataBindingUtil.inflate(
  32.             layoutInflater,
  33.             mLayoutId,
  34.             null,
  35.             false
  36.         )
  37.         //刷新框架
  38.         mRefreshLayout = findViewById(R.id.refreshLayout)
  39.         //找到基础布局中的自定义多状态View控件
  40.         mMultiplyStateView = findViewById(R.id.multiply_state_view)
  41.         // 将加载成功View布局添加到自定义多状态View中
  42.         mMultiplyStateView.setSuccessView(mBinding.root)
  43.     }
  44.     //初始化数据
  45.     abstract fun initData()
  46. }
  47. /**
  48. * @description: 携带ViewModel的Activity基类,继承自BaseActivity
  49. * @author YL Chen
  50. * @date 2024/9/6 14:16
  51. * @version 1.0
  52. */
  53. abstract class BaseVMActivity<VB : ViewDataBinding, VM : BaseViewModel>(@LayoutRes layoutId: Int) :
  54.     BaseActivity<VB>(layoutId) {
  55.     //子类ViewModel实例
  56.     lateinit var mViewModel: VM
  57.     /**
  58.      * 获取对应的ViewModel,并初始化数据
  59.      */
  60.     override fun initData() {
  61.         //dataLoading()
  62.         mViewModel = getViewModel()!!
  63.         //将子类的ViewModel和dataBinding联系起来,实现界面数据的自动更新
  64.         //将xml布局对应的viewModel对象赋值到xml布局中声明的viewModel变量 即实现如:mBinding.viewModel = ViewModel() 的效果
  65.         val variableId = getVariableId()
  66.         if (variableId != -1) {
  67.             mBinding.setVariable(variableId, mViewModel)
  68.             //立即执行 Data Binding 中的挂起绑定
  69.             //即Data Binding 会立即将 ViewModel 的属性和方法更新到布局文件中
  70.             mBinding.executePendingBindings()
  71.         }
  72.         //初始化视图状态
  73.         initViewState()
  74.         //初始化ViewModel数据
  75.         initVMData()
  76.         //监听liveData
  77.         observeLiveData()
  78.         //设置状态页点击重新加载监听
  79.         mMultiplyStateView.setOnReLodListener(mViewModel)
  80.     }
  81.     /**
  82.      * 监听ViewModel中的LiveData
  83.      */
  84.     open fun observeLiveData() {
  85.     }
  86.     /**
  87.      * 初始化状态
  88.      */
  89.     private fun initViewState() {
  90.         mViewModel.mStateViewLiveData.observe(this) {
  91.             when (it) {
  92.                 ViewStateEnum.VIEW_LOADING -> {
  93.                     LogUtils.d(this, "StateLayoutEnum.DATA_LOADING")
  94.                     dataLoading()
  95.                 }
  96.                 ViewStateEnum.VIEW_EMPTY -> {
  97.                     LogUtils.d(this, "StateLayoutEnum.DATA_ERROR")
  98.                     dataEmpty()
  99.                 }
  100.                 ViewStateEnum.VIEW_NET_ERROR -> {
  101.                     LogUtils.d(this, "StateLayoutEnum.NET_ERROR")
  102.                     netError()
  103.                 }
  104.                 ViewStateEnum.VIEW_LOAD_SUCCESS -> {
  105.                     LogUtils.d(this, "StateLayoutEnum.LOAD_SUCCESS")
  106.                     loadSuccess()
  107.                 }
  108.                 ViewStateEnum.VIEW_NONE -> {
  109.                     LogUtils.d(this, "StateLayoutEnum.NONE")
  110.                 }
  111.             }
  112.         }
  113.     }
  114.     /**
  115.      * 数据加载成功
  116.      */
  117.     open fun loadSuccess() {
  118.         mMultiplyStateView.showSuccess()
  119.     }
  120.     /**
  121.      * 网络加载失败
  122.      */
  123.     open fun netError() {
  124.         mMultiplyStateView.showNetError()
  125.     }
  126.     /**
  127.      * 数据加载为空
  128.      */
  129.     open fun dataEmpty() {
  130.         mMultiplyStateView.showEmpty()
  131.     }
  132.     /**
  133.      * 数据加载中
  134.      */
  135.     open fun dataLoading() {
  136.         mMultiplyStateView.showLoading()
  137.         val loadingView = mMultiplyStateView.getLoadingView()
  138.         val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
  139.         myLoadingView.startRotate()
  140.     }
  141.     /**
  142.      * 初始化ViewModel数据
  143.      */
  144.     abstract fun initVMData()
  145.     /**
  146.      * 获取xml绑定的variable
  147.      * @return Int
  148.      */
  149.     //子类通过重写此方法返回子类对应xml文件中绑定的viewModel变量的id
  150.     open fun getVariableId(): Int {
  151.         return -1
  152.     }
  153.     /**
  154.      * 通过反射获取子类的ViewModel
  155.      * @return VM?
  156.      */
  157.     private fun getViewModel(): VM? {
  158.         //这里获得到的是类的泛型的类型
  159.         val type = javaClass.genericSuperclass
  160.         if (type != null && type is ParameterizedType) {
  161.             val actualTypeArguments = type.actualTypeArguments
  162.             val tClass = actualTypeArguments[1]
  163.             return ViewModelProvider(
  164.                 this,
  165.                 ViewModelProvider.AndroidViewModelFactory.getInstance(application)
  166.             )
  167.                 .get(tClass as Class<VM>)
  168.         }
  169.         return null
  170.     }
  171. }
复制代码
fragment:

  1. /**
  2. * @description: 普通Fragment基类,不带ViewModel
  3. * @author YL Chen
  4. * @date 2024/9/6 16:19
  5. * @version 1.0
  6. */
  7. abstract class BaseFragment<VB : ViewDataBinding>(@LayoutRes layoutId: Int = 0) : Fragment() {
  8.     open lateinit var mRefreshLayout: SmartRefreshLayout
  9.     open lateinit var mMultiplyStateView: MultiplyStateView
  10.     //子类的布局id
  11.     private val mLayoutId: Int = layoutId
  12.     //子View的dataBinding
  13.     lateinit var mBinding: VB
  14.     lateinit var mRootView: View
  15.     override fun onCreateView(
  16.         inflater: LayoutInflater,
  17.         container: ViewGroup?,
  18.         savedInstanceState: Bundle?
  19.     ): View? {
  20.         //设置根布局
  21.         mRootView = layoutInflater.inflate(R.layout.base_load_more_layout, container, false)
  22.         return mRootView
  23.     }
  24.     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  25.         super.onViewCreated(view, savedInstanceState)
  26.         //初始化视图
  27.         initView()
  28.         //初始化数据
  29.         initData()
  30.     }
  31.     //初始化视图
  32.     open fun initView() {
  33.         //获取加载成功View的DataViewBinding
  34.         mBinding = DataBindingUtil.inflate(layoutInflater, mLayoutId, null, false)
  35.         //找到根布局的baseFrameLayout
  36.         mMultiplyStateView = mRootView.findViewById(R.id.multiply_state_view)
  37.         //将子类加载成功View布局添加进去
  38.         mMultiplyStateView.setSuccessView(mBinding.root)
  39.         //获取到刷新框架
  40.         mRefreshLayout = mRootView.findViewById(R.id.refreshLayout)
  41.     }
  42.     //初始化数据
  43.     abstract fun initData()
  44. }
  45. /**
  46. * @description: 携带ViewModel的fragment
  47. * @author YL Chen
  48. * @date 2024/9/6 21:52
  49. * @version 1.0
  50. */
  51. abstract class BaseVMFragment<VB : ViewDataBinding, VM : BaseViewModel>(@LayoutRes layoutId: Int) :
  52.     BaseFragment<VB>(layoutId) {
  53.     lateinit var mViewModel: VM
  54.     override fun initData() {
  55.         mViewModel = getViewModel()!!
  56.         val variableId = getVariableId()
  57.         if (variableId != -1) {
  58.             mBinding.setVariable(getVariableId(), mViewModel)
  59.             mBinding.executePendingBindings()
  60.         }
  61.         initState()
  62.         initVMData()
  63.         observeLiveData()
  64.         //设置状态页点击重新加载监听
  65.         mMultiplyStateView.setOnReLodListener(mViewModel)
  66.     }
  67.     /**
  68.      * 获取子类xml 的Variable
  69.      * @return Int
  70.      */
  71.     open fun getVariableId(): Int {
  72.         return -1
  73.     }
  74.     /**
  75.      * 初始化状态
  76.      */
  77.     private fun initState() {
  78.         mViewModel.mStateViewLiveData.observe(this) {
  79.             when (it) {
  80.                 ViewStateEnum.VIEW_LOADING -> {
  81.                     LogUtils.d(this, "StateLayoutEnum.DATA_LOADING")
  82.                     dataLoading()
  83.                 }
  84.                 ViewStateEnum.VIEW_EMPTY -> {
  85.                     LogUtils.d(this, "StateLayoutEnum.DATA_ERROR")
  86.                     dataEmpty()
  87.                 }
  88.                 ViewStateEnum.VIEW_NET_ERROR -> {
  89.                     LogUtils.d(this, "StateLayoutEnum.NET_ERROR")
  90.                     netError()
  91.                 }
  92.                 ViewStateEnum.VIEW_LOAD_SUCCESS -> {
  93.                     LogUtils.d(this, "StateLayoutEnum.LOAD_SUCCESS")
  94.                     loadSuccess()
  95.                 }
  96.                 ViewStateEnum.VIEW_NONE -> {
  97.                     LogUtils.d(this, "StateLayoutEnum.NONE")
  98.                 }
  99.             }
  100.         }
  101.     }
  102.     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  103.         super.onViewCreated(view, savedInstanceState)
  104.         LogUtils.d(this, "onViewCreated")
  105.     }
  106.     /**
  107.      * 数据加载成功
  108.      */
  109.     open fun loadSuccess() {
  110.         mMultiplyStateView.showSuccess()
  111.     }
  112.     /**
  113.      * 网络加载失败
  114.      */
  115.     open fun netError() {
  116.         mMultiplyStateView.showNetError()
  117.     }
  118.     /**
  119.      * 数据加载错误
  120.      */
  121.     open fun dataEmpty() {
  122.         mMultiplyStateView.showEmpty()
  123.     }
  124.     /**
  125.      * 数据加载中
  126.      */
  127.     open fun dataLoading() {
  128.         mMultiplyStateView.showLoading()
  129.         val loadingView = mMultiplyStateView.getLoadingView()
  130.         val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
  131.         myLoadingView.startRotate()
  132.     }
  133.     /**
  134.      * 通过反射获取子类的ViewModel
  135.      * @return VM?
  136.      */
  137.     private fun getViewModel(): VM? {//这里获得到的是类的泛型的类型
  138.         val type = javaClass.genericSuperclass
  139.         if (type != null && type is ParameterizedType) {
  140.             val actualTypeArguments = type.actualTypeArguments
  141.             val tClass = actualTypeArguments[1]
  142.             return ViewModelProvider(
  143.                 this,
  144.                 ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().application)
  145.             )
  146.                 .get(tClass as Class<VM>)
  147.         }
  148.         return null
  149.     }
  150.     /**
  151.      * 初始化ViewModel数据
  152.      */
  153.     abstract fun initVMData()
  154.     /**
  155.      * 监听ViewModel中的LiveData
  156.      */
  157.     open fun observeLiveData() {
  158.     }
  159. }
复制代码
思路:

BaseXXX:

通过泛型吸收子类视图的ViewDataBInding类型,并通过构造方法获取子类的视图ID。
onCreate()方法中,设置底子视图base_load_more_layout.xml,base_load_more_layout.xml中携带SmartRefreshLayout刷新框架和自定义的MultiplyStateView状态视图切换View,以实现刷新、加载更多视图和底子的状态视图切换功能;调用initView(),initData()方法
initView(): 初始化ViewDataBinding,使用构造方法中转达过来的子类视图ID举行初始化,并将其声明为公开的成员变量(mBinding),子类可直接调用,此ViewDataBinding为成功视图,并初始化SmartRefreshLayout和MultiplyStateView,声明为成员变量(mRefreshLayout和mMultiplyStateView),设置初始化的ViewDataBinding.root为MultiplyStateView的成功视图。
initData抽象方法欺压子类重写,用于初始化数据。
BaseVMXXX:

通过泛型担当子类的ViewDataBinding和ViewModel,并通过构造方法获取子类视图ID,继续自BaseXXX,将泛型ViewDataBinding和视图ID转达给BaseXXX。
重写BaseXXX的initData()方法:

  • 通过反射获取通过泛型转达进来的子类ViewModel的对象,并将其声明为公开的成员变量,子类可直接调用(mViewModel)
  • 调用getVariableId()方法获取子类转达过来的在XML结构中绑定的ViewModel变量,并通过mBinding调用setsetVariable()方法将其与mViewModel绑定到一起,使得可以在子类的视图的一些事件可以绑定对应的ViewModel中。
  • 调用initViewState()初始化当前View的视图状态,默认是Loding(加载中)状态。
  • 调用initVMData()初始化ViewModel数据
  • 调用observeLiveData()监听ViewMode中的LiveData
  • lifecycle.addObserver(mViewModel),使ViewModel监听对应View类的生命周期,相当于将ViewModel与View类举行绑定,使其只在View生命周期内相应,制止内存泄漏大概资源的错误开释。
  • mMultiplyStateView.setOnReLodListener(mViewModel):为状态视图设置重新加载监听,监听者为View类对应的ViewModel。
getVariableId():公开的方法,默认返回-1,供子类重写返回XML视图中绑定的ViewModel变量
initViewState():观察对应ViewModel中的切换视图LiveData变量(mStateViewLiveData),实现状态视图的初始化和切换。
observeLiveData():公开的方法,供子类重写监听观察对应ViewMode中的LiveData变量。
补充:

自定义多状态视图View代码:

  1. package com.yl.wanandroid.ui.custom
  2. import android.content.Context
  3. import android.util.AttributeSet
  4. import android.view.LayoutInflater
  5. import android.view.View
  6. import android.widget.FrameLayout
  7. import androidx.annotation.LayoutRes
  8. import com.yl.wanandroid.R
  9. import com.yl.wanandroid.model.ViewStateEnum
  10. import com.yl.wanandroid.utils.LogUtils
  11. /**
  12. * @description: 自定义多状态View
  13. * @author YL Chen
  14. * @date 2024/9/29 10:47
  15. * @version 1.0
  16. */
  17. open class MultiplyStateView : FrameLayout {
  18.     private var mOnReLodListener: OnReLodListener? = null
  19.     private lateinit var params: LayoutParams
  20.     private lateinit var mInflater: LayoutInflater
  21.     private var mSuccessViewId: Int = 0
  22.     private var mEmptyViewId: Int = 0
  23.     private var mNetErrorViewId: Int = 0
  24.     private var mLoadingViewId: Int = 0
  25.     //四种展示的view
  26.     private var mLoadingView: View? = null
  27.     private var mSuccessView: View? = null
  28.     private var mNetErrorView: View? = null
  29.     private var mEmptyView: View? = null
  30.     //当前视图状态
  31.     private var currentState: ViewStateEnum = ViewStateEnum.VIEW_NONE
  32.     constructor(context: Context) : this(context, null)
  33.     constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
  34.     constructor(context: Context, attrs: AttributeSet?, defy: Int) : super(context, attrs, defy) {
  35.         initView(context, attrs)
  36.     }
  37.     private fun initView(context: Context, attrs: AttributeSet?) {
  38.         //获取自定义属性
  39.         val a =
  40.             context.obtainStyledAttributes(attrs, R.styleable.MultiplyStateView)
  41.         mLoadingViewId =
  42.             a.getResourceId(R.styleable.MultiplyStateView_msv_loadingView, R.layout.view_loading)
  43.         mNetErrorViewId =
  44.             a.getResourceId(R.styleable.MultiplyStateView_msv_netErrorView, R.layout.view_net_error)
  45.         mEmptyViewId =
  46.             a.getResourceId(R.styleable.MultiplyStateView_msv_emptyView, R.layout.view_empty)
  47.         mSuccessViewId = a.getResourceId(R.styleable.MultiplyStateView_msv_successView, 0)
  48.         a.recycle()
  49.         mInflater = LayoutInflater.from(context)
  50.         params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
  51.         setLoadingView(mLoadingViewId)
  52.         setEmptyView(mEmptyViewId)
  53.         setNetErrorView(mNetErrorViewId)
  54.     }
  55.     //在 XML 布局文件中的视图被加载并且所有的子视图都被添加到父视图中之后执行
  56. /*    override fun onFinishInflate() {
  57.         super.onFinishInflate()
  58.         //展示加载页面
  59.         LogUtils.d(this,"onFinishInflate-->showLoading")
  60.         showLoading()
  61.         val loadingView = getLoadingView()
  62.         val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
  63.         myLoadingView.startRotate()
  64.     }*/
  65.     //++++++++++++++++++++++++++++++++加载页面++++++++++++++++++++
  66.     //动态加载并展示加载页面
  67.     fun showLoading() {
  68.         if (mLoadingView == null) {
  69.             mLoadingView = mInflater.inflate(mLoadingViewId, null)
  70.         }
  71.         if (mLoadingView != null ) {
  72.             removeAllViews()
  73.             addView(mLoadingView, 0, params)
  74.         } else {
  75.             throw NullPointerException("you have to set loading view before that")
  76.         }
  77.     }
  78.     /**
  79.      * 提供方法给外界设置自定义加载页面
  80.      * @param layoutId Int 布局Id
  81.      */
  82.     open fun setLoadingView(@LayoutRes layoutId: Int) {
  83.         setLoadingView(mInflater.inflate(layoutId, null))
  84.     }
  85.     open fun setLoadingView(view: View) {
  86.         mLoadingView = view
  87.     }
  88.     /**
  89.      * 获取加载页面
  90.      */
  91.     fun getLoadingView(): View {
  92.         if (null == mLoadingView) {
  93.             mLoadingView = mInflater.inflate(mLoadingViewId, null)
  94.         }
  95.         return mLoadingView!!
  96.     }
  97.     //++++++++++++++++++++++++++++++++成功页面++++++++++++++++++++
  98.     /**
  99.      * 显示成功状态
  100.      */
  101.     fun showSuccess() {
  102.         if (null == mSuccessView) {
  103.             mSuccessView = mInflater.inflate(mSuccessViewId, null)
  104.         }
  105.         if (mSuccessView != null) {
  106.             LogUtils.d(this,"childCount-->${childCount}")
  107.             val loadingView = getLoadingView()
  108.             val myLoadingView = loadingView.findViewById<MyLoadingView>(R.id.my_loading_view)
  109.             myLoadingView.stopRotate()
  110.             removeAllViews()
  111.             LogUtils.d(this,"childCount-->${childCount}")
  112.             addView(mSuccessView, 0, params)
  113.             LogUtils.d(this,"childCount-->${childCount}")
  114.             currentState = ViewStateEnum.VIEW_LOAD_SUCCESS
  115.         } else {
  116.             throw NullPointerException("you have to set success view before that")
  117.         }
  118.     }
  119.     /**
  120.      * 设置自定义的成功页面
  121.      *
  122.      * @param layoutResID
  123.      */
  124.     fun setSuccessView(@LayoutRes layoutResID: Int) {
  125.         setSuccessView(mInflater.inflate(layoutResID, null))
  126.     }
  127.     /**
  128.      * 设置自定义的成功页面
  129.      *
  130.      * @param view
  131.      */
  132.     fun setSuccessView(view: View) {
  133.         mSuccessView = view
  134.         LogUtils.d(this,"setSuccessView-->${view}")
  135.     }
  136.     /**
  137.      * 获取成功页面
  138.      */
  139.     fun getSuccessView(): View {
  140.         if (null == mSuccessView) {
  141.             mSuccessView = mInflater.inflate(mSuccessViewId, null)
  142.         }
  143.         return mSuccessView!!
  144.     }
  145.     //++++++++++++++++++++++++++++++++网络错误页面++++++++++++++++++++
  146.     /**
  147.      * 显示加载失败(网络错误)状态 带监听器的
  148.      */
  149.     fun showNetError() {
  150.         if (null == mNetErrorView) {
  151.             mNetErrorView = mInflater.inflate(mNetErrorViewId, null)
  152.         }
  153.         if (mNetErrorView != null) {
  154.             removeAllViews()
  155.             addView(mNetErrorView, 0, params)
  156.             currentState = ViewStateEnum.VIEW_NET_ERROR
  157.             mNetErrorView!!.setOnClickListener { showReLoading() }
  158.         } else {
  159.             throw java.lang.NullPointerException("you have to set unknown view before that")
  160.         }
  161.     }
  162.     /**
  163.      * 设置自定义的网络异常
  164.      *
  165.      * @param layoutResID
  166.      */
  167.     fun setNetErrorView(@LayoutRes layoutResID: Int) {
  168.         setNetErrorView(mInflater.inflate(layoutResID, null))
  169.     }
  170.     /**
  171.      * 设置自定义的网络异常
  172.      *
  173.      * @param view
  174.      */
  175.     fun setNetErrorView(view: View) {
  176.         mNetErrorView = view
  177.     }
  178.     /**
  179.      * 设置获取网络错误页面
  180.      */
  181.     fun getNetErrorView(): View {
  182.         if (null == mNetErrorView) {
  183.             mNetErrorView = mInflater.inflate(mNetErrorViewId, null)
  184.         }
  185.         return mNetErrorView!!
  186.     }
  187.     //++++++++++++++++++++++++++++++++空页面页面++++++++++++++++++++
  188.     /**
  189.      * 显示无数据状态
  190.      */
  191.     fun showEmpty() {
  192.         if (null == mEmptyView) {
  193.             mEmptyView = mInflater.inflate(mEmptyViewId, null)
  194.         }
  195.         if (mEmptyView != null) {
  196.             removeAllViews()
  197.             addView(mEmptyView, 0, params)
  198.             currentState = ViewStateEnum.VIEW_EMPTY
  199.         } else {
  200.             throw java.lang.NullPointerException("you have to set empty view before that")
  201.         }
  202.     }
  203.     /**
  204.      * 设置自定义的空页面
  205.      *
  206.      * @param layoutResID
  207.      */
  208.     fun setEmptyView(@LayoutRes layoutResID: Int) {
  209.         setEmptyView(mInflater.inflate(layoutResID, null))
  210.     }
  211.     /**
  212.      * 设置自定义的空页面
  213.      *
  214.      * @param view
  215.      */
  216.     fun setEmptyView(view: View) {
  217.         mEmptyView = view
  218.     }
  219.     /**
  220.      * 设置获取空页面
  221.      */
  222.     fun getEmptyView(): View {
  223.         if (null == mEmptyView) {
  224.             mEmptyView = mInflater.inflate(mEmptyViewId, null)
  225.         }
  226.         return mEmptyView!!
  227.     }
  228.     /**
  229.      * 再次加载数据
  230.      */
  231.     private fun showReLoading() {
  232.         //第一步重新loading
  233.         if (mOnReLodListener != null) {
  234.             showLoading()
  235.             mOnReLodListener!!.onReLoad()
  236.         } else {
  237.             //未设置重新加载回调
  238.             LogUtils.e(this, "请设置重新加载监听")
  239.         }
  240.     }
  241.     /**
  242.      * 外部回调
  243.      *
  244.      * @param onReLodListener
  245.      */
  246.     fun setOnReLodListener(onReLodListener: OnReLodListener) {
  247.         this.mOnReLodListener = onReLodListener
  248.     }
  249.     /**
  250.      * 重新加载页面的回调接口
  251.      */
  252.     interface OnReLodListener {
  253.         fun onReLoad()
  254.     }
  255.     override fun onAttachedToWindow() {
  256.         super.onAttachedToWindow()
  257.         updateVisibility()
  258.     }
  259.     private fun updateVisibility() {
  260.         // 获取父布局的可见性
  261.         val parentVisibility = (parent as? View)?.visibility ?: View.VISIBLE
  262.         visibility = parentVisibility
  263.     }
  264.     // 如果需要监听父布局的可见性变化,可以重写这个方法
  265.     override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
  266.         super.onLayout(changed, left, top, right, bottom)
  267.         updateVisibility()
  268.     }
  269.     override fun onDetachedFromWindow() {
  270.         super.onDetachedFromWindow()
  271.         mOnReLodListener = null // 清除监听器引用
  272.     }
  273. }
复制代码
ViewModel层

BaseViewModel详细代码:

  1. /**
  2. * @description: ViewModel基类
  3. * @author YL Chen
  4. * @date 2024/9/6 14:17
  5. * @version 1.0
  6. */
  7. abstract class BaseViewModel : ViewModel(),MultiplyStateView.OnReLodListener {
  8.     /**
  9.      * 控制状态视图的LiveData
  10.      */
  11.     val mStateViewLiveData = MutableLiveData(ViewStateEnum.VIEW_LOADING)
  12.     /**
  13.      * 切换到UI线程
  14.      * @param errorCallback SuspendFunction0<Unit> 错误回调
  15.      * @param requestCall SuspendFunction0<Unit> 网络请求函数
  16.      */
  17.     fun launchUI(
  18.         errorCallback: suspend (Int?, String?) -> Unit,
  19.         requestCall: suspend () -> Unit
  20.     ) {
  21.         viewModelScope.launch(Dispatchers.Main) {
  22.             //统一进行异常捕获
  23.             safeApiCall(errorCallback, requestCall)
  24.         }
  25.     }
  26.     /**
  27.      * 对网络请求进行统一异常捕获
  28.      * @param errorCallback SuspendFunction2<Int?, String?, Unit> 错误回调
  29.      * @param requestCall SuspendFunction0<Unit> 网络请求函数
  30.      * @return T? 网络请求成功数据
  31.      */
  32.     private suspend fun<T> safeApiCall(
  33.         errorCallback: suspend (Int?, String?) -> Unit,
  34.         requestCall: suspend () -> T?
  35.     ): T? {
  36.         try {
  37.             //返回网络请求结果
  38.             return requestCall()
  39.         }catch (e: Exception){
  40.             LogUtils.e(this@BaseViewModel,e.message.toString())
  41.             e.printStackTrace()
  42.             //统一异常处理
  43.             //将异常转为ApiException
  44.             val apiException = ExceptionHandler.handleException(e)
  45.             if (apiException.errCode == ERROR.UNKNOW_HOST.code || apiException.errCode == ERROR.NETWORD_ERROR.code){
  46.                 changeStateView(ViewStateEnum.VIEW_NET_ERROR)
  47.             }
  48.             errorCallback(apiException.errCode,apiException.errMsg)
  49.         }
  50.         return null
  51.     }
  52.     /**
  53.      * 更改状态视图的状态
  54.      */
  55.     fun changeStateView(
  56.         state: ViewStateEnum
  57.     ) {
  58.         // 对参数进行校验
  59.         when (state) {
  60.             ViewStateEnum.VIEW_LOADING -> {
  61.                 mStateViewLiveData.postValue(ViewStateEnum.VIEW_LOADING)
  62.             }
  63.             ViewStateEnum.VIEW_EMPTY -> {
  64.                 mStateViewLiveData.postValue(ViewStateEnum.VIEW_EMPTY)
  65.             }
  66.             ViewStateEnum.VIEW_NET_ERROR -> {
  67.                 mStateViewLiveData.postValue(ViewStateEnum.VIEW_NET_ERROR)
  68.             }
  69.             ViewStateEnum.VIEW_LOAD_SUCCESS -> {
  70.                 mStateViewLiveData.postValue(ViewStateEnum.VIEW_LOAD_SUCCESS)
  71.             }
  72.             ViewStateEnum.VIEW_NONE -> {
  73.                 mStateViewLiveData.postValue(ViewStateEnum.VIEW_NONE)
  74.             }
  75.         }
  76.     }
  77.     //错误视图点击回调函数
  78.     override fun onReLoad() {
  79.         //调用方法由子类实现
  80.         onReload()
  81.     }
  82.     //子类可实现此方法实现界面重新加载
  83.     open fun onReload(){}
  84. }
复制代码
思路:

继续自ViewModel,实现MultiplyStateView.OnReLodListener接口,重写onReload()方法,当用户点击重新加载状态视图时,回调此方法
声明mStateViewLiveData成员变量,给View层观察监听
封装changeStateView()方法,用于在网络哀求数据相干操纵后举行手动调用,改变mStateViewLiveData变量的值,View下层对此变量举行观察监听,实现状态视图的实时切换。
onReload()方法:公开的方法,用户自己选择是否重新,不是MultiplyStateView.OnReLodListener欺压重写的,而是自定义的,在欺压重写的onReload中调用。
封装safeApiCall()方法,传入网络哀求函数和错误回调函数,用于调用Modle层中的网络哀求方法,并对返回结果举行统一的异常处理,如当网络异常时,调用ViewModel.changStateView(ViewStateEnum.VIEW_NET_ERROR)改变状态视图。
封装launchUI()方法,用于切换到UI线程,在其中调用safeApiCall()方法。
Model层

BaseBean详细代码:网络哀求返回的底子数据Bean类(与接口返回的数据结构有关)

  1. data class BaseBean<T>(val data: T,val errorCode: Int, val errorMsg: String)/**
  2. * 网络返回数据类
  3. * @param T
  4. * @property errorCode String 0:正常,非0异常
  5. * @property errorMsg String
  6. * @property data T
  7. * @constructor
  8. */
  9. data class BaseResult<T>(val data: T,val errorCode: Int, val errorMsg: String, ) {
  10.     fun isFailed(): Boolean {
  11.         return errorCode != 0
  12.     }
  13. }
复制代码
BaseRepository详细代码:网络哀求底子仓库

  1. /**
  2. * @description: 网络请求基础仓库
  3. * @author YL Chen
  4. * @date 2024/9/10 17:06
  5. * @version 1.0
  6. */
  7. open class BaseRepository {
  8.     /**
  9.      * IO中处理请求,请求错误抛出自定义异常
  10.      * @param requestCall SuspendFunction0<BaseResult<T>?>
  11.      * @return T?
  12.      */
  13.     suspend fun <T> requestResponse(requestCall: suspend () -> BaseResult<T>?): T? {
  14.         val result = withContext(Dispatchers.IO) {
  15.             withTimeout(Constant.CONNECT_TIME_OUT * 1000) {
  16.                 requestCall()
  17.             }
  18.         } ?: return null
  19.         LogUtils.e(this@BaseRepository,"result-->$result")
  20.         if (result.isFailed()) {
  21.             throw ApiException(result.errorCode, result.errorMsg)
  22.         }
  23.         return result.data
  24.     }
  25. }
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表