利用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
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
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
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
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 mDatas; private DeleteView mDeleteView; private DeleteView mOpenView; public DeleteAdapter(List list) {
mDatas = list;
} public void setData(List 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