Android RecyclerView曝光采集的实现方法

本文实例为大家分享了Android RecyclerView曝光采集的具体代码,供大家参考,具体内容如下

一、背景

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

二、监听recylerview的滚动事件OnScrollListener

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 时,也就是用户停止滚动。然后在这里做文章

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

这里的起始位置就是指我们屏幕当中最上面和最下面条目的位置。比如下图的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了,才能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));
}

这里有几点需要注意

这这里起始位置的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 {

    //刚进入列表时统计当前屏幕可见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;
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android Camera2开启自动曝光功能

    本文实例为大家分享了Android Camera2开启自动曝光功能的具体代码,供大家参考,具体内容如下 首先,打开3A的总开关: CaptureRequest.Builder builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); 获取支持的 AE FPS Range: List<int[]> ae_fps_ranges = new ArrayList<>(); for (Range&l

  • Android RecyclerView曝光采集的实现方法

    本文实例为大家分享了Android RecyclerView曝光采集的具体代码,供大家参考,具体内容如下 一.背景 近期pm提出需要统计首页商品的曝光亮,由于我们的首页是用的recylerview实现的,这里就来讲下如何使用监听recylerview的滚动事件来实现子view的曝光量统计,我们这里说的view都是列表中的子item条目(子view) 二.监听recylerview的滚动事件OnScrollListener onScrollStateChanged:监听滚动状态onScrolled

  • Android RecyclerView 滚动到中间位置的方法示例

    最近看到QQ音乐的歌词每次滑动后都可以滚回到中间位置.觉得甚是神奇,打开开发者模式显示布局,发现歌词部分不是采用 android 控件的写的,应该是前端写的.于是,我想,能不能用 recyclerView 实现这个自动回滚到中间位置呢. 功夫不负有心人,查找了一些资料之后,终于搞定了. 下面由我细细讲来. 目标 点击某个条目,在经过4s无任何操作之后,该条目滚动到中间位置显示.点击后,用户在滑动,等用户不操作后再开始延时.用户多次点击,记最后一次点击位置. 分析 首先先考虑,滚动到指定位置是如何

  • Android RecyclerView设置下拉刷新的实现方法

    Android RecyclerView设置下拉刷新的实现方法 1 集成 SwipeRefreshLayout 1.1 xml布局文件中使用 <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh" android:layout_width = "match_parent" android:layout_height = "match_parent" &g

  • Android RecyclerView使用方法详解

    本文为大家分享了Android RecyclerView使用方法,供大家参考,具体内容如下 1.RecyclerView 是在Android support - v7 里面提供的 新的列表组件,用来替代传统的ListView. . 要使用RecyclerView 需要给我工程添加 support:recycle-v7 的支持: app 右键 - Open Module Settings - Dependencies(依赖项) - 点 + 号 - 添加一个库 upport:recycle-v7 

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

  • Android中RecyclerView实现分页滚动的方法详解

    一.需求分析 最近公司项目要实现一个需求要满足以下功能: 1)显示一个 list 列表, item 数量不固定. 2)实现翻页功能,一次翻一页. 3)实现翻至某一页功能. 下面介绍通过 RecyclerView 实现该需求的实现过程(效果图如下). 二.功能实现 2.1 OnTouchListener 记录当前开始滑动位置 要实现翻页滑动首先我们要确定是向前翻页还是向后翻页,这里通过记录开始翻页前当前的位置和滑动后的位置比较即可得知,下面选择手指触摸按下时滑动的位置为当前开始滑动位置: //当前

  • Android RecyclerView详解及简单实例

    Android  RecyclerView 小白今天第一次接触RecyclerView,前辈大神告诉我这是一个很神奇的控件,一看就是一整天. RecyclerView中有规定好的方法去显示列表,图片甚至视频.还带有删除新建某一列表的方法.相对于ListView这个 RecyclerView控件就更加牛了. 明白的大神看一眼就懂,小白可以自己照源码敲一遍看看效果.这段程序是把A-Z按列排列下来: package com.example.osserver.recycler; import andro

  • android编程实现电话录音的方法

    本文实例讲述了android编程实现电话录音的方法.分享给大家供大家参考.具体如下: 在清单文件AndroidManifest.xml中添加权限: <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <!-- 在SDCard中创建与删除文件权限 --> <uses-permission android:name="android.permission.MOUN

  • Android RecyclerView上拉加载和下拉刷新(基础版)

    这里讲述的是用谷歌原生的SwipeRefreshLayout,进行刷新,以及利用RecycleView的滚动事件,判断是否到最后一个item,进行加载更多,这里加载更多是在RecycleView的适配器中使用不同item进行完成的. 这是activity的xml布局: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.a

  • Android RecyclerView 数据绑定实例代码

    前言 在上一个项目里有很多很多很多很多的RecyclerView,然后我需要写很多很多很多很多的Adapter和Viewholder--多倒没问题,但是里面有很多重复的代码这就不能忍了!每一个Adapter和ViewHolder其实做的事情非常的像:视图绑定,数据绑定,点击事件分发.还有啥?既然它们做的事情都一样,为啥我们还要傻傻的继续写着重复的代码? 正文 BaseAdapter 通常我们要创建一个RecyclerView.Adapter是怎么做的? 接收一个数据列表 重写getItemCou

随机推荐