列表侧滑删除(一)

今天为大家介绍下列表的策划删除功能,废话不多说,直接看效果图:


我们先坐下介绍:和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>

奋斗奋斗我们的介绍就这些啦!你还有什么不明白的请告诉我哦。。。


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值