安卓系列之第三方库 BaseRecyclerViewAdapterHelper 监听事件篇

954 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

本章记录点击,长按,拖拽,滑动,顶部-向上加载,底部-加载更多等监听事件。

练习项目 GitHub 地址

github.com/ElaineTaylo…

点击和长按事件

「参考」 练习项目里的 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是否可以滑动
itemTouchHelperCallbackItem 触碰返回
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(0getData())
}, 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设置全局的加载更多自定义布局
  1. 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>
  1. 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);
    }


}
  1. 使用方式

「Application」 全局初始化,通用型。

/**
 * 初始化自定义底部--加载更多视图
 * 全局通用
 */
private fun initCustomDownMoreView() {
   LoadMoreModuleConfig.defLoadMoreView = CustomDownMoreView()
}

「Adapter」 单独设置。

//设置加载更多布局--自定义布局  Adapter单独设置(优先于全局设置)
downMoreListenerAdapter.loadMoreModule.loadMoreView = CustomDownMoreView()

效果图