Take you to write a simple RecycleView

Usage of RecyclerView

RecyclerView is generally used as a control for the Android display list and has many excellent performances.
1) The recycling pool strategy can load hundreds of millions of data without stalling,
2) The adapter mode can display any display requirements

Core components in the RecyclerView architecture

1. Recycling pool: It can recycle any Item control and return the Item control that matches the type; for example, the first parameter in the onBinderViewHolder method is returned from the recycling pool.
2. Adapter: Adapter interface, often assists RecyclerView to achieve list display; The mode separates the user interface display from the interaction.
3. RecyclerView: It is the interaction of touch events, which mainly realizes the boundary value judgment;
according to the user's touch feedback, it coordinates the work between the recycling pool object and the adapter object.

The embodiment of RecyclerView architecture in work

The working mechanism of the conveyor belt

Loading transmission Arrivals Add
Vacant position at the beginning of the conveyor belt Put the goods on the conveyor belt The conveyor belt starts to drive Goods arrive at the end of the conveyor belt, put new goods on the conveyor belt

RecyclerView architecture implementation

load slide Slide out load
Load the first screen data The user's finger starts to slide The user will not need, the information is drawn off the screen New users at the bottom of the screen, need to see the data

Architecture thinking of RecyclerView

Architecture: Make full use of the conveyor belt principle, only the data that the user sees will be loaded into the memory, and the unseen data are waiting to be loaded. The conveyor belt can continuously transport billions of goods, and RecyclerView can also display the loading of billions of ltem.
The working mechanism of the conveyor belt can be compared to the producer and consumer model

The recycling strategy of the recycling pool

Insert picture description here

Filling strategy of recycling pool

Insert picture description here

Design of recycling pool

Insert picture description here

Deposit and withdrawal are necessary for the recycling pool strategy

Insert picture description here

Methods that need to be rewritten

  • onMeasure
  • onLayout
  • onInterceptTouchEvent
  • onTouchEvent
  • scrollBy

Reference Code

/**
 * 1 List<View> viewList
 * 缓存已经加载到屏幕上的View这些View不存在回收池中,
 * 需要用集合表示,方便后续查找和移除
 * 2 nt currentY:记录在Y轴 上滑动的距离
 * 3 int rowCount:记录在RecyclerView中加载的总数据条数
 * 4 int firstRow;记录在屏幕中第一个View在数据中的位置,比如当前是第34个元
 * 素在屏幕的第一个位置
 * 5 Recycler recycler:持有一个回收池的引用
 * 6 int scrollY: RecyclerView中 第一个View的左上顶点离屏幕的距离
 */
public class RecyclerView extends ViewGroup {
    
    
    private Adapter adapter;
    //当前显示的View
    private List<View> viewList;
    //当前滑动的y值
    private int currentY;
    //行数
    private int rowCount;
    //view的第一行  是占内容的几行
    private int firstRow;
    //y偏移量
    private int scrollY;
    //初始化  第一屏最慢
    private boolean needRelayout;
    private int width;

    private int height;
    private int[] heights;//item  高度
    Recycler recycler;
    //最小滑动距离
    private int touchSlop;

    public Adapter getAdapter() {
    
    
        return adapter;
    }

    public void setAdapter(Adapter adapter) {
    
    
        this.adapter = adapter;
        if (adapter != null) {
    
    
            //回收池初始化
            recycler = new Recycler(adapter.getViewTypeCount());
            scrollY = 0;
            firstRow = 0;
            needRelayout = true;
            requestLayout();//1  onMeasure   2  onLayout
        }
    }

    public RecyclerView(Context context, AttributeSet attrs) {
    
    
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
    
    
        ViewConfiguration configuration = ViewConfiguration.get(context);
        this.touchSlop = configuration.getScaledTouchSlop();
        this.viewList = new ArrayList<>();
        this.needRelayout = true;
    }

    //初始化
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    
    
        if (needRelayout || changed) {
    
    
            needRelayout = false;

            viewList.clear();
            removeAllViews();
            if (adapter != null) {
    
    
                //               摆放
                width = r - l;
                height = b - t;
                int left, top = 0, right, bottom;
                for (int i = 0; i < rowCount && top < height; i++) {
    
    
                    right = width;
                    bottom = top + heights[i];
//                    生成一个View
                    View view = makeAndStep(i, 0, top, width, bottom);
                    viewList.add(view);
                    top = bottom;//循环摆放
                }

            }

        }
    }

    private View makeAndStep(int row, int left, int top, int right, int bottom) {
    
    
        View view = obtainView(row, right - left, bottom - top);
        view.layout(left, top, right, bottom);
        return view;
    }

    private View obtainView(int row, int width, int height) {
    
    
//        key type
        int itemType = adapter.getItemViewType(row);
//       取不到
        View recyclerView = recycler.get(itemType);
        View view = null;
        if (recyclerView == null) {
    
    
            view = adapter.onCreateViewHolder(row, recyclerView, this);
            if (view == null) {
    
    
                throw new RuntimeException("onCreateViewHolder  必须填充布局");
            }
        } else {
    
    
            view = adapter.onBinderViewHolder(row, recyclerView, this);
        }
        view.setTag(R.id.tag_type_view, itemType);
        view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)
                , MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        addView(view, 0);
        return view;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int h = 0;
        if (adapter != null) {
    
    
            this.rowCount = adapter.getCount();
            heights = new int[rowCount];
            for (int i = 0; i < heights.length; i++) {
    
    
                heights[i] = adapter.getHeight(i);
            }
        }
//        数据的高度
        int tmpH = sumArray(heights, 0, heights.length);
        h = Math.min(heightSize, tmpH);
        setMeasuredDimension(widthSize, h);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    //    firstIndex  firstIndex+count
    private int sumArray(int array[], int firstIndex, int count) {
    
    
        int sum = 0;
        count += firstIndex;
        for (int i = firstIndex; i < count; i++) {
    
    
            sum += array[i];
        }
        return sum;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
    
    
        boolean intercept = false;
        switch (event.getAction()) {
    
    
            case MotionEvent.ACTION_DOWN: {
    
    
                currentY = (int) event.getRawY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
    
    
                int y2 = Math.abs(currentY - (int) event.getRawY());
                if (y2 > touchSlop) {
    
    
                    intercept = true;
                }
            }
        }
        return intercept;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    
    
        switch (event.getAction()) {
    
    
            case MotionEvent.ACTION_MOVE: {
    
    
//                移动的距离   y方向
                int y2 = (int) event.getRawY();
//         //            上滑正  下滑负
                int diffY = currentY - y2;
                currentY = y2;//不加响应会变慢
//                画布移动  并不影响子控件的位置
                scrollBy(0, diffY);
            }
        }
        return super.onTouchEvent(event);
    }


    @Override
    public void scrollBy(int x, int y) {
    
    
        //修正
//        scrollY表示 第一个可见Item的左上顶点 距离屏幕的左上顶点的距离
        scrollY += y;
        scrollY = scrollBounds(scrollY);
//        scrolly
        if (scrollY > 0) {
    
    
//              上滑正  下滑负  边界值
            while (scrollY > heights[firstRow]) {
    
    
//      1 上滑移除  2 上划加载  3下滑移除  4 下滑加载
                removeView(viewList.remove(0));
                scrollY -= heights[firstRow];
                firstRow++;
            }
            while (getFillHeight() < height) {
    
    
                int addLast = firstRow + viewList.size();
                View view = obtainView(addLast, width, heights[addLast]);
                viewList.add(viewList.size(), view);
            }


        } else if (scrollY < 0) {
    
    
//            4 下滑加载
            while (scrollY < 0) {
    
    
                int firstAddRow = firstRow - 1;
                View view = obtainView(firstAddRow, width, heights[firstAddRow]);
                viewList.add(0, view);
                firstRow--;
                scrollY += heights[firstRow + 1];
            }
//             3下滑移除
            while (sumArray(heights, firstRow, viewList.size()) - scrollY - heights[firstRow + viewList.size() - 1] >= height) {
    
    
                removeView(viewList.remove(viewList.size() - 1));
            }

        } else {
    
    
        }
        repositionViews();
    }

    private int scrollBounds(int scrollY) {
    
    
//        上滑
        if (scrollY > 0) {
    
    
            scrollY = Math.min(scrollY, sumArray(heights, firstRow, heights.length - firstRow) - height);
        } else {
    
    
//            极限值  会取零  非极限值的情况下   socrlly
            scrollY = Math.max(scrollY, -sumArray(heights, 0, firstRow));

        }
        return scrollY;
//        下滑
    }

    private void repositionViews() {
    
    
        int left, top, right, bottom, i;
        top = -scrollY;
        i = firstRow;
        for (View view : viewList) {
    
    
            bottom = top + heights[i++];
            view.layout(0, top, width, bottom);
            top = bottom;
        }
    }

    private int getFillHeight() {
    
    
//        数据的高度 -scrollY
        return sumArray(heights, firstRow, viewList.size()) - scrollY;
    }


    @Override
    public void removeView(View view) {
    
    
        super.removeView(view);
        int key = (int) view.getTag(R.id.tag_type_view);
        recycler.put(view, key);
    }

    interface Adapter {
    
    
        View onCreateViewHolder(int position, View convertView, ViewGroup parent);

        View onBinderViewHolder(int position, View convertView, ViewGroup parent);

        //Item的类型
        int getItemViewType(int row);

        //Item的类型数量
        int getViewTypeCount();

        int getCount();

        int getHeight(int index);
    }
}

Example effect

Insert picture description here


Recommended reading

Finally understand why I came to Netease!

Android Development Challenge | Week 2: Countdown Timer

About

Demo download of this article

List of UI series articles

Guess you like

Origin blog.csdn.net/u014158743/article/details/114364935