利用Scroller来写一个侧滑布局效果如下:
ezgif-5-2511a3329a.gif
1.自定义一个Viewgroup在构造方法初始化参数
mScroller = new Scroller(context); //滑动临界值 ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(viewConfiguration);
2.在onMeasure方法中测量子孩子大小
if (!mIsMeasure) { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); } mIsMeasure = true; }
3.在onLayout布局中初始化子孩子的位置里面有三个view分别是文本内容,置顶以及删除
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); if (i == 0) { mTextWidth = view.getMeasuredWidth(); view.layout(0, 0, mTextWidth, view.getMeasuredHeight()); } else if (i == 1) { //置顶文本的宽度 mStickWidth = view.getMeasuredWidth(); view.layout(mTextWidth, 0, mTextWidth + mStickWidth, view.getMeasuredHeight()); } else if (i == 2) { //删除文本的宽度 mDeleteWidth = view.getMeasuredWidth(); view.layout(mTextWidth + mStickWidth, 0, mTextWidth + mStickWidth + mDeleteWidth, view.getMeasuredHeight()); } } }
4.在onInterceptTouchEvent方法中处理滑动事件,如果是横向滑动我们就拦截事件自己去处理事件
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = (int) ev.getRawX(); break; case MotionEvent.ACTION_MOVE: mMovex = (int) ev.getRawX(); float diff = Math.abs(mMovex - mDownX); // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件 mLastMove = mMovex; if (diff > mTouchSlop) { return true; } break; } return super.onInterceptTouchEvent(ev); }
5.在onTouchEvent方法中处理滑动事件,用ScrollBy方法来移动控件,移动过程中对移动范围进行限制
move事件中:
mMovex = (int) event.getX(); scrollBy(mLastMove - mMovex, 0); if (getScrollX() <= 0) { scrollTo(0, 0); return true; } if (getScrollX() >= mDeleteWidth + mStickWidth) { scrollTo(mDeleteWidth + mStickWidth, 0); return true; } mLastMove = mMovex;
当手指抬起的时候根据移动的位置来判断是否是打开还是关闭getScrollX()方法可以得到移动的x值,大于置顶和删除文本宽度一半时就是打开状态否则就是关闭
if (getScrollX() >= mDeleteWidth) {//open dx = mDeleteWidth + mStickWidth - getScrollX(); if (mOnStateChangeListener != null) { mOnStateChangeListener.onOpen(this); } } else {//close dx = 0 - getScrollX(); mOnStateChangeListener.onClose(this); } mScroller.startScroll(getScrollX(), 0, dx, 0); invalidate();
6.在配合Recycleview使用的时候要注意的地方:
在onTouchEvent方法中move事件要请求父控件不要拦截否则收不到up事件
//请求父容器不要拦截事件getParent().requestDisallowInterceptTouchEvent(true);
在侧滑时一个条目时其他条目都要关闭这里利用adapter进行对当前滑动条目的记录和已经打开的条目记录,然后在move的时候进行判断
case MotionEvent.ACTION_MOVE: if (getAdapter().getOpenView() != null && getAdapter().getOpenView() != this) { getAdapter().getOpenView().close(); getAdapter().setOpenView(null); return true; } if (getAdapter().getCurrentDeleteView() !=null && getAdapter().getCurrentDeleteView() != this) { return true; }
完整代码:
public class DeleteView extends ViewGroup { private Scroller mScroller; private int mDownX; private int mMovex; private int mTouchSlop; private boolean mIsMeasure = false; private int mTextWidth; private int mStickWidth; private int mDeleteWidth; private int mLastMove; public DeleteView(Context context) { this(context, null); } public DeleteView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mScroller = new Scroller(context); //滑动临界值 ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(viewConfiguration); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!mIsMeasure) { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); } mIsMeasure = true; } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); if (i == 0) { mTextWidth = view.getMeasuredWidth(); view.layout(0, 0, mTextWidth, view.getMeasuredHeight()); } else if (i == 1) { mStickWidth = view.getMeasuredWidth(); view.layout(mTextWidth, 0, mTextWidth + mStickWidth, view.getMeasuredHeight()); } else if (i == 2) { mDeleteWidth = view.getMeasuredWidth(); view.layout(mTextWidth + mStickWidth, 0, mTextWidth + mStickWidth + mDeleteWidth, view.getMeasuredHeight()); } } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = (int) ev.getRawX(); break; case MotionEvent.ACTION_MOVE: mMovex = (int) ev.getRawX(); float diff = Math.abs(mMovex - mDownX); // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件 mLastMove = mMovex; if (diff > mTouchSlop) { return true; } break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: if (getAdapter().getOpenView() != null && getAdapter().getOpenView() != this) { getAdapter().getOpenView().close(); getAdapter().setOpenView(null); return true; } if (getAdapter().getCurrentDeleteView() !=null && getAdapter().getCurrentDeleteView() != this) { return true; } getAdapter().setCurrentDeleteView(this); //请求父容器不要拦截事件 getParent().requestDisallowInterceptTouchEvent(true); mMovex = (int) event.getX(); scrollBy(mLastMove - mMovex, 0); if (getScrollX() <= 0) { scrollTo(0, 0); return true; } if (getScrollX() >= mDeleteWidth + mStickWidth) { scrollTo(mDeleteWidth + mStickWidth, 0); return true; } mLastMove = mMovex; break; case MotionEvent.ACTION_UP: LogUtils.LogE("up"); int dx = 0; if (getScrollX() >= mDeleteWidth) {//open dx = mDeleteWidth + mStickWidth - getScrollX(); if (mOnStateChangeListener != null) { mOnStateChangeListener.onOpen(this); } } else {//close dx = 0 - getScrollX(); mOnStateChangeListener.onClose(this); } mScroller.startScroll(getScrollX(), 0, dx, 0); invalidate(); getAdapter().setCurrentDeleteView(null); break; } return true; } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } } public interface onStateChangeListener { void onOpen(DeleteView deleteView); void onClose(DeleteView deleteView); } private onStateChangeListener mOnStateChangeListener; public void setOnStateChangeListener(onStateChangeListener onStateChangeListener) { mOnStateChangeListener = onStateChangeListener; } public void open() { scrollTo(mStickWidth + mDeleteWidth, 0); } public void close() { scrollTo(0, 0); } /** * 得到适配器 * * @return */ private DeleteAdapter getAdapter() { return (DeleteAdapter) ((RecyclerView) getParent()).getAdapter(); }
Adapter代码:
public class DeleteAdapter extends RecyclerView.Adapter { private List<CartModel> mDatas; private DeleteView mDeleteView; private DeleteView mOpenView; public DeleteAdapter(List<CartModel> list) { mDatas = list; } public void setData(List<CartModel> list) { mDatas = list; notifyDataSetChanged(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_delete, parent, false); MyHolder holder = new MyHolder(view); return holder; } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { MyHolder myHolder = (MyHolder) holder; myHolder.setItem(position); ((DeleteView) holder.itemView).setOnStateChangeListener(new DeleteView.onStateChangeListener() { @Override public void onOpen(DeleteView deleteView) { mOpenView = deleteView; } @Override public void onClose(DeleteView deleteView) { } }); myHolder.delete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(v.getContext(),"删除"+position,Toast.LENGTH_SHORT).show(); mDatas.remove(position); getOpenView().close(); notifyDataSetChanged(); } }); myHolder.stick.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { CartModel cartModel = mDatas.get(position); Toast.makeText(v.getContext(),"置顶"+position,Toast.LENGTH_SHORT).show(); mDatas.remove(cartModel); mDatas.add(0,cartModel); getOpenView().close(); notifyDataSetChanged(); } }); } /** * 得到当前滑动的view * * @return */ public DeleteView getCurrentDeleteView() { return mDeleteView; } /** * 得到当前滑动的view * * @return */ public void setCurrentDeleteView(DeleteView deleteView) { this.mDeleteView = deleteView; } public DeleteView getOpenView() { return mOpenView; } public void setOpenView(DeleteView openView) { mOpenView = openView; } @Override public int getItemCount() {//加的一是脚步局 return mDatas.size(); } class MyHolder extends RecyclerView.ViewHolder { TextView title; TextView stick; TextView delete; public MyHolder(View itemView) { super(itemView); title = itemView.findViewById(R.id.tv_title); stick = itemView.findViewById(R.id.tv_stick); delete = itemView.findViewById(R.id.tv_delete); } public void setItem(int position) { CartModel cartModel = mDatas.get(position); title.setText(cartModel.getName()); } } }
作者:xlh__
链接:https://www.jianshu.com/p/34e756508549