RecyclerView使用详解

RecyclerView使用详解

Recyclerview API

第1节 常用属性理解

  1. stopScroll 停止滚动

1.1 setHasFixedSize 优化

官方推荐设置true 理解:告诉RecyclerView我的Item大小不会变,重新绘制的时候不用重新计算

解释博文:RecyclerView setHasFixedSize(true)的意义

1.2 RecyclerView.Adapter 不同数据不同布局

Demo.java

 @Override
    public int getItemViewType(int position) {
        if (mTypeList.get(position) == 0) {         // 横向
            return TYPE_HORIZONTAL;
        } else if (mTypeList.get(position) == 1) {  // 纵向
            return TYPE_VERTICAL;
        } else {
            return super.getItemViewType(position);
        }
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == TYPE_HORIZONTAL) {
            //inflate viewHorizontal
            return new HorizontalViewHolder(viewHorizontal);
        } else if (viewType == TYPE_VERTICAL) {
            //inflate viewVertical
            return new VerticalViewHolder(viewVertical);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        // 布局控制
        if (holder instanceof HorizontalViewHolder) {
            if (mHorizontalList != null) {

            }
        } else if (holder instanceof VerticalViewHolder) {
            if (mVerticalList != null) {
            }
        }

1.3 滑动相关

  • smoothScrollToPosition(int) 大量数据不理想,查看下文TopSmoothScroller案例,实践过程中TopSmoothScroller在下滑数据量很大的情况下置顶时间过长,滑动时间久会导致使用不舒适
  • scrollToPosition(int) 滑动到指定位置
  • scrollBy(int x,int y) 滑动指定像素

最终的置顶方案

 int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
            //当可显示的item大于30时,直接置顶,否则丝滑滚动置顶
      if (firstVisibleItemPosition > 30) {
        mRecyclerView.scrollToPosition(0);
      }else {
        LinearSmoothScroller lss = new TopSmoothScroller(getActivity());
        lss.setTargetPosition(0);
        layoutManager.startSmoothScroll(lss);
      }

第2节 OnScrollListener

2.1 三种滑动状态 newState

/**
 * The RecyclerView is not currently scrolling.(静止没有滚动)
 */
public static final int SCROLL_STATE_IDLE = 0;
/**
 * The RecyclerView is currently being dragged by outside input such as user touch input.
 *(正在被外部拖拽,一般为用户正在用手指滚动)
 */
public static final int SCROLL_STATE_DRAGGING = 1;
/**
 * The RecyclerView is currently animating to a final position while not under outside control.
 *(自动滚动)
 */
public static final int SCROLL_STATE_SETTLING = 2;

2.2 onScrolled

onScrolled(RecyclerView recyclerView, int dx, int dy)

dx : 水平滚动距离
dy : 垂直滚动距离

第3节 LinearLayoutManager

LinearLayoutManager API

2.1 常用API白话理解

  1. 位置数量相关
linearLayoutManager.getChildCount()

注解:得到显示屏幕内的list数量

mLinearLayoutManager.getItemCount()

注解:得到list的总数量

int position = linearLayoutManager.findFirstVisibleItemPosition();

注解: 得到显示屏内的第一个list的位置数position* 类似:findLastVisibleItemPosition

findFirstCompletelyVisibleItemPosition

注解:第一个完全显示的Item位置 类似findLastCompletelyVisibleItemPosition

View firstVisiableChildView = linearLayoutManager.findViewByPosition(position);

注解:根据position找到这个Item的View

public class TopSmoothScroller extends LinearSmoothScroller {
  public TopSmoothScroller(Context context) {
    super(context);
  }

  @Override protected int getHorizontalSnapPreference() {
    return SNAP_TO_START;
  }

  @Override
  protected int getVerticalSnapPreference() {
    return SNAP_TO_START;//具体见源码注释
  }
}

LinearSmoothScroller lss = new TopSmoothScroller(getActivity());
lss.setTargetPosition(0);
layoutManager.startSmoothScroll(lss);

注解:使滑动到RecyclerView指定位置

canScrollVertically和canScrollHorizontally

注解:是否能垂直/水平滑动

第4节 ItemDecoration

  @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
       //根据itemView的位置设置边框大小
        int position = parent.getChildAdapterPosition(view);
        outRect.right = spaces;
    }

参考:ItemDecoration解析(二) onDraw onDrawOver 画出Item之间的分割线

使用案例 RecyclerView分割线

第5 节 ItemTouchHelper

ItemTouchHelper.Callback callback = new DragItemTouchHelper(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView);

DragItemTouchHelper.java

public class DragItemTouchHelper extends ItemTouchHelper.Callback {

    private DragAdapter mAdapter;

    public DragItemTouchHelper(DragAdapter adapter) {
        mAdapter = adapter;
    }

    /**
     * 设置滑动类型标记
     *
     * @param recyclerView
     * @param viewHolder
     * @return 返回一个整数类型的标识,用于判断Item那种移动行为是允许的
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
          // dragFlags =0 禁止拖动
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;  // 允许上下的拖动
          //int swipeFlags = ItemTouchHelper.LEFT;  // 只允许从右向左滑动
        int swipeFlags = 0; // 不允许左右滑动
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    /**
     * 拖拽切换 Item 的回调
     *
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return true Item切换了位置,false Item没切换位置
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }

    /**
     * Item 是否支持长按拖动
     * @return true  支持长按操作,false 不支持长按操作
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * Item 是否支持滑动
     * @return true  支持滑动操作,false 不支持滑动操作
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }

    /**
     * Item被选中时候回调
     *
     * @param viewHolder
     * @param actionState 当前Item的状态
     *                    ItemTouchHelper.ACTION_STATE_IDLE   闲置状态
     *                    ItemTouchHelper.ACTION_STATE_SWIPE  滑动中状态
     *                    ItemTouchHelper#ACTION_STATE_DRAG   拖拽中状态
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        // item 被选中的操作
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundResource(R.drawable.select_bg);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        // 操作完毕后恢复颜色
        viewHolder.itemView.setBackgroundResource(R.drawable.common_bg);
        super.clearView(recyclerView, viewHolder);
    }

     /**
     * 移动过程中重新绘制 Item,随着滑动的距离,设置 Item 的透明度
     *  @param isCurrentlyActive True if this view is currently being controlled by the user or
     *                          false it is simply animating back to its original state.
     */
    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        float x = Math.abs(dX) + 0.5f;
        float width = viewHolder.itemView.getWidth();
        float alpha = 1f - x / width;
        viewHolder.itemView.setAlpha(alpha);
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

  public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
                              ViewHolder viewHolder,
                              float dX, float dY, int actionState, boolean isCurrentlyActive) {
  }

}

Recylcerview 常用功能实现

  1. 水平滑动时不是最后一个Item, 都显示居中

  2. 左右联动 LinkActivity.java

滑动事件冲突

Android事件分发机制及滑动冲突解决方案【讲解比较详细】,提供两种方式

  1. 外部拦截法
  2. 内部拦截法

浅谈RecycleView嵌套RecycleView竖向滑动冲突解决

onInterceptTouchEvent == false; // 不向下分发,自己消费这次事件
onTouchEvent == true ;// 不向上分发,自己消费这次事件
parent.requestDisallowInterceptTouchEvent(true) // 请求父类true不拦截,false 请求父控件拦截

RecyclerView 源码解析

RecyclerView 优化

附录

参考博文

开源库