点滴RecyclerView

1.为RecyclerView添加分割线

RecyclerView添加分割线可以在item的布局中设置,这里通过实现ItemDecoration来实现分割线效果。
思路:
a.创建一个类继承RecyclerView.ItemDecoration,主要重写两个方法getItemOffsets(..)和onDraw(…),
b.getItemOffsets(…)确定的是item四边的偏移量,onDraw(…)就是绘制分割线

/**
 * RecyclerView的LinearLayoutManager分割线
 */

public class RecyclerViewLinearDivider extends RecyclerView.ItemDecoration {
    private Drawable mDivider;
    private int mDividerHeight = 2;//分割线高度,默认是2px
    private int mOrientation;//列表方向:LinearLayoutManager.VERTICAL 或LinearLayoutManager.Horienzontal
    private int mDividerHeaderMargin;
    private int mDividerFooterMargin;

    /**
     * 自定义分割线
     *
     * @param context
     * @param orientation  列表方向
     * @param drawableId   分割线drawable资源
     * @param headerMargin 分割线距离头的距离 单位:像素
     * @param footerMargin 分割线距离尾的距离 单位:像素
     */
    public RecyclerViewLinearDivider(Context context, int orientation, @DrawableRes int drawableId, int headerMargin, int footerMargin) {
        if (orientation != LinearLayoutManager.HORIZONTAL && orientation != LinearLayoutManager.VERTICAL) {
            throw new IllegalArgumentException("orientation must be LinearLayoutManager.HORIZONTAL or LinearLayoutManager.VERTICAL");
        }
        mOrientation = orientation;
        mDivider = ContextCompat.getDrawable(context, drawableId);
        mDividerHeight = Math.abs(mDivider.getIntrinsicHeight());//返回drawable的内在高度。
        mDividerHeaderMargin = headerMargin;
        mDividerFooterMargin = footerMargin;

    }


    //设置四个方向的偏移量,根据位置索引可以指定view的偏移量。这里是当时画横线时,bottom的偏移量就是分割线的高度,也就是上下item的间隔,当画纵线时,right的偏移量就是分割线的厚度,也就是左右item的间隔
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView recyclerView, RecyclerView.State state) {
        int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        if (mOrientation == LinearLayoutManager.VERTICAL) {

            outRect.set(0, 0, mDividerHeight, 0);
        } else {
            outRect.set(0, 0, 0, mDividerHeight);
        }
    }

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

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

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

public class RecyclerViewGridDivider extends RecyclerView.ItemDecoration {
    private static final String TAG = RecyclerViewGridDivider.class.getName();
    private Drawable mDivider;
    private int mDividerHeight = 2;//分割线高度,默认是2px

    public RecyclerViewGridDivider(Context context, @DrawableRes int drawableId) {
        mDivider = ContextCompat.getDrawable(context, drawableId);
        mDividerHeight = Math.abs(mDivider.getIntrinsicHeight());//返回drawable的内在高度。
    }


    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (((GridLayoutManager) parent.getLayoutManager()).getOrientation() != GridLayoutManager.VERTICAL) {
            throw new RuntimeException("GridLayoutManager's oritention must be GridLayoutManager.VERTICAL");
        }
        int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        int spanCount = getSpanCount(parent);//列数
        int childCount = parent.getAdapter().getItemCount();


        int left = 0;
        @SuppressWarnings("SuspiciousNameCombination")
        int right = mDividerHeight;
        int top ;
        int bottom = mDividerHeight;

        if (isfirstRow(parent, itemPosition, spanCount, childCount)) {//如果是第一行,这里做的是第一行距离顶端是分割线的高度
            top = mDividerHeight;
        } else {
            top = 0;
        }
        if (isLastRow(parent, itemPosition, spanCount, childCount)) {//如果是最后一行,item底端的偏移量置为0,不再绘制分割线
            bottom = 0;
        }
        if (isRightMost(parent, itemPosition, spanCount, childCount)) {//如果是最右边的一列,item的右边偏移量置为0,不再绘制分割线
            right = 0;
        }
        outRect.set(left, top, right, bottom);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        draw(c, parent);
    }

    //绘制横向 item 分割线
    private void draw(Canvas canvas, RecyclerView parent) {
        int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            int left;
            int right;
            int top;
            int bottom;
            //画水平分隔线
            left = child.getLeft();
            right = child.getRight();
            top = child.getBottom() + layoutParams.bottomMargin;
            bottom = top + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            }
            //画垂直分割线
            top = child.getTop();
            bottom = child.getBottom() + mDividerHeight;
            left = child.getRight() + layoutParams.rightMargin;
            right = left + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            }
        }
    }


    /**
     * 判断是否是最后一行
     *
     * @param parent
     * @param pos
     * @param spanCount
     * @param childCount
     * @return
     */
    private boolean isLastRow(RecyclerView parent, int pos, int spanCount, int childCount) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int lines = childCount % spanCount == 0 ? childCount / spanCount : childCount / spanCount + 1;
            return lines == pos / spanCount + 1;
        } else {
            throw new RuntimeException("LayoutManager must be GridLayoutManager");
        }

    }

    /**
     * 判断是否是第一行
     *
     * @param parent
     * @param pos
     * @param spanCount
     * @param childCount
     * @return
     */
    private boolean isfirstRow(RecyclerView parent, int pos, int spanCount, int childCount) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int lines = childCount % spanCount == 0 ? childCount / spanCount : childCount / spanCount + 1;//得到有多少行
            if ((pos / spanCount) == 0) {//只有第一行的position<spanCount,其比值的int值一定是0
                return true;
            } else {
                return false;
            }
        } else {
            throw new RuntimeException("LayoutManager must be GridLayoutManager");
        }
    }

    /**
     * 是否是最右边的一列
     *
     * @param parent
     * @param pos
     * @param spanCount
     * @param childCount
     * @return
     */
    private boolean isRightMost(RecyclerView parent, int pos, int spanCount, int childCount) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {

            if ((pos % spanCount) == (spanCount - 1)) {
                return true;
            } else {
                return false;
            }
        } else {
            throw new RuntimeException("LayoutManager must be GridLayoutManager");
        }
    }

    /**
     * 获取列数
     *
     * @param parent
     * @return
     */
    private int getSpanCount(RecyclerView parent) {
        int spanCount = -1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        } else {
            throw new RuntimeException("LayoutManager must be GridLayoutManager");
        }
        return spanCount;
    }
}

2.RecyclerView条目点击事件的优化

RecyclerView点击事件很多的做法就是对item每一个都设置点击监听,明显是很消耗性能的方式,这里通过触摸监听来实现点击事件的监听,进行优化,adapter只负责数据的绑定
思路:
1.为RecyclerView添加手势触摸的监听
2.重写点击事件的处理
3.OnRecyclerItemClickListener将别点击的View的id和ViewHolder对象传递给RecyclerView,然后根据业务做处理

这里写图片描述

OnGestureListener主要回调各种单击事件,必须实现所有方法
OnDoubleTapListener主要回调各种双击事件,必须实现所有方法
SimpeOnGestureListener实现了上面两个接口,都是空方法

OnGestureListener:
//用户按下屏幕就会触发
public boolean onDown(MotionEvent e);
//如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行
public void onShowPress(MotionEvent e);
//一次单独的轻击抬起操作,也就是轻击一下屏幕,就是普通点击事件
public boolean onSingleTapUp(MotionEvent e);
//在屏幕上拖动事件
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//长按触摸屏,超过一定时长,就会触发这个事件
public void onLongPress(MotionEvent e);
//滑屏,用户按下触摸屏、快速移动后松开
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);

OnDoubleTapListener:
//单击事件。用来判定该次点击是SingleTap而不是DoubleTap,
//如果连续点击两次就是DoubleTap手势,如果只点击一次,
//系统等待一段时间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,
//然后触发SingleTapConfirmed事件
public boolean onSingleTapConfirmed(MotionEvent e);
//双击事件
public boolean onDoubleTap(MotionEvent e);
//双击间隔中发生的动作。指触发onDoubleTap以后,在双击之间发生的其它动作
public boolean onDoubleTapEvent(MotionEvent e);
/**
 * 实例化手势探测器,需要一个手势监听器:OnGestureListener,
 * OnGestureListener主要回调各种单击事件,必须实现所有方法
 * OnDoubleTapListener主要回调各种双击事件,必须实现所有方法
 * SimpeOnGestureListener实现了上面两个接口,都是空方法
 */

public abstract class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
    private static final String TAG = OnRecyclerItemClickListener.class.getName();
    private GestureDetectorCompat mGestureDetector;
    private RecyclerView recyclerView;

    public OnRecyclerItemClickListener(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener());
    }

    public abstract void onItemClick(int viewId, RecyclerView.ViewHolder viewHolder);

    public abstract void onItemLongClick(RecyclerView.ViewHolder vh);

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        mGestureDetector.onTouchEvent(e);//解析坐标点和触摸规律来识别触摸手势
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        mGestureDetector.onTouchEvent(e);//解析坐标点和触摸规律来识别触摸手势
    }

    @Override//用来处理触摸冲突
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }

    private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override//点击事件处理
        public boolean onSingleTapUp(MotionEvent e) {
            final View itemRecycler = recyclerView.findChildViewUnder(e.getX(), e.getY());//获取被点击item的view
            if (itemRecycler instanceof ViewGroup) {
                ViewGroup itemView = (ViewGroup) itemRecycler;
                for (int i = 0; i < itemView.getChildCount(); i++) {
                    itemView.getChildAt(i).setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            onItemClick(v.getId(), recyclerView.getChildViewHolder(itemRecycler));

                        }
                    });
                }
            } else {
                itemRecycler.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (itemRecycler != null) {
                            onItemClick(itemRecycler.getId(), recyclerView.getChildViewHolder(itemRecycler));
                        }
                    }
                });
            }
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                onItemLongClick(vh);
            }
        }
    }
}
mRecycler.addOnItemTouchListener(new OnRecyclerItemClickListener(mRecycler) {
             @Override
            public void onItemClick(int viewId, RecyclerView.ViewHolder viewHolder) {
                int position = viewHolder.getAdapterPosition();
                MyAdapter.ViewHolder myViewHolder = (MyAdapter.ViewHolder) viewHolder;
              switch (viewId){
                  case R.id.tv:
                      Toast.makeText(MainActivity.this,"第"+position+"个条目的TextView被点击"+myViewHolder.tv.getText(),Toast.LENGTH_SHORT).show();
                      break;
                  case R.id.img:
                      Toast.makeText(MainActivity.this,"第"+position+"个条目的ImageView被点击",Toast.LENGTH_SHORT).show();
                      break;
              }
            }
            @Override
            public void onItemLongClick(RecyclerView.ViewHolder vh) {
            }
        });

3.RecyclerView实现拖拽效果

这里写图片描述     这里写图片描述

/**
 * RecyclerView实现拖拽的帮助类,(swipe方式侧滑删除与拖拽同时存在时,会有冲突,这里只有拖拽)
 */

public class RecyclerViewDragHelper extends ItemTouchHelper.Callback {
    private ItemTouchAdapter mItemTouchAdapter;
    private OnDragListener mOnDragListener;

    public RecyclerViewDragHelper(ItemTouchAdapter itemTouchAdapter) {
        this.mItemTouchAdapter = itemTouchAdapter;
    }

    @Override//设置recyclerview是否支持长按拖拽,默认是true,如果这里返回true,所有的Item都支持拖拽,如果有特定的item不支持拖拽,这里就要返回false,在长按的状态下进行处理
    public boolean isLongPressDragEnabled() {
        return false;
    }

    @Override//设置item是否可以侧滑删除
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override//设置是否处理拖拽事件和滑动事件以及拖拽和滑动操作的方向
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;//拖拽标志
            int swipeFlags = 0;//滑动标志,设置为0,不处理滑动操作
            return makeMovementFlags(dragFlags,swipeFlags);
        }else if (layoutManager instanceof LinearLayoutManager){
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            int swipeFlags = 0 ;//列表可以实现侧滑删除,这里用0 处理,侧滑删除和拖拽都需要长时间按住,所以手势识别有冲突
            return makeMovementFlags(dragFlags,swipeFlags);
        }else {
            throw new IllegalArgumentException("LayoutManager must be GridLayoutManager or LinearLayoutManager");
        }
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        int fromPosition = viewHolder.getAdapterPosition();//获取拖动viewholder的position
        int toPosition = target.getAdapterPosition();//获取目标viewholder的position
        mItemTouchAdapter.onMove(fromPosition,toPosition);
        return true;
    }

    @Override//侧滑删除时调用,这里没有使用
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        int position = viewHolder.getAdapterPosition();
        mItemTouchAdapter.onSwiped(position);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
            float alpha = 1 - Math.abs(dX)/(float)viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);
            viewHolder.itemView.setTranslationX(dX);
        }else {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
    }

    @Override//当长按选中item的时候调用(拖拽的时候)
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG ){
            viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(R.color.md_red_A400));
        }
    }

    @Override//当手指松开时调用
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(R.color.md_red_A200));
        if (mOnDragListener != null) mOnDragListener.onFinishDrag();
    }

    public RecyclerViewDragHelper setOnDragListener(OnDragListener onDragListener){
        this.mOnDragListener = onDragListener;
        return this;
    }

    public  interface OnDragListener{
        void onFinishDrag();
    }

    public interface ItemTouchAdapter{
        void onMove(int fromPosition,int toPosition);//拖拽
        void onSwiped(int position);//侧滑删除
    }
}
  //Adapter要实现拖拽帮助类的接口,拖拽的接口,
  private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements RecyclerViewDragHelper.ItemTouchAdapter{
  ...
   @Override
          public void onMove(int fromPosition, int toPosition) {
              if (fromPosition == mList.size() -1 || toPosition == mList.size() -1){
                  return;
              }
              if (fromPosition < toPosition){
                  for (int i = fromPosition; i < toPosition; i++) {
                      Collections.swap(mList,i,i + 1);
                  }
              }else {
                  for (int i = fromPosition; i > toPosition ; i--) {
                      Collections.swap(mList,i,i -1);
                  }
              }
              notifyItemMoved(fromPosition,toPosition);
          }
   ...
  }
//RecyclerView的监听处理
 mRecycler.addOnItemTouchListener(new OnRecyclerItemClickListener(mRecycler) {
            @Override
            public void onItemClick(RecyclerView.ViewHolder vh) {
                int position = vh.getLayoutPosition();
                Toast.makeText(MainActivity.this,"第"+position+"被点击了",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onItemLongClick(RecyclerView.ViewHolder vh) {
                int childCount = ((RecyclerView)vh.itemView.getParent()).getAdapter().getItemCount();
                if (vh.getLayoutPosition() != childCount -1){//最后一个不能拖动
                    mDragHelper.startDrag(vh);//可以拖动
                    VibratorUtil.vibrate(MainActivity.this,70);
                }
            }
        });
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值