Recently, PM proposed that we need to count the exposure brightness of commodities on the home page. Since our home page is realized by recylerView, here is how to use recylerView to monitor the scroll events to realize the exposure statistics of sub-views. The views we are talking about here are all sub-item items in the list (sub-views).

Let’s take a look at the statistics

First, the activity uses recylerView and displays the data

I won’t be verbose here, recylerView is the most basic use.

Listen for the recylerView OnScrollListener

OnScrollStateChanged: listens for the rolling state onScrolled: listens for the rolling state

/ / testing recylerview scroll event recyclerView addOnScrollListener (new recyclerView.OnScrollListener() {@override public void onScrollStateChanged(RecyclerView RecyclerView, int newState) { SCROLL_STATE_IDLE: Stop scrolling SCROLL_STATE_DRAGGING: User dragging SCROLL_STATE_DRAGGING: Inertial scrolling */ Can be distinguished by newStateif(newState == RecyclerView.SCROLL_STATE_IDLE) { ..... } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); . }});Copy the code

First, to be clear again, we want to count the controls that appear on the screen when the user stops swiping. So we need to detect when newState == recyclerView. SCROLL_STATE_IDLE in onScrollStateChanged, that is, the user stops scrolling. And build on that.

Third, get the starting position of the items visible on the screen

The starting position here refers to the position of the top and bottom items on our screen. For example, 0 is the top visible item and 3 is the bottom visible item. So our exposure view is going to be 0,1,2,3 and these four items are on the screen. We now need to increase the exposure of the four views by one

FindFirstVisibleItemPosition () and findLastVisibleItemPosition ()

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);
}
Copy the code

The LinearLayoutManager and GridLayoutManager get the starting position as follows

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;
}
Copy the code

StaggeredGridLayoutManager starting position is a bit complicated, as follows

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;
    }
Copy the code

Fourth, after obtaining the starting position, we will obtain the view and the data in the view according to the position

Once we get the starting position of the visible items on the screen in step 3 above, we use a for loop to get all the child views visible on the current screen

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

RecordViewCount is a method I wrote to get the binding data in the child view

Private void 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; } // Here is our view binding data, corresponding to you to go to your viewsetThe Tag, onlysetItemData 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));
}
Copy the code

There are a few things to note here

  • 1. If the view area at the starting position does not exceed 50%, the view is not visible, and therefore the exposure is not counted.
  • 2, we pass view.getTag(); To retrieve data from the view, you must first setTag() the data, and in this case the setTag is set into the Viewholder

Here we have achieved the recylerView list view control exposure statistics. The complete code is posted below for you

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: Public Class ViewShowCountUtils {// Public class ViewShowCountUtils views private Boolean isFirstVisible =true; Private ConcurrentHashMap<String, Integer>hashMap = new ConcurrentHashMap<String, Integer>(); /* * RecyclerView RecyclerView RecyclerView (RecyclerView RecyclerView) {hashMap.clear();
        if(recyclerView == null || recyclerView.getVisibility() ! = View.VISIBLE) {return; } / / testing recylerview scroll event recyclerView addOnScrollListener (new recyclerView.OnScrollListener() {@override public void onScrollStateChanged(RecyclerView RecyclerView, int newState) { SCROLL_STATE_IDLE: Stop scrolling SCROLL_STATE_DRAGGING: User dragging SCROLL_STATE_DRAGGING: Inertial scrolling */ Can be distinguished by newStateif(newState == RecyclerView.SCROLL_STATE_IDLE) { getVisibleViews(recyclerView); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // The current screen displays views when entering the listif (isFirstVisible) {
                    getVisibleViews(recyclerView);
                    isFirstVisible = false; }}}); Private void getVisibleViews(RecyclerView reView) {private void getVisibleViews(RecyclerView reView) {if(reView == null || reView.getVisibility() ! = View.VISIBLE || ! reView.isShown() || ! reView.getGlobalVisibleRect(new Rect())) {return; } range = new int[2]; range = new int[2]; 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"."Starting position of visible items on screen:" + 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(); 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; } // Here is our view binding data, corresponding to you to go to your viewsetThe Tag, onlysetItemData 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};
        returnres; }}Copy the code

Use is in our RecylerView after setting up the data, pass the RecylerView in. The diagram below:

We count exposure, get exposure view binding data, we can combine the view behind the click, see which goods view exposure is high, which goods conversion rate is high. Of course, this is all business partners, we just need to be responsible for the exposure of the statistics. If you have any programming questions, you can add me to wechat exchange 2501902696 (note programming)