美文网首页安卓开发常用知识点互联网科技Android开发经验谈
Android 曝光采集,商品view曝光量的统计,判断Recy

Android 曝光采集,商品view曝光量的统计,判断Recy

作者: 编程小石头666 | 来源:发表于2019-04-02 15:01 被阅读8次

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

    先来看下统计结果图

    image.png
    左边是我们的列表,右边是我们统计到每个条目的曝光量。下面就来讲讲具体实现步骤。

    一,activity中使用recylerview并显示数据

    这里我不再啰嗦,recylerview最基础的使用。

    二,监听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

    image.png
    那么接下来的重点就是要去获取屏幕内可见条目的起始位置。获取到起始位置后,当前屏幕里的可见条目就都能拿到了。
    而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;
        }
       Rect rect = new Rect();
       boolean cover = view.getGlobalVisibleRect(rect);
            if (!cover) {
                return;
            }
           
            if (rect.height() < view.getMeasuredHeight() / 2) {
                //大于一半被覆盖,就不统计
                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));
    }
    

    这里有几点需要注意

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


      image.png

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

    package com.example.qcl.demo.xuexi.baoguang;
    
    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 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;
            }
    
            Rect rect = new Rect();
            boolean cover = view.getGlobalVisibleRect(rect);
            if (!cover) {
                return;
            }
            if (rect.height() < view.getMeasuredHeight() / 2) {
                //大于一半被覆盖,就不统计
                return;
            }
            //        if (rect.width() >= view.getMeasuredWidth() && rect.height() >= view.getMeasuredHeight()) {
            //            //没有被覆盖
            //        }
    
            //这里获取的是我们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传递进去就可以了。如下图:


    image.png

    我们统计到曝光量,拿到曝光view绑定的数据,就可以结合后面的view点击,来看下那些商品view的曝光量高,那些商品的转化率高。当然,这都是运营小伙伴的事了,我们只需要负责把曝光量统计到即可。
    如果你有任何编程方面的问题,可以加我微信交流 2501902696(备注编程)

    相关文章

      网友评论

        本文标题:Android 曝光采集,商品view曝光量的统计,判断Recy

        本文链接:https://www.haomeiwen.com/subject/oszwbqtx.html