开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
本章记录点击,长按,拖拽,滑动,顶部-向上加载,底部-加载更多等监听事件。
练习项目 GitHub 地址
点击和长按事件
「参考」 练习项目里的 ItemListenerActivity 相关。
使用场景
点击:点赞,收藏功能
长按:更多功能
相关属性
「BaseQuickAdapter」
| 属性 | 功能 |
|---|---|
| setOnItemClickListener() | item 点击事件 |
| setOnItemLongClickListener() | item 长按事件 |
| addChildClickViewIds() | 子控件点击事件添加子控件 ID |
| setOnItemChildClickListener() | 子控件点击事件 |
| addChildLongClickViewIds() | 子控件长按事件添加子控件 ID |
| setOnItemChildLongClickListener() | 子控件长按事件 |
| setOnItemClick() | 重新实现 item 点击事件逻辑 |
| setOnItemLongClick() | 重新实现 item 长按事件逻辑 |
| setOnItemChildClick() | 重新实现 item 子控件点击事件逻辑 |
| setOnItemChildLongClick() | 重新实现 item 子控件长按事件逻辑 |
//点击事件
itemListenerAdapter.setOnItemClickListener { adapter, view, position ->
val baseBean: BaseBean = adapter.data[position] as BaseBean
val viewId: Int = view.id
Toast.makeText(
this@ItemListenerActivity,
"item点击事件 内容 ${baseBean.content}| 位置 $position | viewId $viewId",
Toast.LENGTH_SHORT
).show()
}
//长按事件
itemListenerAdapter.setOnItemLongClickListener { adapter, view, position ->
val baseBean: BaseBean = adapter.data[position] as BaseBean
val viewId: Int = view.id
Toast.makeText(
this@ItemListenerActivity,
"item长按事件 内容 ${baseBean.content}| 位置 $position | viewId $viewId",
Toast.LENGTH_SHORT
).show()
true
}
//子控件点击事件添加子View的Id
itemListenerAdapter.addChildClickViewIds(R.id.btn_inner)
//子控件点击事件
itemListenerAdapter.setOnItemChildClickListener { adapter, view, position ->
val baseBean: BaseBean = adapter.data[position] as BaseBean
val viewId: Int = view.id
Toast.makeText(
this@ItemListenerActivity,
"item子控件的点击事件 内容 ${baseBean.content}| 位置 $position | viewId $viewId",
Toast.LENGTH_SHORT
).show()
}
//子控件长按事件添加子View的Id
itemListenerAdapter.addChildLongClickViewIds(R.id.btn_inner)
//子控件长按事件
itemListenerAdapter.setOnItemChildLongClickListener { adapter, view, position ->
val baseBean: BaseBean = adapter.data[position] as BaseBean
val viewId: Int = view.id
Toast.makeText(
this@ItemListenerActivity,
"item子控件的长按事件 内容 ${baseBean.content}| 位置 $position | viewId $viewId",
Toast.LENGTH_SHORT
).show()
true
}
/**
* 如果你想重新实现 item 点击事件逻辑,请重写此方法
*/
override fun setOnItemClick(v: View, position: Int) {
Toast.makeText(context, "点击事件", Toast.LENGTH_SHORT).show()
}
/**
* 如果你想重新实现 item 长按事件逻辑,请重写此方法
*/
override fun setOnItemLongClick(v: View, position: Int): Boolean {
Toast.makeText(context, "长按事件", Toast.LENGTH_SHORT).show()
return true
}
/**
* 如果你想重新实现 item 子控件点击事件逻辑,请重写此方法
*/
override fun setOnItemChildClick(v: View, position: Int) {
Toast.makeText(context, "子控件点击事件", Toast.LENGTH_SHORT).show()
}
/**
* 如果你想重新实现 item 子控件长按事件逻辑,请重写此方法
*/
override fun setOnItemChildLongClick(v: View, position: Int): Boolean {
Toast.makeText(context, "子控件长按事件", Toast.LENGTH_SHORT).show()
return true
}
效果图
拖拽和滑动事件
「重要」 首先需要 adapter 实现接口 DraggableModule。
「参考」 练习项目里的 ItemListenerActivity 相关。
使用场景
拖拽:调整支付方式顺序功能
滑动:删除功能
相关属性
「BaseQuickAdapter」
| 属性 | 功能 |
|---|---|
| draggableModule | 拖拽滑动模块 |
「DraggableModule」
| 属性 | 功能 |
|---|---|
| isDragEnabled | 是否可以拖拽 |
| toggleViewId | 长按拖拽的目标组件 ID |
| hasToggleView() | 判断长按拖拽是否有目标组件 ID |
| isDragOnLongPressEnabled | 是否可以长按拖动 (配合 toggleViewId 使用 ) |
| setOnItemDragListener() | 拖拽监听事件 |
| isSwipeEnabled | 是否可以滑动 |
| itemTouchHelperCallback | Item 触碰返回 |
| setOnItemSwipeListener() | 滑动监听事件 |
「itemTouchHelperCallback」
| 属性 | 功能 |
|---|---|
| setSwipeMoveFlags() | 设置滑动运动方向 |
//是否可以拖拽 true--是,false--否
itemListenerAdapter.draggableModule.isDragEnabled = true
//长按拖拽的目标组件ID
itemListenerAdapter.draggableModule.toggleViewId = R.id.btn_inner
//判断是否有目标组件
val isHasToggleView = itemListenerAdapter.draggableModule.hasToggleView()
Log.e("是否有目标组件==", "$isHasToggleView")//true
//是否可以长按拖动 配合toggleViewId使用
itemListenerAdapter.draggableModule.isDragOnLongPressEnabled = true
//拖拽监听事件
itemListenerAdapter.draggableModule.setOnItemDragListener(object : OnItemDragListener {
//拖拽开始
override fun onItemDragStart(viewHolder: RecyclerView.ViewHolder?, pos: Int) {
Log.e("拖拽===", "onItemDragStart")
}
//拖拽中
override fun onItemDragMoving(
source: RecyclerView.ViewHolder?,
from: Int,
target: RecyclerView.ViewHolder?,
to: Int
) {
Log.e("拖拽===", "onItemDragMoving")
}
//拖拽结束
override fun onItemDragEnd(viewHolder: RecyclerView.ViewHolder?, pos: Int) {
Log.e("拖拽===", "onItemDragEnd")
}
})
//是否可以滑动 true--是,false--否
itemListenerAdapter.draggableModule.isSwipeEnabled = true
//设置滑动运动方向
itemListenerAdapter.draggableModule.itemTouchHelperCallback.setSwipeMoveFlags(
//前滑或者后滑
ItemTouchHelper.START or ItemTouchHelper.END
)
//滑动监听事件
itemListenerAdapter.draggableModule.setOnItemSwipeListener(object : OnItemSwipeListener {
//滑动开始
override fun onItemSwipeStart(viewHolder: RecyclerView.ViewHolder?, pos: Int) {
Log.e("滑动===", "onItemSwipeStart")
}
//清除View
override fun clearView(viewHolder: RecyclerView.ViewHolder?, pos: Int) {
Log.e("滑动===", "clearView")
}
//滑动了
override fun onItemSwiped(viewHolder: RecyclerView.ViewHolder?, pos: Int) {
Log.e("滑动===", "onItemSwiped")
}
//滑动中
override fun onItemSwipeMoving(
canvas: Canvas?,
viewHolder: RecyclerView.ViewHolder?,
dX: Float,
dY: Float,
isCurrentlyActive: Boolean
) {
Log.e("滑动===", "onItemSwipeMoving")
}
})
效果图
顶部--向上加载事件
「重要」 首先需要 adapter 实现接口 UpFetchModule。
「参考」 练习项目里的 MoreListenerActivity 相关。
使用场景
向上加载:聊天记录功能
相关属性
「BaseQuickAdapter」
| 属性 | 功能 |
|---|---|
| UpFetchModule | 向上加载模块 |
「UpFetchModule」
| 属性 | 功能 |
|---|---|
| isUpFetchEnable | 是否可以向上加载 |
| setOnUpFetchListener() | 是否可以向上加载 |
| isUpFetchEnable | 向上加载监听 |
| isUpFetching | 是否在向上加载中 |
| startUpFetchPosition | 向上加载的启动位置 |
//是否可以向上加载 true-是 false-否
moreListenerAdapter.upFetchModule.isUpFetchEnable = true
//向上加载监听 --增加数据
moreListenerAdapter.upFetchModule.setOnUpFetchListener { addData() }
//是否在向上加载中
val isUpFetching = moreListenerAdapter.upFetchModule.isUpFetching
Log.e("是否在上拉中", "$isUpFetching")
//向上加载的启动位置,默认是1
moreListenerAdapter.upFetchModule.startUpFetchPosition = 2
/**
* 向上加载数据
*/
private fun addData() {
//是否在向上加载中 true--是
moreListenerAdapter.upFetchModule.isUpFetching = true
//延迟300添加数据,这里避免出现IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
mBinding.rvData.postDelayed({
moreListenerAdapter.addData(0, getData())
}, 300)
//是否在向上加载中 false--否
moreListenerAdapter.upFetchModule.isUpFetching = false
}
注意事项
「错误内容」:
IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
「解决方案」:
延迟添加数据。
mBinding.rvData.postDelayed({
moreListenerAdapter.addData(0, getData())
}, 300)
效果图
底部--加载更多事件
「重要」 首先需要 adapter 实现接口 LoadMoreModule。
「参考」 练习项目里的 DownMoreListenerActivity 相关。
使用场景
加载更多:收藏列表
相关属性
「BaseQuickAdapter」
| 属性 | 功能 |
|---|---|
| LoadMoreModule | 向下加载更多模块 |
「LoadMoreModule」
| 属性 | 功能 |
|---|---|
| isEnableLoadMore | 是否打开加载更多 |
| setOnLoadMoreListener() | 设置加载监听事件 |
| isAutoLoadMore | 是否打开自动加载更多 |
| isEnableLoadMoreIfNotFullPage | 当自动加载 isAutoLoadMore 开启,同时数据不满一屏时,是否继续执行自动加载更多 |
| enableLoadMoreEndClick | 所有数据加载完成后是否允许点击 |
| isLoading | 是否加载中 |
| preLoadNumber | 预加载的位置(默认为 1) |
| hasLoadMoreView() | 是否有加载中视图 |
| loadMoreViewPosition | 可以加载更多视图的位置 |
| checkDisableLoadMoreIfNotFullPage() | 用来检查数据是否满一屏 |
| loadMoreToLoading() | 状态手动置为“加载中” |
| loadMoreComplete() | 本次数据加载完毕 |
| loadMoreFail() | 本次数据加载错误 |
| loadMoreEnd() | 所有数据加载完成 |
//是否打开加载更多 true-是 false-否
downMoreListenerAdapter.loadMoreModule.isEnableLoadMore = true
//设置加载监听事件
downMoreListenerAdapter.loadMoreModule.setOnLoadMoreListener { addData() }
//是否打开自动加载更多 true-是 false-否
downMoreListenerAdapter.loadMoreModule.isAutoLoadMore = true
//当自动加载isAutoLoadMore开启,同时数据不满一屏时,是否继续执行自动加载更多
downMoreListenerAdapter.loadMoreModule.isEnableLoadMoreIfNotFullPage = true
//所有数据加载完成后是否允许点击 true-是 false-否
downMoreListenerAdapter.loadMoreModule.enableLoadMoreEndClick = true
//是否加载中
val isLoading = downMoreListenerAdapter.loadMoreModule.isLoading
Log.e("是否加载中===", "$isLoading")
//预加载的位置(默认为1)
downMoreListenerAdapter.loadMoreModule.preLoadNumber = 2
//是否有加载中视图
val isHas = downMoreListenerAdapter.loadMoreModule.hasLoadMoreView()
Log.e("是否有加载中视图===", "$isHas")
//可以加载更多视图的位置
val position = downMoreListenerAdapter.loadMoreModule.loadMoreViewPosition
Log.e("可以加载更多的位置===", "$position")
//用来检查数据是否满一屏
val isCheck = downMoreListenerAdapter.loadMoreModule.checkDisableLoadMoreIfNotFullPage()
Log.e("用来检查数据是否满一屏===", "$isCheck")
//状态手动置为“加载中”,并且会调用加载更多监听 一般情况下,不需要自己设置'加载中'状态
downMoreListenerAdapter.loadMoreModule.loadMoreToLoading()
//本次数据加载错误
downMoreListenerAdapter.loadMoreModule.loadMoreFail()
//所有数据加载完成
downMoreListenerAdapter.loadMoreModule.loadMoreEnd(true)
自定义视图
在某些场景中,我们需要不同样式的加载效果(比如英文版),这时候用自定义试图来实现。
「参考」 练习项目里的 CustomDownMoreView 相关。
「LoadMoreModule」
| 属性 | 功能 |
|---|---|
| loadMoreView | 设置加载更多自定义布局 |
「LoadMoreModuleConfig」
| 属性 | 功能 |
|---|---|
| defLoadMoreView | 设置全局的加载更多自定义布局 |
- view_cuostom_load_more.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40">
<LinearLayout
android:id="@+id/load_more_loading_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/loading_progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_4" />
<TextView
android:id="@+id/loading_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_4"
android:text="loading"
android:textColor="@android:color/black"
android:textSize="@dimen/sp_14" />
</LinearLayout>
<FrameLayout
android:id="@+id/load_more_load_fail_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:id="@+id/tv_prompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="load failed,click retry" />
</FrameLayout>
<FrameLayout
android:id="@+id/load_more_load_complete_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="click more data"
android:textColor="@android:color/darker_gray" />
</FrameLayout>
<FrameLayout
android:id="@+id/load_more_load_end_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="no more data"
android:textColor="@android:color/darker_gray" />
</FrameLayout>
</FrameLayout>
</layout>
- CustomDownMoreView.java
package com.elaine.lib_baserecyclerviewadapterhelper.downmorelistenerpackage
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.chad.library.adapter.base.loadmore.BaseLoadMoreView
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.elaine.lib_baserecyclerviewadapterhelper.R
/**
* 自定义底部--加载更多视图
*/
class CustomDownMoreView : BaseLoadMoreView() {
override fun getLoadComplete(holder: BaseViewHolder): View {
return holder.getView(R.id.load_more_load_complete_view)
}
override fun getLoadEndView(holder: BaseViewHolder): View {
return holder.getView(R.id.load_more_load_end_view)
}
override fun getLoadFailView(holder: BaseViewHolder): View {
return holder.getView(R.id.load_more_load_fail_view)
}
override fun getLoadingView(holder: BaseViewHolder): View {
return holder.getView(R.id.load_more_loading_view)
}
override fun getRootView(parent: ViewGroup): View {
return LayoutInflater.from(parent.context)
.inflate(R.layout.view_cuostom_load_more, parent, false);
}
}
- 使用方式
「Application」 全局初始化,通用型。
/**
* 初始化自定义底部--加载更多视图
* 全局通用
*/
private fun initCustomDownMoreView() {
LoadMoreModuleConfig.defLoadMoreView = CustomDownMoreView()
}
「Adapter」 单独设置。
//设置加载更多布局--自定义布局 Adapter单独设置(优先于全局设置)
downMoreListenerAdapter.loadMoreModule.loadMoreView = CustomDownMoreView()