今天为大家介绍下列表的策划删除功能,废话不多说,直接看效果图:
我们先坐下介绍:和QQ列表的侧滑删除功能很像
1.是个列表
2.每一个条目都可以向左滑动,并且滑动第二个条目时,第一个条目返回,显示“取消收藏”,也就是删除。
3.点击“取消收藏”,条目删除,并且不存在列表条目错乱问题。
接下来我们就逐一介绍:
1.使用ListView或者RecyclerView都可,不做介绍。
2.每一个条目都可以向左滑动,并且滑动第二个条目时,第一个条目返回,显示“取消收藏”
这里我用的是RecyclerView,通过图 我们可以看出是三fragment组成,并且每个fragment里都可以上拉加载,下拉刷新(这里对这一部分功能我们以后再做介绍),我们先看代码:这里的每一个条目(也就是列表的item)是一个自定义的item。
我们先看整体的代码,然后再介绍;
1.自定义SlidingButtonView 继承 HorizontalScrollView
/**
* Created by MJJ on 2015/7/25.
* 可以左滑显示删除的view
*/
public class SlidingButtonView extends HorizontalScrollView {
private TextView mTextView_Delete;
private IonSlidingButtonListener mIonSlidingButtonListener;
private int mScrollWidth;
private Boolean isOpen = false;
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);
once = true;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(0, 0);
//获取水平滚动条可以滑动的范围,即右侧按钮的宽度
mScrollWidth = mTextView_Delete.getWidth();
Log.i("asd", "mScrollWidth:" + mScrollWidth);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (mIonSlidingButtonListener != null)
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);
}
/**
* 按滚动条被拖动距离判断关闭或打开菜单
*/
public void changeScrollx() {
if (getScrollX() >= (mScrollWidth / 2)) {//拖动距离大于"取消"按钮的一半
this.smoothScrollTo(mScrollWidth, 0);//滑动至(mScrollWidth, 0) ,smoothScrollTo 与scrollTo相似
isOpen = true;//为打开状态
if (mIonSlidingButtonListener != null)
mIonSlidingButtonListener.onMenuIsOpen(this);//菜单开启
} else {//拖动距离小于"取消"按钮的一半
this.smoothScrollTo(0, 0);//滑动至(0, 0)
isOpen = false;//为关闭状态
}
}
/**
* 打开菜单
*/
public void openMenu() {
if (isOpen) {
return;
}
this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
if (mIonSlidingButtonListener != null)
mIonSlidingButtonListener.onMenuIsOpen(this);
}
/**
* 关闭菜单
*/
public void closeMenu() {
if (!isOpen) {
return;
}
this.smoothScrollTo(0, 0);
isOpen = false;//关闭状态
}
public interface IonSlidingButtonListener {
void onMenuIsOpen(View view);//菜单开启
void onDownOrMove(SlidingButtonView slidingButtonView);//点击或拖动菜单
}
public void setSlidingButtonListener(IonSlidingButtonListener listener) {
mIonSlidingButtonListener = listener;
}
}
我们看到SlidingButtonView继承了一个HorizontalScrollView 具体参考:
http://www.tuicool.com/articles/yQ3E7b
http://blog.csdn.net/lixiang_y/article/details/55102855
http://www.cnblogs.com/xiaoluo501395377/p/3460645.html
这里有一个方法:
public SlidingButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setOverScrollMode(OVER_SCROLL_NEVER);
}
我们需要注意的就是setOverScrollMode(OVER_SCROLL_NEVER); 具体介绍看考:
http://blog.sina.com.cn/s/blog_5da93c8f0102uxxd.html
http://yangsongjing.iteye.com/blog/1973903
方法onMrasue 中:获取空间
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!once) {//如果是第一次
mTextView_Delete = (TextView) findViewById(R.id.tv_delete);
once = true;
}
}
方法onLayout 中:得到空间的宽,并且当changed时,不移动
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(0, 0);
//获取水平滚动条可以滑动的范围,即右侧按钮的宽度
mScrollWidth = mTextView_Delete.getWidth();
Log.i("asd", "mScrollWidth:" + mScrollWidth);
}
}
具体参考:onLayout:
http://blog.csdn.net/hudashi/article/details/50913257
http://www.2cto.com/kf/201511/449947.html
scrollTo: http://blog.csdn.net/bigconvience/article/details/26697645
重写 onTouchEvent :在不同的动作中作出操作:
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (mIonSlidingButtonListener != null)
mIonSlidingButtonListener.onDownOrMove(this);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//按滚动条被拖动距离判断关闭或打开菜单
changeScrollx();
return true;
default:
break;
}
return super.onTouchEvent(ev);
}
当操作为“点击”“滑动” 和 “抬起”“取消” 是不懂得状态中,在“点击”“滑动” 状态 中自定义监听方法onDownOrMove() , 在 “抬起”“取消” 状态中 自定义监听方法 onmenuIsOpen() 判断菜单是否为打开状态。
看监听方法:
public interface IonSlidingButtonListener {
void onMenuIsOpen(View view);//菜单开启
void onDownOrMove(SlidingButtonView slidingButtonView);//点击或拖动菜单
}
public void setSlidingButtonListener(IonSlidingButtonListener listener) {
mIonSlidingButtonListener = listener;
}
通过changeScrollX();判断菜单是否为开启状态:
/**
* 按滚动条被拖动距离判断关闭或打开菜单
*/
public void changeScrollx() {
if (getScrollX() >= (mScrollWidth / 2)) {//拖动距离大于"取消"按钮的一半
this.smoothScrollTo(mScrollWidth, 0);//滑动至(mScrollWidth, 0) ,smoothScrollTo 与scrollTo相似
isOpen = true;//为打开状态
if (mIonSlidingButtonListener != null)
mIonSlidingButtonListener.onMenuIsOpen(this);//菜单开启
} else {//拖动距离小于"取消"按钮的一半
this.smoothScrollTo(0, 0);//滑动至(0, 0)
isOpen = false;//为关闭状态
}
}
2.看一下Adapter 的代码,列表条目的操作都是在adapter中完成的。
/**
* 我的收藏列表 -- 产品
*/
public class MyCollect_ProductAdapter extends RecyclerView.Adapter<MyCollect_ProductAdapter.ViewHolder> implements SlidingButtonView.IonSlidingButtonListener {
private Context mContext;
private LayoutInflater mInflater;
private List<MyCollectListBean.CollectListBean> mDatas;
private SlidingButtonView mMenu = null;
public MyCollect_ProductAdapter(Context context, List<MyCollectListBean.CollectListBean> lists) {
mContext = context;
mDatas = lists;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mInflater = LayoutInflater.from(parent.getContext());
View view = mInflater.inflate(R.layout.item_mycollect_product, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
Glide.with(mContext).load(mDatas.get(position).product_photo).centerCrop().into(holder.iv_img);
holder.tv_product_name.setText(mDatas.get(position).product_name);
holder.tv_product_date.setText(mDatas.get(position).collect_time);
holder.tv_factory_name.setText(mDatas.get(position).p_shop_name);
holder.tv_product_price.setText("¥ " + mDatas.get(position).product_price);
holder.tv_sale_num.setText("销售量: " + mDatas.get(position).product_count_order);
//获取屏幕像素赋值给条目的宽
holder.rl_item.getLayoutParams().width = UIutils.getWindowDisplayMetrics(mContext).widthPixels;
holder.rl_item.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext, Classify_ProductDetailActivity.class);
intent.putExtra("product_id", mDatas.get(position).content_id);
mContext.startActivity(intent);
}
});
holder.tv_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// int realPos = holder.getLayoutPosition();
onLoadToCancleCollect(mDatas.get(position).product_id, position);
}
});
}
@Override
public int getItemCount() {
return mDatas.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RelativeLayout rl_item;
ImageView iv_img;
TextView tv_product_name;
TextView tv_product_date;
TextView tv_factory_name;
TextView tv_product_price;
TextView tv_sale_num;
TextView tv_delete;
public ViewHolder(View itemView) {
super(itemView);
rl_item = (RelativeLayout) itemView.findViewById(R.id.rl_item);
iv_img = (ImageView) itemView.findViewById(R.id.iv_img);
tv_product_name = (TextView) itemView.findViewById(R.id.tv_product_name);
tv_product_date = (TextView) itemView.findViewById(R.id.tv_product_date);
tv_factory_name = (TextView) itemView.findViewById(R.id.tv_factory_name);
tv_product_price = (TextView) itemView.findViewById(R.id.tv_product_price);
tv_sale_num = (TextView) itemView.findViewById(R.id.tv_sale_num);
tv_delete = (TextView) itemView.findViewById(R.id.tv_delete);
//获取监听
((SlidingButtonView) itemView).setSlidingButtonListener(MyCollect_ProductAdapter.this);
}
}
/**
* 取消收藏
*
* @param product_id 收藏 产品product_id
* @param position 取消的位置position
*/
private void onLoadToCancleCollect(String product_id, final int position) {
String user_id = SpUtils.getUserId(mContext);
ServiceApi.getCancleCollect(user_id, product_id, "0", new MyString2Callback() {
@Override
public void onError(Call call, Exception e) {
ToastUtils.showInternetErrorToast();
}
@Override
public void onResponse(Call call, String s) {
BaseBean result = new Gson().fromJson(s, BaseBean.class);
if (result.code == 0) {
//取消成功
mDatas.remove(position);
notifyItemRemoved(position);
if (position != mDatas.size()) {
notifyItemRangeChanged(position, mDatas.size() - position);
}
} else {
ToastUtils.showToast(result.msg);
}
}
});
}
@Override
public void onMenuIsOpen(View view) { //菜单开启,//mMenu 当菜单开启才会获得mMenu
mMenu = (SlidingButtonView) view;
}
@Override
public void onDownOrMove(SlidingButtonView slidingButtonView) {//点击或拖动菜单
if (menuIsOpen()) { //判断是否有菜单打开 ,有菜单是开启状态
if (mMenu != slidingButtonView) { //判断拖动的菜单是否是已开启的菜单
closeMenu();//关闭已开启的菜单
}
}
}
/**
* 关闭菜单
*关闭已开启的菜单
*/
public void closeMenu() {
mMenu.closeMenu(); //关闭菜单 ,调取SlidingButtonView中方法
mMenu = null;
}
/**
* 判断是否有菜单打开
*/
public Boolean menuIsOpen() {
if (mMenu != null) { //mMenu 当已经有菜单是开启状态才会获得mMenu
return true;
}
Log.i("asd", "mMenu为null");
return false;
}
}
3.对于点击“取消收藏”,条目删除,并且不存在列表条目错乱问题。解决的方法:
if (result.code == 0) {
//取消成功
mDatas.remove(position);
notifyItemRemoved(position);
if (position != mDatas.size()) {
notifyItemRangeChanged(position, mDatas.size() - position);
}
具体介绍参考:http://blog.csdn.net/lijinweii/article/details/72636753
最后我们展示下xml,文件:item_mycollect_product.xml
<?xml version="1.0" encoding="utf-8"?>
<包名.view.SlidingButtonView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/slidingbt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_delete"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_alignBottom="@id/rl_item"
android:layout_alignTop="@+id/rl_item"
android:layout_toRightOf="@+id/rl_item"
android:background="@color/red"
android:gravity="center"
android:text="取消收藏"
android:textColor="#000000" />
<RelativeLayout
android:id="@+id/rl_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white">
<ImageView
android:id="@+id/iv_img"
android:layout_width="110dp"
android:layout_height="90dp"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:background="@drawable/error_img"
android:scaleType="fitXY"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/iv_img"
android:layout_alignTop="@+id/iv_img"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_toRightOf="@id/iv_img">
<TextView
android:id="@+id/tv_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@+id/tv_product_date"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/default_text"
android:textColor="@color/black"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_product_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/tv_product_name"
android:layout_alignParentRight="true"
android:text="@string/default_text"
android:textColor="@color/tv_gray"
android:textSize="11sp" />
<TextView
android:id="@+id/tv_factory_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/default_text"
android:textColor="@color/tv_black_light"
android:textSize="13sp" />
<TextView
android:id="@+id/tv_product_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:minWidth="50dp"
android:text="@string/default_text"
android:textColor="@color/tv_orange"
android:textSize="13sp" />
<TextView
android:id="@+id/tv_sale_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/tv_product_price"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_product_price"
android:text="@string/default_text"
android:textSize="13sp" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
</包名.view.SlidingButtonView>
我们的介绍就这些啦!你还有什么不明白的请告诉我哦。。。