Android自定义控件:类QQ抽屉效果

其实网上类似的实现已经很多了,原理也并不难,只是网上各种demo运行下来,多少都有一些问题。折腾了半天,决定自己实现一个。
首先我们看看实现效果:


这里写图片描述

对比网上各类demo,这次要实现的主要表现在以下几点:
1.侧滑显示抽屉view
2.侧滑抽屉隐藏view控件点击事件
3.单击任意item隐藏显示的抽屉view
4.滑动list隐藏显示的抽屉view
5.增加SwipeLayout点击事件和Swipe touch事件判断处理
6.优化快速划开多个抽屉隐藏view时多个SwipeLayout滑动状态判断处理,仅显示最后一个滑动的抽屉隐藏view,隐藏前面所有打开的抽屉view(快速滑动时,可能存在多个抽屉view打开情况,网上找的几个demo主要问题都集中在这一块)

实现原理

其实单就一个SwipeLayout的实现原理来讲的话,还是很简单的,实际上单个SwipeLayout隐藏抽屉状态时,应该是这样的:


这里写图片描述

也就是说,最初的隐藏状态,实际上是将hide view区域layout到conten view的右边,达到隐藏效果,而后显示则是根据拖拽的x值变化来动态的layout 2个view,从而达到一个滑动抽屉效果。
当然,直接重写view的onTouchEvent来动态的layout 2个view是可以实现我们需要的效果的,但是有更好的方法来实现,就是同过ViewDragHelper。
ViewDragHelper是google官方提供的一个专门用于手势分析处理的类,关于ViewDragHelper的基本使用,网上有一大堆的资源。具体的ViewDragHelper介绍以及基本使用方法,本文就不重复造轮子了,此处推荐鸿洋大神的一篇微博:http://blog.csdn.net/lmj623565791/article/details/46858663

具体实现

下面我们开始具体的实现。
布局比较简单,这里就不贴代码了,最后会贴上本demo的完整代码地址。

首先我们实现一个继承FrameLayout的自定义SwipeLauout,重写onFinishInflate方法:
这里我们只允许SwipeLayout设置2个子View,ContentLayout是继承LinearLayout的自定义layout,后面会讲到这个,此处先略过;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalStateException("Must 2 views in SwipeLayout");
        }
        contentView = getChildAt(0);
        hideView = getChildAt(1);
        if (contentView instanceof ContentLayout)
            ((ContentLayout) contentView).setSwipeLayout(this);
        else {
            throw new IllegalStateException("content view must be an instanceof FrontLayout");
        }
    }

接着重写onSizeChanged,onLayout,onInterceptTouchEvent方法:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        hideViewHeight = hideView.getMeasuredHeight();
        hideViewWidth = hideView.getMeasuredWidth();
        contentWidth = contentView.getMeasuredWidth();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        // super.onLayout(changed, left, top, right, bottom);
        contentView.layout(0, 0, contentWidth, hideViewHeight);
        hideView.layout(contentView.getRight(), 0, contentView.getRight()
                + hideViewWidth, hideViewHeight);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
        //        Log.e("SwipeLayout", "-----onInterceptTouchEvent-----");
        return result;
    }

然后是比较关键的,重写onTouchEvent方法以及ViewDragHelper.Callback回调,我们定了一个enum来判断SwipeLayout的三种状态。在onViewPositionChanged中,有2种方法实现content view和hide view的伴随移动,一种是直接offset view的横向变化量,还有一种就是直接通过layout的方式,两种方式都可以。

    public enum SwipeState {
        Open, Swiping, Close;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //        Log.e("SwipeLayout", "-----onTouchEvent-----");
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 1.获取x和y方向移动的距离
                float moveX = event.getX();
                float moveY = event.getY();
                float delatX = moveX - downX;// x方向移动的距离
                float delatY = moveY - downY;// y方向移动的距离

                if (Math.abs(delatX) > Math.abs(delatY)) {
                    // 表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求listview不要拦截
                    this.requestDisallowInterceptTouchEvent(true);
                }

                // 更新downX,downY
                downX = moveX;
                downY = moveY;
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        viewDragHelper.processTouchEvent(event);
        return true;
    }

    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == contentView || child == hideView;
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return hideViewWidth;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (child == contentView) {
                if (left > 0)
                    left = 0;
                if (left < -hideViewWidth)
                    left = -hideViewWidth;
            } else if (child == hideView) {
                if (left > contentWidth)
                    left = contentWidth;
                if (left < (contentWidth - hideViewWidth))
                    left = contentWidth - hideViewWidth;
            }
            return left;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top,
                                          int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            if (changedView == contentView) {
                // 如果手指滑动deleteView,那么也要讲横向变化量dx设置给contentView
                hideView.offsetLeftAndRight(dx);
            } else if (changedView == hideView) {
                // 如果手指滑动contentView,那么也要讲横向变化量dx设置给deleteView
                contentView.offsetLeftAndRight(dx);
            }

            //            if (changedView == contentView) {
            //                // 手动移动deleteView
            //                hideView.layout(hideView.getLeft() + dx,
            //                        hideView.getTop() + dy, hideView.getRight() + dx,
            //                        hideView.getBottom() + dy);
            //            } else if (hideView == changedView) {
            //                // 手动移动contentView
            //                contentView.layout(contentView.getLeft() + dx,
            //                        contentView.getTop() + dy, contentView.getRight() + dx,
            //                        contentView.getBottom() + dy);
            //            }
            //实时更新当前状态
            updateSwipeStates();
            invalidate();
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            //根据用户滑动速度处理开关
            //xvel: x方向滑动速度
            //yvel: y方向滑动速度
            //            Log.e("tag", "currentState = " + currentState);
            //            Log.e("tag", "xvel = " + xvel);
            if (xvel < -200 && currentState != SwipeState.Open) {
                open();
                return;
            } else if (xvel > 200 && currentState != SwipeState.Close) {
                close();
                return;
            }

            if (contentView.getLeft() < -hideViewWidth / 2) {
                // 打开
                open();
            } else {
                // 关闭
                close();
            }
        }
    };

open(),close()实现

    public void open() {
        open(true);
    }

    public void close() {
        close(true);
    }

    /**
     * 打开的方法
     *
     * @param isSmooth 是否通过缓冲动画的形式设定view的位置
     */
    public void open(boolean isSmooth) {
        if (isSmooth) {
            viewDragHelper.smoothSlideViewTo(contentView, -hideViewWidth,
                    contentView.getTop());
            ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
        } else {
            contentView.offsetLeftAndRight(-hideViewWidth);//直接偏移View的位置
            hideView.offsetLeftAndRight(-hideViewWidth);//直接偏移View的位置
            //            contentView.layout(-hideViewWidth, 0, contentWidth - hideViewWidth, hideViewHeight);//直接通过坐标摆放
            //            hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通过坐标摆放
            invalidate();
        }
    }

    /**
     * 关闭的方法
     *
     * @param isSmooth true:通过缓冲动画的形式设定view的位置
     *                 false:直接设定view的位置
     */
    public void close(boolean isSmooth) {
        if (isSmooth) {
            viewDragHelper.smoothSlideViewTo(contentView, 0, contentView.getTop());
            ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
        } else {
            contentView.offsetLeftAndRight(hideViewWidth);
            hideView.offsetLeftAndRight(hideViewWidth);
            invalidate();
            //contentView.layout(0, 0, contentWidth, hideViewHeight);//直接通过坐标摆放
            //hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通过坐标摆放
        }
    }

此上基本实现了单个SwipeLayout的抽屉滑动效果,但是将此SwipeLayout作为一个item布局设置给一个listView的时候,还需要做许多的判断。
由于listView的重用机制,我们这里并未针对listview做任何处理,所以一旦有一个item的SwipeLayout的状态是打开状态,不可避免的其它也必然有几个是打开状态,所以我们这里需要根据检测listView的滑动,当listView滑动时,关闭SwipeLayout。既然需要在外部控制SwipeLayout的开关,我们先定义一个SwipeLayoutManager用于管理SwipeLayout的控制。

public class SwipeLayoutManager {
    //记录打开的SwipeLayout集合
    private HashSet<SwipeLayout> mUnClosedSwipeLayouts = new HashSet<SwipeLayout>();

    private SwipeLayoutManager() {
    }

    private static SwipeLayoutManager mInstance = new SwipeLayoutManager();

    public static SwipeLayoutManager getInstance() {
        return mInstance;
    }

    /**
     * 将一个没有关闭的SwipeLayout加入集合
     * @param layout
     */
    public void add(SwipeLayout layout) {
        mUnClosedSwipeLayouts.add(layout);
    }

    /**
     * 将一个没有关闭的SwipeLayout移出集合
     * @param layout
     */
    public void remove(SwipeLayout layout){
        mUnClosedSwipeLayouts.remove(layout);
    }

    /**
     * 关闭已经打开的SwipeLayout
     */
    public void closeUnCloseSwipeLayout() {
        if(mUnClosedSwipeLayouts.size() == 0){
            return;
        }

        for(SwipeLayout l : mUnClosedSwipeLayouts){
            l.close(true);
        }
        mUnClosedSwipeLayouts.clear();
    }

    /**
     * 关闭已经打开的SwipeLayout
     */
    public void closeUnCloseSwipeLayout(boolean isSmooth) {
        if(mUnClosedSwipeLayouts.size() == 0){
            return;
        }

        for(SwipeLayout l : mUnClosedSwipeLayouts){
            l.close(isSmooth);
        }
        mUnClosedSwipeLayouts.clear();
    }
}

这样就可以监听listView的滑动,然后在listView滑动的时候,关闭所有的抽屉View。

    listView.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                swipeLayoutManager.closeUnCloseSwipeLayout();
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            }
        });

考虑到大多数时候,在我们打开抽屉View和关闭抽屉View的时候,外部需要知道SwipeLayout的状态值,所以我们需要在SwipeLayout中增加几个接口,告诉外部当前SwipeLayout的状态值:

SwipeLayout.java
----------------
    private void updateSwipeStates() {
        SwipeState lastSwipeState = currentState;
        SwipeState swipeState = getCurrentState();

        if (listener == null) {
            try {
                throw new Exception("please setOnSwipeStateChangeListener first!");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }

        if (swipeState != currentState) {
            currentState = swipeState;
            if (currentState == SwipeState.Open) {
                listener.onOpen(this);
                // 当前的Swipelayout已经打开,需要让Manager记录
                swipeLayoutManager.add(this);
            } else if (currentState == SwipeState.Close) {
                listener.onClose(this);
                // 说明当前的SwipeLayout已经关闭,需要让Manager移除
                swipeLayoutManager.remove(this);
            } else if (currentState == SwipeState.Swiping) {
                if (lastSwipeState == SwipeState.Open) {
                    listener.onStartClose(this);
                } else if (lastSwipeState == SwipeState.Close) {
                    listener.onStartOpen(this);
                    //hideView准备显示之前,先将之前打开的的SwipeLayout全部关闭
                    swipeLayoutManager.closeUnCloseSwipeLayout();
                    swipeLayoutManager.add(this);
                }
            }
        } else {
            currentState = swipeState;
        }
    }

    /**
     * 获取当前控件状态
     *
     * @return
     */
    public SwipeState getCurrentState() {
        int left = contentView.getLeft();
        //        Log.e("tag", "contentView.getLeft() = " + left);
        //        Log.e("tag", "hideViewWidth = " + hideViewWidth);
        if (left == 0) {
            return SwipeState.Close;
        }

        if (left == -hideViewWidth) {
            return SwipeState.Open;
        }
        return SwipeState.Swiping;
    }
    private OnSwipeStateChangeListener listener;

    public void setOnSwipeStateChangeListener(
            OnSwipeStateChangeListener listener) {
        this.listener = listener;
    }

    public View getContentView() {
        return contentView;
    }

    public interface OnSwipeStateChangeListener {
        void onOpen(SwipeLayout swipeLayout);

        void onClose(SwipeLayout swipeLayout);

        void onStartOpen(SwipeLayout swipeLayout);

        void onStartClose(SwipeLayout swipeLayout);
    }

然后接下来是写一个为listView设置的SwipeAdapter

SwipeAdapter.java
------------
public class SwipeAdapter extends BaseAdapter implements OnSwipeStateChangeListener {
    private Context mContext;
    private List<String> list;
    private MyClickListener myClickListener;
    private SwipeLayoutManager swipeLayoutManager;

    public SwipeAdapter(Context mContext) {
        super();
        this.mContext = mContext;
        init();
    }

    private void init() {
        myClickListener = new MyClickListener();
        swipeLayoutManager = SwipeLayoutManager.getInstance();
    }

    public void setList(List<String> list){
        this.list = list;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = UIUtils.inflate(R.layout.list_item_swipe);
        }
        ViewHolder holder = ViewHolder.getHolder(convertView);

        holder.tv_content.setText(list.get(position));
        holder.tv_overhead.setOnClickListener(myClickListener);
        holder.tv_overhead.setTag(position);
        holder.tv_delete.setOnClickListener(myClickListener);
        holder.tv_delete.setTag(position);
        holder.sv_layout.setOnSwipeStateChangeListener(this);
        holder.sv_layout.setTag(position);

        holder.sv_layout.getContentView().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ToastUtils.showToast("item click : " + position);
                swipeLayoutManager.closeUnCloseSwipeLayout();
            }
        });

        return convertView;
    }

    static class ViewHolder {
        TextView tv_content, tv_overhead, tv_delete;
        SwipeLayout sv_layout;

        public ViewHolder(View convertView) {
            tv_content = (TextView) convertView.findViewById(R.id.tv_content);
            tv_overhead = (TextView) convertView.findViewById(R.id.tv_overhead);
            tv_delete = (TextView) convertView.findViewById(R.id.tv_delete);
            sv_layout = (SwipeLayout) convertView.findViewById(R.id.sv_layout);
        }

        public static ViewHolder getHolder(View convertView) {
            ViewHolder holder = (ViewHolder) convertView.getTag();
            if (holder == null) {
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }
            return holder;
        }
    }

    class MyClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            Integer position = (Integer) v.getTag();
            switch (v.getId()) {
                case R.id.tv_overhead:
                    //ToastUtils.showToast("position : " + position + " overhead is clicked.");
                    }
                    break;
                case R.id.tv_delete:
                    //ToastUtils.showToast("position : " + position + " delete is clicked.");
                    }
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void onOpen(SwipeLayout swipeLayout) {
        //ToastUtils.showToast(swipeLayout.getTag() + "onOpen.");
    }

    @Override
    public void onClose(SwipeLayout swipeLayout) {
        //ToastUtils.showToast(swipeLayout.getTag() + "onClose.");
    }

    @Override
    public void onStartOpen(SwipeLayout swipeLayout) {
        //            ToastUtils.showToast("onStartOpen.");
    }

    @Override
    public void onStartClose(SwipeLayout swipeLayout) {
        //            ToastUtils.showToast("onStartClose.");
    }
}

此时已经基本实现了我们需要的大部分功能了,但是当我们滑动的时候,又发现新的问题,我们的SwipeLayout和listview滑动判断有问题。由于前面我们仅仅是将touch拦截事件简简单单的丢给了viewDragHelper.shouldInterceptTouchEvent(ev)来处理,导致SwipeLayout和listview拦截touch事件时的处理存在一定的问题,这里我们要提到一个知识点:Android view事件的传递。
(1)首先由Activity分发,分发给根View,也就是DecorView(DecorView为整个Window界面的最顶层View)
(2)然后由根View分发到子的View
view事件拦截如下图所示:


这里写图片描述

view事件的消费如下图所示:


这里写图片描述

注:以上2张图借鉴网上总结的比较经典的图

所以这里我们就要谈到一开始出现的ContentLayout,主要重写了onInterceptTouchEvent和onTouchEvent。

public class ContentLayout extends LinearLayout {
    SwipeLayoutInterface mISwipeLayout;

    public ContentLayout(Context context) {
        super(context);
    }

    public ContentLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ContentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setSwipeLayout(SwipeLayoutInterface iSwipeLayout) {
        this.mISwipeLayout = iSwipeLayout;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
//        Log.e("ContentLayout", "-----onInterceptTouchEvent-----");
        if (mISwipeLayout.getCurrentState() == SwipeState.Close) {
            return super.onInterceptTouchEvent(ev);
        } else {
            return true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
//        Log.e("ContentLayout", "-----onTouchEvent-----");
        if (mISwipeLayout.getCurrentState() == SwipeState.Close) {
            return super.onTouchEvent(ev);
        } else {
            if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
                mISwipeLayout.close();
            }
            return true;
        }
    }
}

另外由于在ContentLayout中需要拿到父View SwipeLayout的开关状态以及控制SwipeLayout的关闭,因此在再写一个接口,用于ContentLayout获取SwipeLayout的开关状态以及更新SwipeLayout。

public interface SwipeLayoutInterface {

    SwipeState getCurrentState();

    void open();

    void close();
}

然后接着的是完善SwipeLayout的onInterceptTouchEvent,我们在这里增加一个GestureDetectorCompat处理手势识别:

    private void init(Context context) {
        viewDragHelper = ViewDragHelper.create(this, callback);
        mGestureDetector = new GestureDetectorCompat(context, mOnGestureListener);
        swipeLayoutManager = SwipeLayoutManager.getInstance();
    }

    private SimpleOnGestureListener mOnGestureListener = new SimpleOnGestureListener() {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // 当横向移动距离大于等于纵向时,返回true
            return Math.abs(distanceX) >= Math.abs(distanceY);
        }
    };
     @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean result = viewDragHelper.shouldInterceptTouchEvent(ev) & mGestureDetector.onTouchEvent(ev);
        //        Log.e("SwipeLayout", "-----onInterceptTouchEvent-----");
        return result;
    }

如此下来,整个View不管是上下拖动,还是SwipeLayout的开关滑动,都已经实现完成了。最后增加对应overhead,delete以及item的点击事件,此处完善SwipeAdapter的代码之后如下。

    class MyClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            Integer position = (Integer) v.getTag();
            switch (v.getId()) {
                case R.id.tv_overhead:
                    //ToastUtils.showToast("position : " + position + " overhead is clicked.");
                    swipeLayoutManager.closeUnCloseSwipeLayout(false);
                    if(onSwipeControlListener != null){
                        onSwipeControlListener.onOverhead(position, list.get(position));
                    }
                    break;
                case R.id.tv_delete:
                    //ToastUtils.showToast("position : " + position + " delete is clicked.");
                    swipeLayoutManager.closeUnCloseSwipeLayout(false);
                    if(onSwipeControlListener != null){
                        onSwipeControlListener.onDelete(position, list.get(position));
                    }
                    break;
                default:
                    break;
            }
        }
    }
    
    private OnSwipeControlListener onSwipeControlListener;

    public void setOnSwipeControlListener(OnSwipeControlListener onSwipeControlListener){
        this.onSwipeControlListener = onSwipeControlListener;
    }

    /**
     * overhead 和 delete点击事件接口
     */
    public interface OnSwipeControlListener{
        void onOverhead(int position, String itemTitle);

        void onDelete(int position, String itemTitle);
    }

最后贴上MainActivity代码,此处通过OnSwipeControlListener接口回调实现item的删除和置顶:

public class MainActivity extends Activity implements OnSwipeControlListener {
    private ListView listView;
    private List<String> list = new ArrayList<String>();
    private SwipeLayoutManager swipeLayoutManager;
    private SwipeAdapter swipeAdapter;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();
        initView();
    }

    private void initData() {
        for (int i = 0; i < 50; i++) {
            list.add("content - " + i);
        }
    }

    private void initView() {
        swipeLayoutManager = SwipeLayoutManager.getInstance();
        swipeAdapter = new SwipeAdapter(this);
        swipeAdapter.setList(list);

        listView = (ListView) findViewById(R.id.list_view);

        listView.setAdapter(swipeAdapter);
        listView.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                swipeLayoutManager.closeUnCloseSwipeLayout();
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            }
        });

        swipeAdapter.setOnSwipeControlListener(this);
    }

    @Override
    public void onOverhead(int position, String itemTitle) {
        setItemOverhead(position, itemTitle);
    }

    @Override
    public void onDelete(int position, String itemTitle) {
        removeItem(position, itemTitle);
    }

    /**
     * 设置item置顶
     *
     * @param position
     * @param itemTitle
     */
    private void setItemOverhead(int position, String itemTitle) {
        // ToastUtils.showToast("position : " + position + " overhead.");
        ToastUtils.showToast("overhead ---" + itemTitle + "--- success.");
        String newTitle = itemTitle;
        list.remove(position);//删除要置顶的item
        list.add(0, newTitle);//根据adapter传来的Title数据在list 0位置插入title字符串,达到置顶效果
        swipeAdapter.setList(list);//重新给Adapter设置list数据并更新
        UIUtils.runOnUIThread(new Runnable() {
            @Override
            public void run() {
                listView.setSelection(0);//listview选中第0项item
            }
        });
    }

    /**
     * 删除item
     *
     * @param position
     * @param itemTitle
     */

    private void removeItem(int position, String itemTitle) {
        //        ToastUtils.showToast("position : " + position + " delete.");
        ToastUtils.showToast("delete ---" + itemTitle + "--- success.");
        list.remove(position);
        swipeAdapter.setList(list);//重新给Adapter设置list数据并更新
    }
}

至此整个demo基本完成,本次完成的功能基本能够直接放到项目中使用。其实最麻烦的地方就在于view的touch事件拦截和处理,不过将本demo的log打开看一下对比之后,也就能够理解整个传递过程了。

完整demo地址:https://github.com/Horrarndoo/SwipeLayout

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 女儿,时间是一台抛光机,任何人,降生那天,不由分说,时间立即转动锋利的齿轮开始打磨,没商量,没有情愿不情愿,我们都...
    流砂阻水阅读 169评论 2 1
  • 学习分为主动学习和被动学习,这两者有很大的差别。 这两者的区别很好理解。我认为,一个人成长进步的标志是能够主动学习...
    滑铁没有卢阅读 860评论 0 0
  • 警察在办䅁协议调解时,以人情作为砝码,说几百元就当作请我吃饭,如你不同意,据理力争就认为你不讲理,心想我都为你争取...
    香水茉莉阅读 388评论 0 0
  • 俗话说的好:你不理财,财不理你!作为一个小平民,与其让闲钱在银行睡大觉,不如动用点小手段,赚个饭钱也是极好的。这里...
    kingpine阅读 403评论 0 2