点滴RecyclerView

来源:互联网 发布:aloha软件 编辑:程序博客网 时间:2024/06/05 08:21

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);                }            }        });
原创粉丝点击