近期pm提出須要統計首頁商品的曝光亮,因爲咱們的首頁是用的recylerview實現的,這裏就來說下如何使用監聽recylerview的滾動事件來實現子view的曝光量統計,咱們這裏說的view都是列表中的子item條目(子view)java
先來看下統計結果圖 android
這裏我再也不囉嗦,recylerview最基礎的使用。編程
onScrollStateChanged:監聽滾動狀態 onScrolled:監聽滾動 咱們接下來的統計工做,就是拿這兩個方法作文章。數組
//檢測recylerview的滾動事件
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
/*
我這裏經過的是中止滾動後屏幕上可見view。若是滾動過程當中的可見view也要統計,你能夠根據newState去作區分
SCROLL_STATE_IDLE:中止滾動
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);
........
}
});
複製代碼
首先再次明確下,咱們要統計的是用戶中止滑動時,顯示在屏幕的上控件。因此咱們要監測到onScrollStateChanged 方法中 newState == RecyclerView.SCROLL_STATE_IDLE 時,也就是用戶中止滾動。而後在這裏作文章。bash
這裏的起始位置就是指咱們屏幕當中最上面和最下面條目的位置。好比下圖的0就是最上面的可見條目,3就是最下面的可見條目。咱們次數的曝光view就是0,1,2,3 這個時候這四個條目顯示在屏幕中。咱們這時就要對這4個view的曝光量進行加1 微信
//這裏咱們用一個數組來記錄起始位置
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獲取起始位置方法以下app
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獲取起始位置有點複雜,以下ide
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;
}
複製代碼
上面第三步拿到屏幕內可見條目的起始位置之後,咱們就用一個for循環,獲取當前屏幕內可見的全部子view工具
for (int i = range[0]; i <= range[1]; i++) {
View view = manager.findViewByPosition(i);
recordViewCount(view);
}
複製代碼
recordViewCount是我本身寫的用於獲取子view內綁定數據的方法ui
//獲取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了,才能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 + "----出現次數:" + hashMap.get(key));
}
複製代碼
這裏有幾點須要注意
到這裏咱們就實現了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 {
//剛進入列表時統計當前屏幕可見views
private boolean isFirstVisible = true;
//用於統計曝光量的map
private 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() {
@Override
public 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);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//剛進入列表時統計當前屏幕可見views
if (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-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", "屏幕內可見條目的起始位置:" + 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了,才能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 + "----出現次數:" + 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;
}
}
複製代碼
使用就是在咱們的recylerview設置完數據之後,把recylerview傳遞進去就能夠了。以下圖:
咱們統計到曝光量,拿到曝光view綁定的數據,就能夠結合後面的view點擊,來看下那些商品view的曝光量高,那些商品的轉化率高。固然,這都是運營小夥伴的事了,咱們只須要負責把曝光量統計到便可。 若是你有任何編程方面的問題,能夠加我微信交流 2501902696(備註編程)
by:年糕媽媽qcl