一、背景

近期pm提出需要统计首页商品的曝光亮,由于我们的首页是用的recylerview实现的,这里就来讲下如何使用监听recylerview的滚动事件来实现子view的曝光量统计,我们这里说的view都是列表中的子item条目(子view)

二、监听recylerview的滚动事件OnScrollListener

onScrollStateChanged:监听滚动状态
onScrolled:监听滚动
我们接下来的统计工作,就是拿这两个方法做文章。

//检测recylerview的滚动事件
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {/*我这里通过的是停止滚动后屏幕上可见view。如果滚动过程中的可见view也要统计,你可以根据newState去做区分SCROLL_STATE_IDLE:停止滚动SCROLL_STATE_DRAGGING: 用户慢慢拖动SCROLL_STATE_SETTLING:惯性滚动*/if (newState == RecyclerView.SCROLL_STATE_IDLE) {.....}}@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);........}});

首先再次明确下,我们要统计的是用户停止滑动时,显示在屏幕的上控件。所以我们要监测到onScrollStateChanged 方法中
newState == RecyclerView.SCROLL_STATE_IDLE 时,也就是用户停止滚动。然后在这里做文章

三、获取屏幕内可见条目的起始位置

这里的起始位置就是指我们屏幕当中最上面和最下面条目的位置。比如下图的0就是最上面的可见条目,3就是最下面的可见条目。我们次数的曝光view就是0,1,2,3 这个时候这四个条目显示在屏幕中。我们这时就要对这4个view的曝光量进行加1
那么接下来的重点就是要去获取屏幕内可见条目的起始位置。获取到起始位置后,当前屏幕里的可见条目就都能拿到了。
而recylerview的manager正好给我们提供的有对应的方法。
findFirstVisibleItemPosition()和findLastVisibleItemPosition() 看字面意思就能知道这时干嘛用的。
但是我们的manager不止LinearLayoutManager一种,所以我们要做下区分

//这里我们用一个数组来记录起始位置
int[] range = new int[2];
RecyclerView.LayoutManager manager = reView.getLayoutManager();
if (manager instanceof LinearLayoutManager) {range = findRangeLinear((LinearLayoutManager) manager);
} else if (manager instanceof GridLayoutManager) {range = findRangeGrid((GridLayoutManager) manager);
} else if (manager instanceof StaggeredGridLayoutManager) {range = findRangeStaggeredGrid((StaggeredGridLayoutManager) manager);
}

LinearLayoutManager和GridLayoutManager获取起始位置方法如下

private int[] findRangeLinear(LinearLayoutManager manager) {int[] range = new int[2];range[0] = manager.findFirstVisibleItemPosition();range[1] = manager.findLastVisibleItemPosition();return range;
}
private int[] findRangeGrid(GridLayoutManager manager) {int[] range = new int[2];range[0] = manager.findFirstVisibleItemPosition();range[1] = manager.findLastVisibleItemPosition();return range;
}

StaggeredGridLayoutManager获取起始位置有点复杂,如下

private int[] findRangeStaggeredGrid(StaggeredGridLayoutManager manager) {int[] startPos = new int[manager.getSpanCount()];int[] endPos = new int[manager.getSpanCount()];manager.findFirstVisibleItemPositions(startPos);manager.findLastVisibleItemPositions(endPos);int[] range = findRange(startPos, endPos);return range;
}private int[] findRange(int[] startPos, int[] endPos) {int start = startPos[0];int end = endPos[0];for (int i = 1; i < startPos.length; i++) {if (start > startPos[i]) {start = startPos[i];}}for (int i = 1; i < endPos.length; i++) {if (end < endPos[i]) {end = endPos[i];}}int[] res = new int[]{start, end};return res;
}

四、获取到起始位置以后,我们就根据位置获取到view及view中的数据

上面第三步拿到屏幕内可见条目的起始位置以后,我们就用一个for循环,获取当前屏幕内可见的所有子view

for (int i = range[0]; i <= range[1]; i++) {View view = manager.findViewByPosition(i);recordViewCount(view);
}

recordViewCount是我自己写的用于获取子view内绑定数据的方法

//获取view绑定的数据
private void recordViewCount(View view) {if (view == null || view.getVisibility() != View.VISIBLE ||!view.isShown() || !view.getGlobalVisibleRect(new Rect())) {return;}int top = view.getTop();int halfHeight = view.getHeight() / 2;int screenHeight = UiUtils.getScreenHeight((Activity) view.getContext());int statusBarHeight = UiUtils.getStatusBarHeight(view.getContext());if (top < 0 && Math.abs(top) > halfHeight) {return;}if (top > screenHeight - halfHeight - statusBarHeight) {return;}//这里获取的是我们view绑定的数据,相应的你要去在你的view里setTag,只有set了,才能getItemData tag = (ItemData) view.getTag();String key = tag.toString();if (TextUtils.isEmpty(key)) {return;}hashMap.put(key, !hashMap.containsKey(key) ? 1 : (hashMap.get(key) + 1));Log.i("qcl0402", key + "----出现次数:" + hashMap.get(key));
}

这里有几点需要注意

  • 这这里起始位置的view显示区域如果不超过50%,就不算这个view可见,进而也就不统计曝光。
  • 我们通过view.getTag();获取view里的数据,必须在此之前setTag()数据,我这里setTag是在viewholder中把数据set进去的

到这里我们就实现了recylerview列表中view控件曝光量的统计了。下面贴出来完整的代码给大家

package com.example.qcl.demo.xuexi.baoguang;import android.app.Activity;
import android.graphics.Rect;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;import com.example.qcl.demo.utils.UiUtils;import java.util.concurrent.ConcurrentHashMap;/*** 2019/4/2 13:31* author: qcl* desc: 安卓曝光量统计工具类* wechat:2501902696*/
public class ViewShowCountUtils {//刚进入列表时统计当前屏幕可见viewsprivate boolean isFirstVisible = true;//用于统计曝光量的mapprivate ConcurrentHashMap<String, Integer> hashMap = new ConcurrentHashMap<String, Integer>();/** 统计RecyclerView里当前屏幕可见子view的曝光量** */void recordViewShowCount(RecyclerView recyclerView) {hashMap.clear();if (recyclerView == null || recyclerView.getVisibility() != View.VISIBLE) {return;}//检测recylerview的滚动事件recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {/*我这里通过的是停止滚动后屏幕上可见view。如果滚动过程中的可见view也要统计,你可以根据newState去做区分SCROLL_STATE_IDLE:停止滚动SCROLL_STATE_DRAGGING: 用户慢慢拖动SCROLL_STATE_SETTLING:惯性滚动*/if (newState == RecyclerView.SCROLL_STATE_IDLE) {getVisibleViews(recyclerView);}}@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);//刚进入列表时统计当前屏幕可见viewsif (isFirstVisible) {getVisibleViews(recyclerView);isFirstVisible = false;}}});}/** 获取当前屏幕上可见的view* */private void getVisibleViews(RecyclerView reView) {if (reView == null || reView.getVisibility() != View.VISIBLE ||!reView.isShown() || !reView.getGlobalVisibleRect(new Rect())) {return;}//保险起见,为了不让统计影响正常业务,这里做下try-catchtry {int[] range = new int[2];RecyclerView.LayoutManager manager = reView.getLayoutManager();if (manager instanceof LinearLayoutManager) {range = findRangeLinear((LinearLayoutManager) manager);} else if (manager instanceof GridLayoutManager) {range = findRangeGrid((GridLayoutManager) manager);} else if (manager instanceof StaggeredGridLayoutManager) {range = findRangeStaggeredGrid((StaggeredGridLayoutManager) manager);}if (range == null || range.length < 2) {return;}Log.i("qcl0402", "屏幕内可见条目的起始位置:" + range[0] + "---" + range[1]);for (int i = range[0]; i <= range[1]; i++) {View view = manager.findViewByPosition(i);recordViewCount(view);}} catch (Exception e) {e.printStackTrace();}}//获取view绑定的数据private void recordViewCount(View view) {if (view == null || view.getVisibility() != View.VISIBLE ||!view.isShown() || !view.getGlobalVisibleRect(new Rect())) {return;}int top = view.getTop();int halfHeight = view.getHeight() / 2;int screenHeight = UiUtils.getScreenHeight((Activity) view.getContext());int statusBarHeight = UiUtils.getStatusBarHeight(view.getContext());if (top < 0 && Math.abs(top) > halfHeight) {return;}if (top > screenHeight - halfHeight - statusBarHeight) {return;}//这里获取的是我们view绑定的数据,相应的你要去在你的view里setTag,只有set了,才能getItemData tag = (ItemData) view.getTag();String key = tag.toString();if (TextUtils.isEmpty(key)) {return;}hashMap.put(key, !hashMap.containsKey(key) ? 1 : (hashMap.get(key) + 1));Log.i("qcl0402", key + "----出现次数:" + hashMap.get(key));}private int[] findRangeLinear(LinearLayoutManager manager) {int[] range = new int[2];range[0] = manager.findFirstVisibleItemPosition();range[1] = manager.findLastVisibleItemPosition();return range;}private int[] findRangeGrid(GridLayoutManager manager) {int[] range = new int[2];range[0] = manager.findFirstVisibleItemPosition();range[1] = manager.findLastVisibleItemPosition();return range;}private int[] findRangeStaggeredGrid(StaggeredGridLayoutManager manager) {int[] startPos = new int[manager.getSpanCount()];int[] endPos = new int[manager.getSpanCount()];manager.findFirstVisibleItemPositions(startPos);manager.findLastVisibleItemPositions(endPos);int[] range = findRange(startPos, endPos);return range;}private int[] findRange(int[] startPos, int[] endPos) {int start = startPos[0];int end = endPos[0];for (int i = 1; i < startPos.length; i++) {if (start > startPos[i]) {start = startPos[i];}}for (int i = 1; i < endPos.length; i++) {if (end < endPos[i]) {end = endPos[i];}}int[] res = new int[]{start, end};return res;}
}

Android RecyclerView曝光采集相关推荐

  1. Android recyclerView曝光统计

    一.背景 产品需求中我们经常会有统计recyclerView的每个item的曝光需求: recyclerView上下滚动每个item从不可见进入到屏幕可见范围(这里包含item的可见范围,还有item ...

  2. Android RecyclerView 基本使用

    Android RecyclerView 基本使用 概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用. 据官方的介绍,该控件用 ...

  3. Android RecyclerView添加Header头部

     Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...

  4. android 炫酷背景,炫酷-背景图垂直循环滚动登录页,Android RecyclerView实现

    炫酷-背景图上下循环滚动登录页,Android RecyclerView实现方法 某站的登录页背景不停循环滚动,和街边的广告箱很像,感觉不错我也心动了.决定高仿一下,参考了几篇文章后就动手了. 实现步 ...

  5. android批量删除图片,Android RecyclerView单点、批量数据元素项目item的增加、删除和移动...

    Android RecyclerView单点.批量数据元素项目item的增加.删除和移动 前文附录1,2介绍了基本的Android RecyclerView单点.批量元素项目的更新.现在给出其他比较重 ...

  6. Android RecyclerView设计通用Adapter

    RecylerView 的使用频率现在也算做是很高了吧?使用起来的确是挺方便的,也容易实现一些比较好看的效果 一.一般步骤 一般的设计流程都是如下所示 首先是需要一个 JavaBean 来承载数据,包 ...

  7. Android RecyclerView 监听滑动

    今天,简单讲讲Android 如何监听滑动. 不废话了,主要是需要做一个功能,实现RecyclerView滑动时,让一个标题栏固定显示在顶部. 基本知识: 列表的滚动一般分为两种: 手指按下 -> ...

  8. android 水平方向瀑布流,Android RecyclerView(瀑布流)水平/垂直方向分割线

     Android RecyclerView(瀑布流)水平/垂直方向分割线 Android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现Recycle ...

  9. Android实现支付宝AR功能,Android RecyclerView 实现支付宝首页效果

    Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...

最新文章

  1. 耐高温防腐计算机电缆,防腐耐高温计算机屏蔽控制电缆
  2. Java 面试必备 | 7 个实验带你精通 JVM 性能调优!
  3. 初学者:如何使用虚拟PC将Windows 7安装到虚拟机
  4. linux版本i686,linux-x86_64平台上的gcc i686
  5. 关于JPA方法名创建自动查询
  6. java 日期处理_JAVA 日期处理大全
  7. java回收内存_JAVA之内存回收
  8. Intellij idea 设置关闭自动更新
  9. 阿里云云计算 27 在线实验--SLB初体验
  10. Android 项目总结(实现捕捉人脸)
  11. 【C语言】深度剖析数据在内存中的存储
  12. 上海航芯 | 全自动咖啡机设计方案
  13. 微信小程序radio单选框
  14. opencv-python中 boundingRect(cnt)以及cv2.rectangle用法
  15. TMS320C6678开发笔记---IBL编译与分析4
  16. 关于异步的几点思考......
  17. java Date.getDay()
  18. 中国机械式停车设备行业投资建议与发展机遇研究报告2022版
  19. 【开源项目】Flutter版 玩安卓,android开发模拟器推荐
  20. Android利用Cookie实现码源登录效果二

热门文章

  1. 大话西游手游服务器维护要多久,大话西游手游2018年8月2日维护公告
  2. 孤岛求生记JAVA攻略_我的世界1.7.10贝爷的真实世界生存整合包
  3. netty源码编译环境搭建
  4. Linux 终端backspace删除不了内容
  5. Pyppeteer爬取移动端微博评论区简单案例
  6. Show()跟ShowDialog()的区别
  7. 通用的毕业设计商城类项目,涵盖一切商城类的课题,适合写商城类的课设或毕设
  8. 中职学校计算机专业课程设置,中等职业学校计算机专业课程体系
  9. Milvus 在唯品会搜索推荐的实践
  10. HEVC提案下载地址