Kotlin+Mvp+Retrofit+SmartRefreshLayout封装刷新加载更多分页
分享一个好用的android开发基础框架。本框架采用模块化MVP架构思想。
提供了封装列表刷新,网络请求图片加载等操作。
如果在你开发一个新的项目,没有好的基础框架,可以拿来简单的修改后就可以迅速的进入开发
详细请查看 项目地址
框架特点:
- 使用kotlin语言,提高开发效率。无需findViewById,直接访问xml文件中的对象,并且有好多扩展,方便调用。
- Mvp设计思想,解耦合。层次清晰。
- 提供了很多好用的工具类,以及kotlin的扩展。
- 提供了刷新以及加载的封装。使用者无需每个页面都实现下拉刷新的网络处理以及上拉加载更多的分页处理,以及网络失败和空数据时的页面的展示以及对刷新框架的状态恢复等等操作。
- 非常灵活,考虑到了很多特殊场景皆可满足需求。
具体使用:
1 如果你的页面是一个普通的页面没有网络交互的,直接继承BaseActivity或者继承BaseFragment。
class PlatformFragment : BaseFragment(){
override fun getLayoutId(): Int {
return R.layout.fragment_platform
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initView()
}
@SuppressLint("SetTextI18n")
private fun initView() {
//商品清点回调
mLayoutInCount.onClick{
toast("商品清点")
}
//商品验收回调
mLayoutInCheck.onClick {
ARouter.getInstance().build(RouterPath.InWarehouse.IN_CHECK).navigation()
}
mLayoutMove.onClick {
ARouter.getInstance().build(RouterPath.WarehouseManager.LOCATION_MOVE).navigation()
}
}
}
2 如果你的页面不是一个列表,但是带有网络请求。那么继承BaseMvpActivity或BseMvpFragment
Activity:
@Route(path = RouterPath.UserCenter.PATH_LOGIN)
class LoginActivity : BaseMvpActivity<LoginPresenter, ILoginView>(), ILoginView {
private var userEntity: UserInfoEntity? = null
override fun getV(): ILoginView {
return this
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
SpManager.logout()
initView()
//弹框提示
showAlertDialog("该app由新欢独家开发")
}
private fun initView() {
if (SpManager.getUserAccount().isNotEmpty()) {
mEdtAccount.setText(SpManager.getUserAccount())
mEdtAccount.setSelection(mEdtAccount.text.toString().length)
mIvCleanAccount.setVisible(mEdtAccount.text != null && mEdtAccount.text.isNotEmpty())
}
mEdtAccount.addTextChangedListener(object : DefaultTextWatcher() {
override fun afterTextChanged(s: Editable?) {
super.afterTextChanged(s)
if (s == null || s.isEmpty()) {
mIvCleanAccount.setVisible(false)
return
}
mIvCleanAccount.setVisible(true)
}
})
mEdtPwd.addTextChangedListener(object : DefaultTextWatcher() {
override fun afterTextChanged(s: Editable?) {
super.afterTextChanged(s)
if (s == null || s.isEmpty()) {
mIvCleanPwd.setVisible(false)
return
}
mIvCleanPwd.setVisible(true)
}
})
mIvCleanAccount.onClick {
mEdtAccount.setText("")
mEdtPwd.setText("")
mEdtAccount.requestFocus()
mIvCleanAccount.setVisible(false)
mIvCleanPwd.setVisible(false)
}
mIvCleanPwd.onClick {
mEdtPwd.setText("")
mIvCleanPwd.setVisible(false)
}
mBtnLogin.onClick {
login()
}
}
private fun login() {
val account = mEdtAccount.text.toString().trim()
if (account.isEmpty()) {
toast("请输入账号")
return
}
val pwd = mEdtPwd.text.toString().trim()
if (pwd.isEmpty()) {
toast("请输入密码")
return
}
//请求后端接口
mPresenter.userLogin(
params.clear()
.put("loginName", account)
.put("password", pwd)
.build()
)
}
/**
* 登录后回调
*/
override fun onGetUserInfoResult(result: UserInfoEntity) {
this.userEntity = result
//将数据保存到SharedPreferences中
SpManager.setUserId(result.userId)
SpManager.setUserAccount(result.userAccount)
SpManager.setUserName(result.userName)
//Toast提示
toast("登录成功")
finish()
ARouter.getInstance().build(RouterPath.App.MAIN_ACTIVITY).navigation()
}
}
presenter:
class LoginPresenter : BasePresenter<ILoginView>() {
fun userLogin(params: HashMap<String, Any>) {
if (!checkNetWork()) {
return
}
//BaseObserverWithDialog 是带有请求过渡等待对话框显示的
UserService().userLogin(params).execute(object : BaseObserverWithDialog<UserInfoEntity>(mView) {
override fun onNext(t: UserInfoEntity) {
mView.onGetUserInfoResult(t)
}
}, lifecycleProvider)
}
}
view:
interface ILoginView : BaseView{
//请求成功的回调
fun onGetUserInfoResult(result: UserInfoEntity)
}
model:
class UserService {
fun userLogin(params: HashMap<String, Any>): Observable<UserInfoEntity> {
val repository = UserRepository()
//convert 转成你要的对象UserInfoEntity convertBoolean返回的是布尔类型
return repository.userLogin(params).convert()
}
}
3 如果你的页面是一个列表,有网络请求。那么继承BaseListMvpActivity或BaseListMvpFragment,你只需要搭建好自己的页面,返回给框架recyclerView,refreshLayout(刷新框架),multiStateView(多视图View,显示空数据,网络请求失败等样式的页面),Adapter即可。无需对刷新以及加载的处理,这一切都在BaseListMvpActivity和BaseListMvpFragment中帮你完成了。
Activity:
/**
* 任务列表页
* 如果你的页面是一个列表 并且需要 实现刷新加载 空布局显示等这样的页面
* 那么建议继承BaseListMvpFragment 或者BaseListMvpActivity 这样你不需要关心
* 当数据为空该如何显示页面,你不需要关心刷新如何处理,你也不需要关心上拉加载请求参数以及网络回调后的状态如何处理
*/
class TaskListFragment : BaseListMvpFragment<TaskEntity, TaskListPresenter, IGetTaskListView>(),
IGetTaskListView,
OnRefreshListener, OnItemClickListener{
private val mAdapter = TaskListAdapter(null)
private var selectedTypeId = -1
override fun getRefreshViewManager(): RefreshViewManager<TaskEntity> {
//返回需要的对象
return RefreshViewManager(mRv, mRefreshLayout, mMultiView, mAdapter)
}
//返回列表接口需要的参数
override fun getListRequestParams(): HashMap<String, Any> = params.clear().put("taskType", selectedTypeId).build()
override fun getV(): IGetTaskListView = this
override fun getLayoutId(): Int {
//返回你自己的页面布局
return R.layout.fragment_tast_list
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initView()
}
@SuppressLint("SetTextI18n")
private fun initView() {
mHeaderBar.getTitleView().text = "任务列表"
mHeaderBar.getRightView().onClick {
toast("右侧按钮")
}
mAdapter.setOnItemClickListener(this)
}
override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
toast("点击了$position")//利用kotlin的扩展直接调用toast提示
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/common_bg"
android:orientation="vertical">
<!--自定义title-->
<com.xinhuan.wms.demo.provider.widgets.HeaderBar
android:id="@+id/mHeaderBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isShowBack="false"
app:rightText="右侧按钮"
app:titleText="出库任务列表" />
<!--这里提供了通用的布局,里面有写好的列表以及刷新控件-->
<include layout="@layout/layout_common_list"/>
</LinearLayout>
View接口:
//带有列表的页面,这里注意我们继承的是BaseListView
interface IGetTaskListView : BaseListView<TaskEntity> {
//如果你的页面除了列表接口 还有其他的接口例如提交等操作的接口
//那么你的回调可以在这里添加
//fun xxx ()
}
presenter:
import com.xinhuan.wms.baselibrary.presenter.BaseListPresenter
import com.xinhuan.wms.baselibrary.rx.BaseObserverWithRefresh
import com.xinhuan.wms.baselibrary.utils.execute
import com.xinhuan.wms.outwarehouse.entity.ListTaskEntity
import com.xinhuan.wms.outwarehouse.entity.TaskEntity
import com.xinhuan.wms.outwarehouse.service.OutWarehouseService
import com.xinhuan.wms.outwarehouse.view.IGetTaskListView
//如果你的页面包含了列表 那么建议继承 BaseListPresenter
class TaskListPresenter : BaseListPresenter<TaskEntity, IGetTaskListView>() {
/**
* 必须重写的请求列表的接口
*/
override fun getListData(params: HashMap<String, Any>) {
//如果是一个列表 注意这里调用的是checkRequestListNetWork
if (!checkRequestListNetWork()) {
return
}
//请求你要调用的接口
//注意这里是调用的 BaseObserverWithRefresh
OutWarehouseService().getTaskList(params)
.execute(object : BaseObserverWithRefresh<ListTaskEntity>(mView) {
override fun onNext(t: ListTaskEntity) {
super.onNext(t)
mView.onGetListResult(t.data, t.page)
}
}, lifecycleProvider)
}
/**
* 如果你的界面虽然是一个列表页面,但是你的页面中也有其他的接口需要调用,那么你可以在这里继续写你的其他接口
* 例如提交等操作
*/
// fun commitXxx(params: HashMap<String, Any>) {
// if (!checkNetWork()) {
// return
// }
// //BaseObserverWithDialog -- 请求接口带有Dialog等待过渡对话框
// OutWarehouseService().commitXxx(params)
// .execute(object : BaseObserverWithDialog<xxxResult>(mView) {
// override fun onNext(t: xxxResult) {
// super.onNext(t)
// mView.onXXX(t.xxx)
// }
//
// }, lifecycleProvider)
// }
}
这样你就实现了一个带有列表的页面,而且你的列表样式完全可以自由定义。这样显得Activity的代码也非常整洁。
4 如果你在某些场景下需要对请求失败等特殊处理。例如默认实现的是toast提示失败原因,但是你却想要用弹框提示。那么你可以重写showErrorMsg()
class TaskListFragment : BaseListMvpFragment...{
//....
/**
* 如果你对错误信息提示需要更多的处理
* 可以重写该方法,覆盖父类默认的toast提示
*/
override fun showErrorMsg(text: String, status:Int){
//在这里处理错误提示
}
}
或者你也可以在Presenter中这样做处理:
class TaskListPresenter : BaseListPresenter<TaskEntity, IGetTaskListView>() {
override fun getListData(params: HashMap<String, Any>) {
if (!checkRequestListNetWork()) {
return
}
OutWarehouseService().getTaskList(params)
.execute(object : BaseObserverWithRefresh<ListTaskEntity>(mView) {
override fun onNext(t: ListTaskEntity) {
super.onNext(t)
mView.onGetListResult(t.data, t.page)
}
//默认已经处理了错误信息,但是如果当需要对错误信息额外做一些特殊的处理时,可以在这里回调给Activity做处理
override fun onError(e: Throwable) {
super.onError(e)
mView.onGetTestError()
}
}, lifecycleProvider)
}
}
如果你对这个框架很感兴趣请下载Demo,github:https://github.com/LoveNewsweetheart/kotlin-mvp-demo
如果有不足地方以及有更好的思路,请我们互相学习!多多指教!