项目中要实现的功能,之前找了很久发现网上大部分的侧滑删除和列表全选都是ListView的实现,而对RecyclerView的实现却是少之又少,所以花了很多时间实现了一个还比较满意的版本,
效果如下:
侧滑删除(带自动校位滑动效果):
右滑出现选择框:
一键编辑(全选):
实现原理:
1.首先需要实现一个基本的RecyclerView。
2. 自定义Item的布局。
3.结合自定义item布局通过自定义Item项实现左右滑效果。
代码实现:
1.首先是实现RecyclerView的基本用法,这里我们先要实现item的布局:layout_item.xml
布局预览:
注意:因为逻辑问题所以可以这样划分:id为rl_left的RelativeLayout为左侧想自定义的布局,可以直接在这个RelativeLayout里修改,id为text的TextView为中部部分布局,可以替换为其他Viewgroup并自定义。
删除布局是id为tv_delete的TextView,也可以随意自定义替换。
2.Adapter实现:
public class MyAdapter extends RecyclerView.Adapter implements SlidingButtonView.IonSlidingButtonListener {
Context context;
private IonSlidingViewClickListener mIDeleteBtnClickListener;
private List mDatas = new ArrayList();
private SlidingButtonView mMenu = null;
public MyAdapter(Context context, ArrayList date) {
this.context = context;
this.mDatas = date;
mIDeleteBtnClickListener = (IonSlidingViewClickListener) context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_item, parent, false);
return new MyViewHolder(view);
}
boolean allopen = false;
public void setAllopen(boolean allopen) {
this.allopen = allopen;
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
final MyViewHolder viewHolder = (MyViewHolder) holder;
viewHolder.slidingButtonView.setSlidingButtonListener(MyAdapter.this);
viewHolder.textView.setText(mDatas.get(position));
//设置内容布局的宽为屏幕宽度
viewHolder.layout_content.getLayoutParams().width = Utils.getScreenWidth(context) + viewHolder.rl_left.getLayoutParams().width;
// viewHolder.textView.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// //判断是否有删除菜单打开
//// if (menuIsOpen()) {
//// closeMenu();//关闭菜单
//// } else {
//// int n = viewHolder.getLayoutPosition();
//// mIDeleteBtnClickListener.onItemClick(v, n);
//// }
//
// }
// });
viewHolder.btn_Delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int n = holder.getLayoutPosition();
mIDeleteBtnClickListener.onDeleteBtnCilck(v, n);
}
});
LogUtils.d("项:" + position + "是否开:" + allopen);
if (allopen) {
LogUtils.d("打开?");
viewHolder.slidingButtonView.openMenu();
viewHolder.slidingButtonView.setCanTouch(false);
} else {
viewHolder.slidingButtonView.closeMenu();
viewHolder.slidingButtonView.setCanTouch(true);
}
}
@Override
public int getItemCount() {
return mDatas.size();
}
/**
* 删除菜单打开信息接收
*/
@Override
public void onMenuIsOpen(View view) {
mMenu = (SlidingButtonView) view;
}
/**
* 滑动或者点击了Item监听
*
* @param slidingButtonView
*/
@Override
public void onDownOrMove(SlidingButtonView slidingButtonView) {
if (menuIsOpen()) {
if (mMenu != slidingButtonView) {
closeMenu();
}
}
}
/**
* 关闭菜单
*/
public void closeMenu() {
mMenu.closeMenu();
mMenu = null;
}
/**
* 判断是否有菜单打开
*/
public Boolean menuIsOpen() {
if (mMenu != null) {
return true;
}
return false;
}
public interface IonSlidingViewClickListener {
void onItemClick(View view, int position);
void onDeleteBtnCilck(View view, int position);
}
public void addData(int position) {
mDatas.add(position, "添加项");
notifyItemInserted(position);
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
}
这里没有太多要说的,主要就是一个判断是否打开左侧编辑菜单的标识符boolean值allopen的判定方法,其他都为基础的RecyclerView的Adapter使用方法。
在Activity中的使用也是基础的方法,这里不提,后面源码下载可以看到。
3.Item侧滑效果实现类SlidingButtonView:
public class SlidingButtonView extends HorizontalScrollView {
//删除按钮
private TextView mTextView_Delete;
//左侧控件
private RadioButton rbtn;
private TextView text;
private int leftWidth;
//记录滚动条滚动的距离
private int mScrollWidth;
public int getLeftWidth() {
return leftWidth;
}
//自定义的接口,用于传达滑动事件
private IonSlidingButtonListener mIonSlidingButtonListener;
//记录按钮菜单是否打开,默认关闭false
private Boolean isOpen = false;
public Boolean getOpen() {
return isOpen;
}
public void setOpen(Boolean open) {
isOpen = open;
}
//在onMeasure中只执行一次的判断
private Boolean once = false;
public SlidingButtonView(Context context) {
this(context, null);
}
public SlidingButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setOverScrollMode(OVER_SCROLL_NEVER);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!once) {
//只需要执行一次
mTextView_Delete = (TextView) findViewById(R.id.tv_delete);
rbtn = (RadioButton) findViewById(R.id.rbtn);
text = (TextView) findViewById(R.id.text);
once = true;
}
}
//使Item在每次变更布局大小时回到初始位置,并且获取滚动条的可移动距离
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
//获取水平滚动条可以滑动的范围,即右侧按钮的宽度
mScrollWidth = mTextView_Delete.getWidth();
leftWidth = rbtn.getWidth();
this.scrollTo(leftWidth, 0);
// LogUtils.d("可以滑动的范围:" + mScrollWidth);
}
}
private boolean canTouch = true;
public boolean isCanTouch() {
return canTouch;
}
public void setCanTouch(boolean canTouch) {
this.canTouch = canTouch;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!canTouch) {
return true;
}
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mIonSlidingButtonListener.onDownOrMove(this);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
changeScrollx();
return true;
default:
break;
}
return super.onTouchEvent(ev);
}
//滚动监听,为了让删除按钮显示在项的背后的效果
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// mTextView_Delete.setTranslationX(l - mScrollWidth -100);
// mTextView_Delete.setTranslationX(l - mScrollWidth );
//this.setX(l);
}
public void changeScrollx() {
// LogUtils.d("getScrollX(): " + getScrollX());
// LogUtils.d("mScrollWidth: " + mScrollWidth);
// LogUtils.d("leftWidth: " + leftWidth);
if (getScrollX()-leftWidth >= (mScrollWidth / 2)) {
this.smoothScrollTo(mScrollWidth + leftWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
} else {
this.smoothScrollTo(leftWidth, 0);
isOpen = false;
}
}
Handler scrollHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what==2) {
SlidingButtonView.this.smoothScrollTo(leftWidth,0);
} else if (msg.what==1) {
SlidingButtonView.this.smoothScrollTo(0,0);
}
}
};
public void openMenu() {
// if (isOpen) {
// return;
// }
Message msg = new Message();
msg.what= 1;
scrollHandler.sendMessage(msg);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
}
public void closeMenu() {
if (!isOpen) {
return;
}
Message msg = new Message();
msg.what= 2;
scrollHandler.sendMessage(msg);
isOpen = false;
}
public void setSlidingButtonListener(IonSlidingButtonListener listener) {
mIonSlidingButtonListener = listener;
}
public interface IonSlidingButtonListener {
void onMenuIsOpen(View view);
void onDownOrMove(SlidingButtonView slidingButtonView);
}
}
这个类是重点所在,我们按方法来一个一个讲:
onMeasure:
进行布局的初始化操作。
onLayout:
获取水平滚动条可以滑动的范围,即右侧按钮的宽度。
滑动到初始范围。
setCanTouch:
设置是否响应触摸事件。
onTouchEvent:
点击事件判断,在移动事件:MotionEvent.ACTION_MOVE中执行拖动条的滑动。
在事件取消:MotionEvent.ACTION_CANCEL中执行让滑动条滚动回到原位。
源码下载:
源码下载