網(wǎng)站首頁 編程語言 正文
本文實(shí)例為大家分享了Android RecyclerView曝光采集的具體代碼,供大家參考,具體內(nèi)容如下
一、背景
近期pm提出需要統(tǒng)計首頁商品的曝光亮,由于我們的首頁是用的recylerview實(shí)現(xiàn)的,這里就來講下如何使用監(jiān)聽recylerview的滾動事件來實(shí)現(xiàn)子view的曝光量統(tǒng)計,我們這里說的view都是列表中的子item條目(子view)
二、監(jiān)聽recylerview的滾動事件OnScrollListener
onScrollStateChanged:監(jiān)聽滾動狀態(tài)
onScrolled:監(jiān)聽滾動
我們接下來的統(tǒng)計工作,就是拿這兩個方法做文章。
//檢測recylerview的滾動事件 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { ? ? ? ?@Override ? ? ? ?public void onScrollStateChanged(RecyclerView recyclerView, int newState) { ? ? ? ? ? ?/* ? ? ? ? ? ?我這里通過的是停止?jié)L動后屏幕上可見view。如果滾動過程中的可見view也要統(tǒng)計,你可以根據(jù)newState去做區(qū)分 ? ? ? ? ? ?SCROLL_STATE_IDLE:停止?jié)L動 ? ? ? ? ? ?SCROLL_STATE_DRAGGING: 用戶慢慢拖動 ? ? ? ? ? ?SCROLL_STATE_SETTLING:慣性滾動 ? ? ? ? ? ?*/ ? ? ? ? ? ?if (newState == RecyclerView.SCROLL_STATE_IDLE) { ? ? ? ? ? ? ? ?..... ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?@Override ? ? ? ?public void onScrolled(RecyclerView recyclerView, int dx, int dy) { ? ? ? ? ? ?super.onScrolled(recyclerView, dx, dy); ? ? ? ? ? ........ ? ? ? ?} ? ?});
首先再次明確下,我們要統(tǒng)計的是用戶停止滑動時,顯示在屏幕的上控件。所以我們要監(jiān)測到onScrollStateChanged 方法中
newState == RecyclerView.SCROLL_STATE_IDLE 時,也就是用戶停止?jié)L動。然后在這里做文章
三、獲取屏幕內(nèi)可見條目的起始位置
這里的起始位置就是指我們屏幕當(dāng)中最上面和最下面條目的位置。比如下圖的0就是最上面的可見條目,3就是最下面的可見條目。我們次數(shù)的曝光view就是0,1,2,3 這個時候這四個條目顯示在屏幕中。我們這時就要對這4個view的曝光量進(jìn)行加1
那么接下來的重點(diǎn)就是要去獲取屏幕內(nèi)可見條目的起始位置。獲取到起始位置后,當(dāng)前屏幕里的可見條目就都能拿到了。
而recylerview的manager正好給我們提供的有對應(yīng)的方法。
findFirstVisibleItemPosition()和findLastVisibleItemPosition() 看字面意思就能知道這時干嘛用的。
但是我們的manager不止LinearLayoutManager一種,所以我們要做下區(qū)分
//這里我們用一個數(shù)組來記錄起始位置 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獲取起始位置有點(diǎn)復(fù)雜,如下
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; }
四、獲取到起始位置以后,我們就根據(jù)位置獲取到view及view中的數(shù)據(jù)
上面第三步拿到屏幕內(nèi)可見條目的起始位置以后,我們就用一個for循環(huán),獲取當(dāng)前屏幕內(nèi)可見的所有子view
for (int i = range[0]; i <= range[1]; i++) { ?View view = manager.findViewByPosition(i); ? recordViewCount(view); }
recordViewCount是我自己寫的用于獲取子view內(nèi)綁定數(shù)據(jù)的方法
//獲取view綁定的數(shù)據(jù) 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綁定的數(shù)據(jù),相應(yīng)的你要去在你的view里setTag,只有set了,才能get ? ? ItemData 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 + "----出現(xiàn)次數(shù):" + hashMap.get(key)); }
這里有幾點(diǎn)需要注意
這這里起始位置的view顯示區(qū)域如果不超過50%,就不算這個view可見,進(jìn)而也就不統(tǒng)計曝光。
我們通過view.getTag();獲取view里的數(shù)據(jù),必須在此之前setTag()數(shù)據(jù),我這里setTag是在viewholder中把數(shù)據(jù)set進(jìn)去的
到這里我們就實(shí)現(xiàn)了recylerview列表中view控件曝光量的統(tǒng)計了。下面貼出來完整的代碼給大家
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: 安卓曝光量統(tǒng)計工具類 ?* wechat:2501902696 ?*/ public class ViewShowCountUtils { ? ? //剛進(jìn)入列表時統(tǒng)計當(dāng)前屏幕可見views ? ? private boolean isFirstVisible = true; ? ? //用于統(tǒng)計曝光量的map ? ? private ConcurrentHashMap<String, Integer> hashMap = new ConcurrentHashMap<String, Integer>(); ? ? /* ? ? ?* 統(tǒng)計RecyclerView里當(dāng)前屏幕可見子view的曝光量 ? ? ?* ? ? ?* */ ? ? void recordViewShowCount(RecyclerView recyclerView) { ? ? ? ? hashMap.clear(); ? ? ? ? if (recyclerView == null || recyclerView.getVisibility() != View.VISIBLE) { ? ? ? ? ? ? return; ? ? ? ? } ? ? ? ? //檢測recylerview的滾動事件 ? ? ? ? recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onScrollStateChanged(RecyclerView recyclerView, int newState) { ? ? ? ? ? ? ? ? /* ? ? ? ? ? ? ? ? 我這里通過的是停止?jié)L動后屏幕上可見view。如果滾動過程中的可見view也要統(tǒng)計,你可以根據(jù)newState去做區(qū)分 ? ? ? ? ? ? ? ? SCROLL_STATE_IDLE:停止?jié)L動 ? ? ? ? ? ? ? ? SCROLL_STATE_DRAGGING: 用戶慢慢拖動 ? ? ? ? ? ? ? ? SCROLL_STATE_SETTLING:慣性滾動 ? ? ? ? ? ? ? ? */ ? ? ? ? ? ? ? ? if (newState == RecyclerView.SCROLL_STATE_IDLE) { ? ? ? ? ? ? ? ? ? ? getVisibleViews(recyclerView); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onScrolled(RecyclerView recyclerView, int dx, int dy) { ? ? ? ? ? ? ? ? super.onScrolled(recyclerView, dx, dy); ? ? ? ? ? ? ? ? //剛進(jìn)入列表時統(tǒng)計當(dāng)前屏幕可見views ? ? ? ? ? ? ? ? if (isFirstVisible) { ? ? ? ? ? ? ? ? ? ? getVisibleViews(recyclerView); ? ? ? ? ? ? ? ? ? ? isFirstVisible = false; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? }); ? ? } ? ? /* ? ? ?* 獲取當(dāng)前屏幕上可見的view ? ? ?* */ ? ? private void getVisibleViews(RecyclerView reView) { ? ? ? ? if (reView == null || reView.getVisibility() != View.VISIBLE || ? ? ? ? ? ? ? ? !reView.isShown() || !reView.getGlobalVisibleRect(new Rect())) { ? ? ? ? ? ? return; ? ? ? ? } ? ? ? ? //保險起見,為了不讓統(tǒng)計影響正常業(yè)務(wù),這里做下try-catch ? ? ? ? try { ? ? ? ? ? ? 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", "屏幕內(nèi)可見條目的起始位置:" + 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綁定的數(shù)據(jù) ? ? 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綁定的數(shù)據(jù),相應(yīng)的你要去在你的view里setTag,只有set了,才能get ? ? ? ? ItemData 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 + "----出現(xiàn)次數(shù):" + 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; ? ? } }
原文鏈接:https://blog.csdn.net/weixin_42600398/article/details/119796478
相關(guān)推薦
- 2022-05-28 C++?超詳細(xì)講解stack與queue的使用_C 語言
- 2022-07-25 Oracle中的序列SEQUENCE詳解_oracle
- 2022-04-10 用python實(shí)現(xiàn)文件備份_python
- 2022-06-13 jupyter?notebook內(nèi)核啟動失敗問題及解決方法_python
- 2022-11-18 一文搞懂正則表達(dá)式基礎(chǔ)語法以及如何應(yīng)用_正則表達(dá)式
- 2022-06-07 Python利用capstone實(shí)現(xiàn)反匯編_python
- 2022-05-18 python中l(wèi)eastsq函數(shù)的使用方法_python
- 2022-06-27 nginx中配置使用proxy?protocol協(xié)議的全過程_nginx
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支