开源控件学习之-侧滑listview进行删除(一)(自定义listview)

最近工作的项目中需要用到listview的侧滑删除。

参考了网上的一些项目做出了更改,这里做出一篇笔记,希望对看到的人有一些帮助。

实现步骤一:定义自定义控件SlideListView

/**
 * Created by Anthony on 16/1/4.
 */
public class SlideListView extends ListView {

    /**
     * 禁止侧滑模式
     */
    public static int MOD_FORBID = 0;
    /**
     * 从左向右滑出菜单模式
     */
    public static int MOD_LEFT = 1;
    /**
     * 从右向左滑出菜单模式
     */
    public static int MOD_RIGHT = 2;
    /**
     * 左右均可以滑出菜单模式
     */
    public static int MOD_BOTH = 3;
    /**
     * 当前的模式
     */
    private int mode = MOD_FORBID;
    /**
     * 左侧菜单的长度
     */
    private int leftLength = 0;
    /**
     * 右侧菜单的长度
     */
    private int rightLength = 0;

    /**
     * 当前滑动的ListView position
     */
    private int slidePosition;
    /**
     * 手指按下X的坐标
     */
    private int downY;
    /**
     * 手指按下Y的坐标
     */
    private int downX;
    /**
     * ListView的item
     */
    private View itemView;
    /**
     * 滑动类
     */
    private Scroller scroller;
    /**
     * 认为是用户滑动的最小距离
     */
    private int mTouchSlop;

    /**
     * 判断是否可以侧向滑动
     */
    private boolean canMove = false;
    /**
     * 标示是否完成侧滑
     */
    private boolean isSlided = false;

    public SlideListView(Context context) {
        this(context, null);
    }

    public SlideListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlideListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        scroller = new Scroller(context);
        //getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    /**
     * 初始化菜单的滑出模式
     *
     * @param mode
     */
    public void initSlideMode(int mode) {
        this.mode = mode;
    }

    /**
     * 处理我们拖动ListView item的逻辑
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        final int action = ev.getAction();
        int lastX = (int) ev.getX();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                System.out.println("touch-->" + "down");
                //当前模式不允许滑动,则直接返回,交给ListView自身去处理
                if (this.mode == MOD_FORBID) {
                    return super.onTouchEvent(ev);
                }
                // 如果处于侧滑完成状态,侧滑回去,并直接返回
                if (isSlided) {
                    scrollBack();
                    return false;
                }
                // 假如scroller滚动还没有结束,我们直接返回
                if (!scroller.isFinished()) {
                    return false;
                }
                downX = (int) ev.getX();
                downY = (int) ev.getY();
                //通过当前位置xy值获取当前在第几个item
                slidePosition = pointToPosition(downX, downY);

                // 无效的position, 不做任何处理
                if (slidePosition == AdapterView.INVALID_POSITION) {
                    return super.onTouchEvent(ev);
                }

                // 获取我们点击的item view
                itemView = getChildAt(slidePosition - getFirstVisiblePosition());

			/*此处根据设置的滑动模式,自动获取左侧或右侧菜单的长度*/
                if (this.mode == MOD_BOTH) {
                    this.leftLength = -itemView.getPaddingLeft();
                    this.rightLength = -itemView.getPaddingRight();
                } else if (this.mode == MOD_LEFT) {
                    this.leftLength = -itemView.getPaddingLeft();
                } else if (this.mode == MOD_RIGHT) {
                    this.rightLength = - itemView.getPaddingRight();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                System.out.println("touch-->" + "move");
                if (!canMove
                        && slidePosition != AdapterView.INVALID_POSITION
                        && (Math.abs(ev.getX() - downX) > mTouchSlop && Math.abs(ev
                        .getY() - downY) < mTouchSlop)) {
                    int offsetX = downX - lastX;
                    if (offsetX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)) {
                    /*从右向左滑*/
                        canMove = true;
                    } else if (offsetX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)) {
                    /*从左向右滑*/
                        canMove = true;
                    } else {
                        canMove = false;
                    }
                /*此段代码是为了避免我们在侧向滑动时同时触发ListView的OnItemClickListener事件*/
                    MotionEvent cancelEvent = MotionEvent.obtain(ev);
                    cancelEvent
                            .setAction(MotionEvent.ACTION_CANCEL
                                    | (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
                    onTouchEvent(cancelEvent);
                }
                if (canMove) {
                //设置此属性,可以在侧向滑动时,保持ListView不会上下滚动
                    requestDisallowInterceptTouchEvent(true);
                    // 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚
                    int deltaX = downX - lastX;
                    if (deltaX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)) {
                    /*向左滑*/
                        itemView.scrollTo(deltaX, 0);
                    } else if (deltaX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)) {
					/*向右滑*/
                        itemView.scrollTo(deltaX, 0);
                    } else {
                        itemView.scrollTo(0, 0);
                    }
                    return true; // 拖动的时候ListView不滚动
                }
            case MotionEvent.ACTION_UP:
                System.out.println("touch-->" + "up");
                if (canMove) {
                    canMove = false;
                    scrollByDistanceX();
                }
                break;
        }
        // 否则直接交给ListView来处理onTouchEvent事件
        return super.onTouchEvent(ev);
    }

    /**
     * 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动
     */
    private void scrollByDistanceX() {
		/*当前模式不允许滑动,则直接返回*/
        if (this.mode == MOD_FORBID) {
            return;
        }
        if (itemView.getScrollX() > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)) {
			/*从右向左滑*/
            if (itemView.getScrollX() >= rightLength / 2) {
                scrollLeft();
            } else {
                // 滚回到原始位置
                scrollBack();
            }
        } else if (itemView.getScrollX() < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)) {
			/*从左向右滑*/
            if (itemView.getScrollX() <= -leftLength / 2) {
                scrollRight();
            } else {
                // 滚回到原始位置
                scrollBack();
            }
        } else {
            // 滚回到原始位置
            scrollBack();
        }

    }

    /**
     * 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值
     */
    private void scrollRight() {
        isSlided = true;
        final int delta = (leftLength + itemView.getScrollX());
        // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
        scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,
                Math.abs(delta));
        postInvalidate(); // 刷新itemView
    }

    /**
     * 向左滑动,根据上面我们知道向左滑动为正值
     */
    private void scrollLeft() {
        isSlided = true;
        final int delta = (rightLength - itemView.getScrollX());
        // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
        scroller.startScroll(itemView.getScrollX(), 0, delta, 0,
                Math.abs(delta));
        postInvalidate(); // 刷新itemView
    }

    /**
     * 滑动会原来的位置
     */
    private void scrollBack() {
        isSlided = false;
        scroller.startScroll(itemView.getScrollX(), 0, -itemView.getScrollX(),
                0, Math.abs(itemView.getScrollX()));
        postInvalidate(); // 刷新itemView
    }

    /**
     * 通常是用mScroller记录/计算View滚动的位置,
     * 再重写View的computeScroll(),完成实际的滚动。
     */
    @Override
    public void computeScroll() {
        // 调用startScroll的时候scroller.computeScrollOffset()返回true,
        if (scroller.computeScrollOffset()) {
            // 让ListView item根据当前的滚动偏移量进行滚动
            itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());
            postInvalidate();
        }
    }

    /**
     * 提供给外部调用,用以将侧滑出来的滑回去
     */
    public void slideBack() {
        this.scrollBack();
    }

}
实现步骤二:整体activity的布局如下,也就是一个listview:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                >

    <com.test.view.SlideListView
            android:id="@+id/list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="#dddbdb"
            android:dividerHeight="1.0px"
            android:drawSelectorOnTop="true"
            android:listSelector="@drawable/list_item_selector"
            android:scrollbars="none" />

</RelativeLayout>

实现步骤三:listItem的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:background="@color/bg_channel"
              android:paddingRight="-90dp"
              android:orientation="horizontal"
              android:layout_height="@dimen/size100">
    <RelativeLayout android:id="@+id/layout_content"
                    android:layout_height="@dimen/size100"
                    android:layout_centerVertical="true"
                    android:layout_width="fill_parent">
        
        <TextView android:id="@+id/txt_content"
                  android:layout_width="fill_parent"
                  android:layout_height="wrap_content"
                  android:layout_marginLeft="@dimen/size10"
                  android:ellipsize="end"
                  android:lines="1"
                  android:textColor="@color/vtibet_black"
                  android:textSize="16sp" />
        
    </RelativeLayout>
    <LinearLayout
            android:id="@+id/layout_right"
            android:layout_width="90dp"
            android:layout_height="match_parent"
            android:orientation="vertical"
    >
        <RelativeLayout
                android:id="@+id/delete_item"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/tomato"
                android:clickable="true">
            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:gravity="center"
                    android:text="删除"
                    android:textColor="@color/white"
                    android:textSize="15sp"/>

        </RelativeLayout>

    </LinearLayout>

</LinearLayout>
对于listItem,我们主要通过的是PaddingRight来实现的把我们的id为layout_right的控件隐藏。这也是下拉刷新的实现原理之一,通过padding设置为负数来隐藏我们的一些控件。然后需要将我们的item上面的delete的布局的clickable置为true,不然你会发现无法点击item上面的删除键。


实现步骤四:在我们的activity中进行调用:

        list_view = (SlideListView) view.findViewById(R.id.list_view);
        list_view.initSlideMode(SlideListView.MOD_RIGHT);
        list_view.setOnItemClickListener(this);
        list_view.setOnItemLongClickListener(this);
......在自己的adapter的getView中:
<pre name="code" class="html">            final RelativeLayout delete_item = (RelativeLayout) convertView.findViewById(R.id.delete_item);

            delete_item.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    delete(item.getId(), position);//删除数据
                }
            });
......

 在步骤四中需要注意的是,我们需要initSlideMode,不然我在自定义的控件中会抛出空指针错误。 

此时,有的人可能会在listview的adapter中添加我们item的点击事件,这样我们就不可以进行滑动了。这里牵涉到事件机制。

推荐关注的文章:

http://blog.csdn.net/liyuanjinglyj/article/details/48550489

http://blog.csdn.net/jiangwei0910410003/article/details/17504315

http://blog.csdn.net/guolin_blog/article/details/9097463/

http://blog.csdn.net/xiaanming/article/details/21696315注意事项:1 左、右侧滑出菜单的ListView(划出后选择进行操作,本项目主要是进行删除);2 使用请注意与ListView的Item的布局配合;3 该效果的实现是基于在Item的布局中通过设置PaddingLeft和PaddingRight来隐藏左右菜单的, 所以使用此ListView时,请务必在布局Item时使用PaddingLeft和PaddingRight。

好了。当做对这个问题处理的笔记,也希望对看到的人真心有帮助。

<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'宋体';font-size:12.0pt;">

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值