RecyclerView 在项目中使用的常见问题

目录

  • ItemDecoration
  • 侧拉菜单
  • SnapHelper
  • 局部刷新
  • 由于复用导致数据的错乱问题
  • y 轴上的滑动距离
  • 上下左右滑动判断
  • 双击置顶
  • 添加头布局后,每次刷新内容会跳回头布局:解决办法

ItemDecoration

常见的作用有:分割线、吸顶效果、时光轴等 推荐阅读 https://blog.csdn.net/briblue/article/details/70161917

线性分割线:

以下代码来源于网络,在项目中测试可以通过。

public class RecycleViewDivider extends RecyclerView.ItemDecoration {
    private Paint mPaint;
    private Drawable mDivider;
    private int mDividerHeight = 2;//分割线高度,默认为1px
    private int mOrientation;//列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    /**
     * 默认分割线:高度为2px,颜色为灰色
     *
     * @param context
     * @param orientation 列表方向
     */
    public RecycleViewDivider(Context context, int orientation) {
        if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
            throw new IllegalArgumentException("请输入正确的参数!");
        }
        mOrientation = orientation;

        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    /**
     * 自定义分割线
     *
     * @param context
     * @param orientation 列表方向
     * @param drawableId  分割线图片
     */
    public RecycleViewDivider(Context context, int orientation, int drawableId) {
        this(context, orientation);
        mDivider = ContextCompat.getDrawable(context, drawableId);
        mDividerHeight = mDivider.getIntrinsicHeight();
    }

    /**
     * 自定义分割线
     *
     * @param context
     * @param orientation   列表方向
     * @param dividerHeight 分割线高度
     * @param dividerColor  分割线颜色
     */
    public RecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {
        this(context, orientation);
        mDividerHeight = dividerHeight;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(dividerColor);
        mPaint.setStyle(Paint.Style.FILL);
    }


    //获取分割线尺寸
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(0, 0, 0, mDividerHeight);
    }

    //绘制分割线
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.VERTICAL) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    //绘制横向 item 分割线
    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + layoutParams.bottomMargin;
            final int bottom = top + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            }
            if (mPaint != null) {
                canvas.drawRect(left, top, right, bottom, mPaint);
            }
        }
    }

    //绘制纵向 item 分割线
    private void drawVertical(Canvas canvas, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + layoutParams.rightMargin;
            final int right = left + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            }
            if (mPaint != null) {
                canvas.drawRect(left, top, right, bottom, mPaint);
            }
        }
    }
}

网格分割线:

以下代码来源于网络,在项目中测试可以通过。

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
    private int spanCount;
    private int spacing;
    private boolean includeEdge;

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view); // item position
        int column = position % spanCount; // item column

        if (includeEdge) {
            outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
            outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

            if (position < spanCount) { // top edge
                outRect.top = spacing;
            }
            outRect.bottom = spacing; // item bottom
        } else {
            outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
            outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
            if (position >= spanCount) {
                outRect.top = spacing; // item top
            }
        }
    }

侧拉菜单

实现原理暂时还没去阅读源码,实现的功能就是在 RecyclerView的每个Item添加上侧拉出来的布局,通过侧拉出来的布局实现一些功能。具体可到 Github 搜索 这个库PinnedSectionItemDecoration,上面有实现的效果。

compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
compile 'com.oushangfeng:PinnedSectionItemDecoration:1.2.7'
compile 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-5'

SnapHelper

参考:让你明明白白的使用RecyclerView——SnapHelper详解
SnapHelper是一个抽象类,官方提供了一个LinearSnapHelper的子类,可以让RecyclerView滚动停止时相应的Item停留中间位置。25.1.0版本中官方又提供了一个PagerSnapHelper的子类,可以使RecyclerView像ViewPager一样的效果,一次只能滑一页,而且居中显示。

这两个子类使用方式也很简单,只需要创建对象之后调用attachToRecyclerView()附着到对应的RecyclerView对象上就可以了

new LinearSnapHelper().attachToRecyclerView(mRecyclerView);
//或者
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);

局部刷新

例如点击item中的某个控件,刷新该控件对应的点击数。

	// 定义的刷新函数
	adapter.refreshPositionItem(view,count,position);
	adapter.notifyItemChanged(position,0);

在 adapter 中添加一个方法

public void refreshPositionItem(View view, int count, int position) {
    TextView tvCount = view.findViewById(R.id.tv_count);
    tvCount.setText(count+"");
}

由于复用导致数据的错乱问题

一定要修改源数据源后再刷新 adapter ,这就不会错乱了

2018-12-18 更新

y 轴上的滑动距离

这里是为了滑动显示 toolbar 的标题

    /**
     * 获取滑动距离
     * @return
     */
    public int getScollYDistance() {
        int position = linearLayoutManager.findFirstVisibleItemPosition();
        View firstVisiableChildView = linearLayoutManager.findViewByPosition(position);
        int itemHeight = firstVisiableChildView.getHeight();
        return (position) * itemHeight - firstVisiableChildView.getTop();
    }

上下左右滑动判断

这里是为了滑动显示隐藏发布图标

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                // 如果 dx 为正数代表向左滑动,dx 为负数代表向右滑动。 
				// 如果 dy 为正数代表向上滑动,dy 为负数代表向下滑动。
                if (dy > 0 ){
                   floatingActionButton.setVisibility(View.GONE);
                }else {
                   floatingActionButton.setVisibility(View.VISIBLE);
                }
            }
        });

双击置顶

工具:
public class DoubleClickBackToContentTopListener implements View.OnClickListener {
    private final long delayTime = 300;
    private long lastClickTime = 0;
    private final IDoubleClickListener doubleClickListener;

    public interface IDoubleClickListener {
        /**
         * 双击置顶
         */
        void onDoubleClickToTop();
    }

    @Override
    public final void onClick(View v) {
        long nowClickTime = System.currentTimeMillis();
        if (nowClickTime - lastClickTime > delayTime) {
            lastClickTime = nowClickTime;
        } else {
            onDoubleClick(v);
        }
    }

    public DoubleClickBackToContentTopListener(@NonNull IDoubleClickListener iDoubleClickListener) {
        this.doubleClickListener = iDoubleClickListener;
    }

    public void onDoubleClick(View v) {
        doubleClickListener.onDoubleClickToTop();
    }
}

使用:
// 双击顶部置顶(直接置顶失效,使用动画滚动置顶)
toolbarHeightLayout.setOnClickListener(new DoubleClickBackToContentTopListener(new DoubleClickBackToContentTopListener.IDoubleClickListener() {
    @Override
    public void onDoubleClickToTop() {
//                rvCirclelist.scrollToPosition(0); // 失效
        rvCirclelist.smoothScrollToPosition(0);

    }
}));

添加头布局后,每次刷新内容会跳回头布局:解决办法

解决办法有以下几个,推荐第一个


1.recyclerView.setFocusableInTouchMode(false);(亲测成功)

2.recyclerView.setFocusable(false);

3.setHasFixedSize(true);

4.把recyclerview 高度设为match_parent就解决了.....

参考
Android RecyclerView详解

你可能感兴趣的:(Android)