美文网首页RecyclerView特效Android-recyclerview
(效果)RecyclerView使用自定义的LayoutMang

(效果)RecyclerView使用自定义的LayoutMang

作者: 小慧sir | 来源:发表于2020-09-05 10:19 被阅读0次

    先看效果

    capture.gif

    一、recyclerview依赖

    //RecyclerView
        implementation 'com.android.support:recyclerview-v7:28.0.0'
    

    二、工具类

    1、VegaLayoutManager
    import android.graphics.Rect;
    import android.support.v4.util.ArrayMap;
    import android.support.v7.widget.RecyclerView;
    import android.util.SparseArray;
    import android.util.SparseBooleanArray;
    import android.view.View;
    import android.view.ViewGroup;
    
    /**
     * Created by zhanghui on 2020/9/5.
     */
    public class VegaLayoutManager extends RecyclerView.LayoutManager {
    
        private int scroll = 0;
        private SparseArray<Rect> locationRects = new SparseArray<>();
        private SparseBooleanArray attachedItems = new SparseBooleanArray();
        private ArrayMap<Integer, Integer> viewTypeHeightMap = new ArrayMap<>();
    
        private boolean needSnap = false;
        private int lastDy = 0;
        private int maxScroll = -1;
        private RecyclerView.Adapter adapter;
        private RecyclerView.Recycler recycler;
    
        public VegaLayoutManager() {
            setAutoMeasureEnabled(true);
        }
    
        @Override
        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
    
        @Override
        public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
            super.onAdapterChanged(oldAdapter, newAdapter);
            this.adapter = newAdapter;
        }
    
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            this.recycler = recycler; // 二话不说,先把recycler保存了
            if (state.isPreLayout()) {
                return;
            }
    
            buildLocationRects();
    
            // 先回收放到缓存,后面会再次统一layout
            detachAndScrapAttachedViews(recycler);
            layoutItemsOnCreate(recycler);
        }
    
        private void buildLocationRects() {
            locationRects.clear();
            attachedItems.clear();
    
            int tempPosition = getPaddingTop();
            int itemCount = getItemCount();
            for (int i = 0; i < itemCount; i++) {
                // 1. 先计算出itemWidth和itemHeight
                int viewType = adapter.getItemViewType(i);
                int itemHeight;
                if (viewTypeHeightMap.containsKey(viewType)) {
                    itemHeight = viewTypeHeightMap.get(viewType);
                } else {
                    View itemView = recycler.getViewForPosition(i);
                    addView(itemView);
                    measureChildWithMargins(itemView, View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
                    itemHeight = getDecoratedMeasuredHeight(itemView);
                    viewTypeHeightMap.put(viewType, itemHeight);
                }
    
                // 2. 组装Rect并保存
                Rect rect = new Rect();
                rect.left = getPaddingLeft();
                rect.top = tempPosition;
                rect.right = getWidth() - getPaddingRight();
                rect.bottom = rect.top + itemHeight;
                locationRects.put(i, rect);
                attachedItems.put(i, false);
                tempPosition = tempPosition + itemHeight;
            }
    
            if (itemCount == 0) {
                maxScroll = 0;
            } else {
                computeMaxScroll();
            }
        }
    
        /**
         * 对外提供接口,找到第一个可视view的index
         */
        public int findFirstVisibleItemPosition() {
            int count = locationRects.size();
            Rect displayRect = new Rect(0, scroll, getWidth(), getHeight() + scroll);
            for (int i = 0; i < count; i++) {
                if (Rect.intersects(displayRect, locationRects.get(i)) &&
                        attachedItems.get(i)) {
                    return i;
                }
            }
            return 0;
        }
    
        /**
         * 计算可滑动的最大值
         */
        private void computeMaxScroll() {
            maxScroll = locationRects.get(locationRects.size() - 1).bottom - getHeight();
            if (maxScroll < 0) {
                maxScroll = 0;
                return;
            }
    
            int itemCount = getItemCount();
            int screenFilledHeight = 0;
            for (int i = itemCount - 1; i >= 0; i--) {
                Rect rect = locationRects.get(i);
                screenFilledHeight = screenFilledHeight + (rect.bottom - rect.top);
                if (screenFilledHeight > getHeight()) {
                    int extraSnapHeight = getHeight() - (screenFilledHeight - (rect.bottom - rect.top));
                    maxScroll = maxScroll + extraSnapHeight;
                    break;
                }
            }
        }
    
        /**
         * 初始化的时候,layout子View
         */
        private void layoutItemsOnCreate(RecyclerView.Recycler recycler) {
            int itemCount = getItemCount();
            Rect displayRect = new Rect(0, scroll, getWidth(), getHeight() + scroll);
            for (int i = 0; i < itemCount; i++) {
                Rect thisRect = locationRects.get(i);
                if (Rect.intersects(displayRect, thisRect)) {
                    View childView = recycler.getViewForPosition(i);
                    addView(childView);
                    measureChildWithMargins(childView, View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
                    layoutItem(childView, locationRects.get(i));
                    attachedItems.put(i, true);
                    childView.setPivotY(0);
                    childView.setPivotX(childView.getMeasuredWidth() / 2);
                    if (thisRect.top - scroll > getHeight()) {
                        break;
                    }
                }
            }
        }
    
    
        /**
         * 初始化的时候,layout子View
         */
        private void layoutItemsOnScroll() {
            int childCount = getChildCount();
            // 1. 已经在屏幕上显示的child
            int itemCount = getItemCount();
            Rect displayRect = new Rect(0, scroll, getWidth(), getHeight() + scroll);
            int firstVisiblePosition = -1;
            int lastVisiblePosition = -1;
            for (int i = childCount - 1; i >= 0; i--) {
                View child = getChildAt(i);
                if (child == null) {
                    continue;
                }
                int position = getPosition(child);
                if (!Rect.intersects(displayRect, locationRects.get(position))) {
                    // 回收滑出屏幕的View
                    removeAndRecycleView(child, recycler);
                    attachedItems.put(position, false);
                } else {
                    // Item还在显示区域内,更新滑动后Item的位置
                    if (lastVisiblePosition < 0) {
                        lastVisiblePosition = position;
                    }
    
                    if (firstVisiblePosition < 0) {
                        firstVisiblePosition = position;
                    } else {
                        firstVisiblePosition = Math.min(firstVisiblePosition, position);
                    }
    
                    layoutItem(child, locationRects.get(position)); //更新Item位置
                }
            }
    
            // 2. 复用View处理
            if (firstVisiblePosition > 0) {
                // 往前搜索复用
                for (int i = firstVisiblePosition - 1; i >= 0; i--) {
                    if (Rect.intersects(displayRect, locationRects.get(i)) &&
                            !attachedItems.get(i)) {
                        reuseItemOnSroll(i, true);
                    } else {
                        break;
                    }
                }
            }
            // 往后搜索复用
            for (int i = lastVisiblePosition + 1; i < itemCount; i++) {
                if (Rect.intersects(displayRect, locationRects.get(i)) &&
                        !attachedItems.get(i)) {
                    reuseItemOnSroll(i, false);
                } else {
                    break;
                }
            }
        }
    
        /**
         * 复用position对应的View
         */
        private void reuseItemOnSroll(int position, boolean addViewFromTop) {
            View scrap = recycler.getViewForPosition(position);
            measureChildWithMargins(scrap, 0, 0);
            scrap.setPivotY(0);
            scrap.setPivotX(scrap.getMeasuredWidth() / 2);
    
            if (addViewFromTop) {
                addView(scrap, 0);
            } else {
                addView(scrap);
            }
            // 将这个Item布局出来
            layoutItem(scrap, locationRects.get(position));
            attachedItems.put(position, true);
        }
    
    
        private void layoutItem(View child, Rect rect) {
            int topDistance = scroll - rect.top;
            int layoutTop, layoutBottom;
            int itemHeight = rect.bottom - rect.top;
            if (topDistance < itemHeight && topDistance > 0) {
                float rate1 = (float) topDistance / itemHeight;
                float rate2 = 1 - rate1 * rate1 / 3;
                float rate3 = 1 - rate1 * rate1;
                child.setScaleX(rate2);
                child.setScaleY(rate2);
                child.setAlpha(rate3);
                layoutTop = 0;
                layoutBottom = itemHeight;
            } else {
                child.setScaleX(1);
                child.setScaleY(1);
                child.setAlpha(1);
    
                layoutTop = rect.top - scroll;
                layoutBottom = rect.bottom - scroll;
            }
            layoutDecorated(child, rect.left, layoutTop, rect.right, layoutBottom);
        }
    
        @Override
        public boolean canScrollVertically() {
            return true;
        }
    
        @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
            if (getItemCount() == 0 || dy == 0) {
                return 0;
            }
            int travel = dy;
            if (dy + scroll < 0) {
                travel = -scroll;
            } else if (dy + scroll > maxScroll) {
                travel = maxScroll - scroll;
            }
            scroll += travel; //累计偏移量
            lastDy = dy;
            if (!state.isPreLayout() && getChildCount() > 0) {
                layoutItemsOnScroll();
            }
    
            return travel;
        }
    
        @Override
        public void onAttachedToWindow(RecyclerView view) {
            super.onAttachedToWindow(view);
            new StartSnapHelper().attachToRecyclerView(view);
        }
    
        @Override
        public void onScrollStateChanged(int state) {
            if (state == RecyclerView.SCROLL_STATE_DRAGGING) {
                needSnap = true;
            }
            super.onScrollStateChanged(state);
        }
    
        public int getSnapHeight() {
            if (!needSnap) {
                return 0;
            }
            needSnap = false;
    
            Rect displayRect = new Rect(0, scroll, getWidth(), getHeight() + scroll);
            int itemCount = getItemCount();
            for (int i = 0; i < itemCount; i++) {
                Rect itemRect = locationRects.get(i);
                if (displayRect.intersect(itemRect)) {
    
                    if (lastDy > 0) {
                        // scroll变大,属于列表往下走,往下找下一个为snapView
                        if (i < itemCount - 1) {
                            Rect nextRect = locationRects.get(i + 1);
                            return nextRect.top - displayRect.top;
                        }
                    }
                    return itemRect.top - displayRect.top;
                }
            }
            return 0;
        }
    
        public View findSnapView() {
            if (getChildCount() > 0) {
                return getChildAt(0);
            }
            return null;
        }
    }
    
    2、StartSnapHelper
    import android.support.annotation.NonNull;
    import android.support.v7.widget.LinearSnapHelper;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    
    /**
     * 定位到第一个子View的SnapHelper
     * Created by zhanghui on 2020/9/5.
     */
    public class StartSnapHelper extends LinearSnapHelper {
    
        @Override
        public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
                                                  @NonNull View targetView) {
            int[] out = new int[2];
            out[0] = 0;
            out[1] = ((VegaLayoutManager) layoutManager).getSnapHeight();
            return out;
        }
    
        @Override
        public View findSnapView(RecyclerView.LayoutManager layoutManager) {
            VegaLayoutManager custLayoutManager = (VegaLayoutManager) layoutManager;
            return custLayoutManager.findSnapView();
        }
    }
    

    三、activity使用

     private void initView() {
            recycler = (RecyclerView) findViewById(R.id.recyclerview);
            //管理器
            recycler.setLayoutManager(new VegaLayoutManager());
            list = new ArrayList<>();
            bannerlist = new ArrayList<>();
            recyclerAdapter = new RecyclerAdapter(this,list,bannerlist);
            recycler.setAdapter(recyclerAdapter);
            for (int i = 0; i < 3; i++) {
                BannerBean bannerBean = new BannerBean(R.mipmap.ic_launcher);
                bannerlist.add(bannerBean);
            }
            for (int i = 0; i < 25; i++) {
    
                DemoBean demoBean = new DemoBean("张三" + i, 1 + i, R.mipmap.ic_launcher);
                list.add(demoBean);
    
            }
            recyclerAdapter.notifyDataSetChanged();
        }
    

    //管理器 (直接引用即可)
    recycler.setLayoutManager(new VegaLayoutManager());
    本文章仅限实现效果

    相关文章

      网友评论

        本文标题:(效果)RecyclerView使用自定义的LayoutMang

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