当前位置: 首页 > news >正文

做任务网站有哪些内容上海建筑公司排名

做任务网站有哪些内容,上海建筑公司排名,临沂 网站建设,常州微信网站建设咨询背景 项目中首页列表页需要统计每个item的曝光情况,给产品运营提供数据报表分析用户行为,于是封装了一个通用的列表Item曝光工具,方便曝光埋点上报 源码分析 核心就是监听RecyclerView的滚动,在滚动状态为SCROLL_STATE_IDLE的时…

背景

项目中首页列表页需要统计每个item的曝光情况,给产品运营提供数据报表分析用户行为,于是封装了一个通用的列表Item曝光工具,方便曝光埋点上报

源码分析

  • 核心就是监听RecyclerView的滚动,在滚动状态为SCROLL_STATE_IDLE的时候开始计算哪些item是可见的
private fun calculateVisibleItemInternal() {if (!isRecording) {return}val lastRange = currVisibleRangeval currRange = findItemVisibleRange()val newVisibleItemPosList = createCurVisiblePosList(lastRange, currRange)visibleItemCheckTasks.forEach {it.updateVisibleRange(currRange)}if (newVisibleItemPosList.isNotEmpty()) {VisibleCheckTimerTask(newVisibleItemPosList, this, threshold).also {visibleItemCheckTasks.add(it)}.execute()}currVisibleRange = currRange}
  • 根据LayoutManager找出当前可见item的范围,剔除掉显示不到80%的item
private fun findItemVisibleRange(): IntRange {return when (val lm = currRecyclerView?.layoutManager) {is GridLayoutManager -> {val first = lm.findFirstVisibleItemPosition()val last = lm.findLastVisibleItemPosition()return fixCurRealVisibleRange(first, last)}is LinearLayoutManager -> {val first = lm.findFirstVisibleItemPosition()val last = lm.findLastVisibleItemPosition()return fixCurRealVisibleRange(first, last)}is StaggeredGridLayoutManager -> {val firstItems = IntArray(lm.spanCount)lm.findFirstVisibleItemPositions(firstItems)val lastItems = IntArray(lm.spanCount)lm.findLastVisibleItemPositions(lastItems)val first = when (RecyclerView.NO_POSITION) {firstItems[0] -> {firstItems[lm.spanCount - 1]}firstItems[lm.spanCount - 1] -> {firstItems[0]}else -> {min(firstItems[0], firstItems[lm.spanCount - 1])}}val last = when (RecyclerView.NO_POSITION) {lastItems[0] -> {lastItems[lm.spanCount - 1]}lastItems[lm.spanCount - 1] -> {lastItems[0]}else -> {max(lastItems[0], lastItems[lm.spanCount - 1])}}return fixCurRealVisibleRange(first, last)}else -> {IntRange.EMPTY}}}
  • 对可见的item进行分组,形成一个位置列表,上一次和这一次的分开组队
private fun createCurVisiblePosList(lastRange: IntRange, currRange: IntRange): List<Int> {val result = mutableListOf<Int>()currRange.forEach { pos ->if (pos !in lastRange) {result.add(pos)}}return result}
  • 将分组好的列表装进一个定时的task,在延迟一个阈值的时间后执行onVisibleCheck
class VisibleCheckTimerTask(input: List<Int>,private val callback: VisibleCheckCallback,private val delay: Long
) : Runnable {private val visibleList = mutableListOf<Int>()private var isExecuted = falseinit {visibleList.addAll(input)}fun updateVisibleRange(keyRange: IntRange) {val iterator = visibleList.iterator()while (iterator.hasNext()) {val entry = iterator.next()if (entry !in keyRange) {iterator.remove()}}}override fun run() {callback.onVisibleCheck( this, visibleList)}fun execute() {if (isExecuted) {return}mHandler.postDelayed(this, delay)isExecuted = true}fun cancel() {mHandler.removeCallbacks(this)}interface VisibleCheckCallback {fun onVisibleCheck(task: VisibleCheckTimerTask, visibleList: List<Int>)}
}
  • 达到阈值后,再获取一遍可见item的范围,对于仍然可见的item回调onItemShow
override fun onVisibleCheck(task: VisibleCheckTimerTask, visibleList: List<Int>) {val visibleRange = findItemVisibleRange()visibleList.forEach {if (it in visibleRange) {notifyItemShow(it)}}visibleItemCheckTasks.remove(task)}

完整源码

val mHandler = Handler(Looper.getMainLooper())class ListItemExposeUtil(private val threshold: Long = 100): RecyclerView.OnScrollListener(), VisibleCheckTimerTask.VisibleCheckCallback {private var currRecyclerView: RecyclerView? = nullprivate var isRecording = falseprivate var currVisibleRange: IntRange = IntRange.EMPTYprivate val visibleItemCheckTasks = mutableListOf<VisibleCheckTimerTask>()private val itemShowListeners = mutableListOf<OnItemShowListener>()fun attachTo(recyclerView: RecyclerView) {recyclerView.addOnScrollListener(this)currRecyclerView = recyclerView}fun start() {isRecording = truecurrRecyclerView?.post {calculateVisibleItemInternal()}}fun stop() {visibleItemCheckTasks.forEach {it.cancel()}visibleItemCheckTasks.clear()currVisibleRange = IntRange.EMPTYisRecording = false}fun detach() {if (isRecording) {stop()}itemShowListeners.clear()currRecyclerView?.removeOnScrollListener(this)currRecyclerView = null}override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {if (newState == RecyclerView.SCROLL_STATE_IDLE) {calculateVisibleItemInternal()}}private fun calculateVisibleItemInternal() {if (!isRecording) {return}val lastRange = currVisibleRangeval currRange = findItemVisibleRange()val newVisibleItemPosList = createCurVisiblePosList(lastRange, currRange)visibleItemCheckTasks.forEach {it.updateVisibleRange(currRange)}if (newVisibleItemPosList.isNotEmpty()) {VisibleCheckTimerTask(newVisibleItemPosList, this, threshold).also {visibleItemCheckTasks.add(it)}.execute()}currVisibleRange = currRange}private fun findItemVisibleRange(): IntRange {return when (val lm = currRecyclerView?.layoutManager) {is GridLayoutManager -> {val first = lm.findFirstVisibleItemPosition()val last = lm.findLastVisibleItemPosition()return fixCurRealVisibleRange(first, last)}is LinearLayoutManager -> {val first = lm.findFirstVisibleItemPosition()val last = lm.findLastVisibleItemPosition()return fixCurRealVisibleRange(first, last)}is StaggeredGridLayoutManager -> {val firstItems = IntArray(lm.spanCount)lm.findFirstVisibleItemPositions(firstItems)val lastItems = IntArray(lm.spanCount)lm.findLastVisibleItemPositions(lastItems)val first = when (RecyclerView.NO_POSITION) {firstItems[0] -> {firstItems[lm.spanCount - 1]}firstItems[lm.spanCount - 1] -> {firstItems[0]}else -> {min(firstItems[0], firstItems[lm.spanCount - 1])}}val last = when (RecyclerView.NO_POSITION) {lastItems[0] -> {lastItems[lm.spanCount - 1]}lastItems[lm.spanCount - 1] -> {lastItems[0]}else -> {max(lastItems[0], lastItems[lm.spanCount - 1])}}return fixCurRealVisibleRange(first, last)}else -> {IntRange.EMPTY}}}/*** 检查该item是否真实可见* view区域80%显示出来就算*/private fun checkItemCurrRealVisible(pos: Int): Boolean {val holder = currRecyclerView?.findViewHolderForAdapterPosition(pos)return if (holder == null) {false} else {val rect = Rect()holder.itemView.getGlobalVisibleRect(rect)if (holder.itemView.width > 0 && holder.itemView.height > 0) {return (rect.width() * rect.height() / (holder.itemView.width * holder.itemView.height).toFloat()) > 0.8f} else {return false}}}/*** 双指针寻找真实可见的item的范围(有一些item没显示完整剔除)*/private fun fixCurRealVisibleRange(first: Int, last: Int): IntRange {return if (first >= 0 && last >= 0) {var realFirst = firstwhile (!checkItemCurrRealVisible(realFirst) && realFirst <= last) {realFirst++}var realLast = lastwhile (!checkItemCurrRealVisible(realLast) && realLast >= realFirst) {realLast--}if (realFirst <= realLast) {realFirst..realLast} else {IntRange.EMPTY}} else {IntRange.EMPTY}}/*** 创建当前可见的item位置列表*/private fun createCurVisiblePosList(lastRange: IntRange, currRange: IntRange): List<Int> {val result = mutableListOf<Int>()currRange.forEach { pos ->if (pos !in lastRange) {result.add(pos)}}return result}/*** 达到阈值后,再获取一遍可见item的范围,对于仍然可见的item回调onItemShow*/override fun onVisibleCheck(task: VisibleCheckTimerTask, visibleList: List<Int>) {val visibleRange = findItemVisibleRange()visibleList.forEach {if (it in visibleRange) {notifyItemShow(it)}}visibleItemCheckTasks.remove(task)}private fun notifyItemShow(pos: Int) {itemShowListeners.forEach {it.onItemShow(pos)}}fun addItemShowListener(listener: OnItemShowListener) {itemShowListeners.add(listener)}
}interface OnItemShowListener {fun onItemShow(pos: Int)
}class VisibleCheckTimerTask(input: List<Int>,private val callback: VisibleCheckCallback,private val delay: Long) : Runnable {private val visibleList = mutableListOf<Int>()private var isExecuted = falseinit {visibleList.addAll(input)}fun updateVisibleRange(keyRange: IntRange) {val iterator = visibleList.iterator()while (iterator.hasNext()) {val entry = iterator.next()if (entry !in keyRange) {iterator.remove()}}}override fun run() {callback.onVisibleCheck(this, visibleList)}fun execute() {if (isExecuted) {return}mHandler.postDelayed(this, delay)isExecuted = true}fun cancel() {mHandler.removeCallbacks(this)}interface VisibleCheckCallback {fun onVisibleCheck(task: VisibleCheckTimerTask, visibleList: List<Int>)}
}
  • 测试代码
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述
    在这里插入图片描述
http://www.yayakq.cn/news/863042/

相关文章:

  • 网站建设与管理自考怎么制作wordpress主题
  • 怎么在企查查网站做企业认证注册公司流程和费用2020
  • 网站设计公司哪里好深圳网络科技公司大全
  • 卖汽车配件怎么做网站网站建设的目标与思路
  • 怎样制作公司网站wordpress商城开发费用
  • 如何用nat123做网站网站建设朱宁
  • seo优化或网站编辑软件开发视频网站
  • 做网站海口移动端响应式布局
  • 网站域名注册要多少钱做简单的网站链接
  • 建商城网站的平台中文网站 可以做谷歌推广吗
  • 网站怎么做用户登录数据库做设计常逛的网站
  • 怎么下学做衣服网站市直部门网站建设维护工作总结
  • 网站后台怎样登陆360建站模板
  • 网站优化软件开发如何做网站开发
  • wordpress引入qq咨询wordpress文章优化
  • 大型html5浅蓝色网站设计公司dede模板discuz插件
  • 东莞做网站的网络公司枣庄市庄里水库建设管理处网站
  • 深圳网站开发工程师网站seo入门基础教程
  • 建设企业网站官网登录建站公司的服务器
  • 老外做的中国方言网站湖北 商城网站建设
  • 网站数据库无锡营销型网站
  • 个人网站设计论文模板集约化网站建设项目
  • 装修网站制作汕头保安公司
  • lnmp搭建后怎么做网站photoshop+做网站logo
  • 做商务网站四川网站建设一站式服务商
  • 大型租车门户网站商业版源码网站建设全
  • 网页制作与网站管理商品详情页面模板html
  • 网站建设的广告词建设网站前端
  • 网站建设推广机构wordpress免登陆发布模块
  • 淮安建设网站常平镇网站仿做