Android自定義控件:類QQ抽屜效果

其實網(wǎng)上類似的實現(xiàn)已經(jīng)很多了,原理也并不難炉爆,只是網(wǎng)上各種demo運行下來堕虹,多少都有一些問題。折騰了半天芬首,決定自己實現(xiàn)一個赴捞。
首先我們看看實現(xiàn)效果:


這里寫圖片描述

對比網(wǎng)上各類demo,這次要實現(xiàn)的主要表現(xiàn)在以下幾點:
1.側(cè)滑顯示抽屜view
2.側(cè)滑抽屜隱藏view控件點擊事件
3.單擊任意item隱藏顯示的抽屜view
4.滑動list隱藏顯示的抽屜view
5.增加SwipeLayout點擊事件和Swipe touch事件判斷處理
6.優(yōu)化快速劃開多個抽屜隱藏view時多個SwipeLayout滑動狀態(tài)判斷處理郁稍,僅顯示最后一個滑動的抽屜隱藏view赦政,隱藏前面所有打開的抽屜view(快速滑動時,可能存在多個抽屜view打開情況耀怜,網(wǎng)上找的幾個demo主要問題都集中在這一塊)

實現(xiàn)原理

其實單就一個SwipeLayout的實現(xiàn)原理來講的話恢着,還是很簡單的,實際上單個SwipeLayout隱藏抽屜狀態(tài)時封寞,應(yīng)該是這樣的:


這里寫圖片描述

也就是說然评,最初的隱藏狀態(tài),實際上是將hide view區(qū)域layout到conten view的右邊狈究,達(dá)到隱藏效果,而后顯示則是根據(jù)拖拽的x值變化來動態(tài)的layout 2個view盏求,從而達(dá)到一個滑動抽屜效果抖锥。
當(dāng)然,直接重寫view的onTouchEvent來動態(tài)的layout 2個view是可以實現(xiàn)我們需要的效果的碎罚,但是有更好的方法來實現(xiàn)磅废,就是同過ViewDragHelper。
ViewDragHelper是google官方提供的一個專門用于手勢分析處理的類荆烈,關(guān)于ViewDragHelper的基本使用拯勉,網(wǎng)上有一大堆的資源竟趾。具體的ViewDragHelper介紹以及基本使用方法,本文就不重復(fù)造輪子了宫峦,此處推薦鴻洋大神的一篇微博:http://blog.csdn.net/lmj623565791/article/details/46858663岔帽。

具體實現(xiàn)

下面我們開始具體的實現(xiàn)。
布局比較簡單导绷,這里就不貼代碼了犀勒,最后會貼上本demo的完整代碼地址。

首先我們實現(xiàn)一個繼承FrameLayout的自定義SwipeLauout妥曲,重寫onFinishInflate方法:
這里我們只允許SwipeLayout設(shè)置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;
    }

然后是比較關(guān)鍵的箱玷,重寫onTouchEvent方法以及ViewDragHelper.Callback回調(diào),我們定了一個enum來判斷SwipeLayout的三種狀態(tài)陌宿。在onViewPositionChanged中锡足,有2種方法實現(xiàn)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)) {
                    // 表示移動是偏向于水平方向爽蝴,那么應(yīng)該SwipeLayout應(yīng)該處理沐批,請求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設(shè)置給contentView
                hideView.offsetLeftAndRight(dx);
            } else if (changedView == hideView) {
                // 如果手指滑動contentView九孩,那么也要講橫向變化量dx設(shè)置給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);
            //            }
            //實時更新當(dāng)前狀態(tài)
            updateSwipeStates();
            invalidate();
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            //根據(jù)用戶滑動速度處理開關(guān)
            //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 {
                // 關(guān)閉
                close();
            }
        }
    };

open(),close()實現(xiàn)

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

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

    /**
     * 打開的方法
     *
     * @param isSmooth 是否通過緩沖動畫的形式設(shè)定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);//直接通過坐標(biāo)擺放
            //            hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通過坐標(biāo)擺放
            invalidate();
        }
    }

    /**
     * 關(guān)閉的方法
     *
     * @param isSmooth true:通過緩沖動畫的形式設(shè)定view的位置
     *                 false:直接設(shè)定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);//直接通過坐標(biāo)擺放
            //hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通過坐標(biāo)擺放
        }
    }

此上基本實現(xiàn)了單個SwipeLayout的抽屜滑動效果发框,但是將此SwipeLayout作為一個item布局設(shè)置給一個listView的時候躺彬,還需要做許多的判斷。
由于listView的重用機制梅惯,我們這里并未針對listview做任何處理宪拥,所以一旦有一個item的SwipeLayout的狀態(tài)是打開狀態(tài),不可避免的其它也必然有幾個是打開狀態(tài)铣减,所以我們這里需要根據(jù)檢測listView的滑動她君,當(dāng)listView滑動時,關(guān)閉SwipeLayout葫哗。既然需要在外部控制SwipeLayout的開關(guān)缔刹,我們先定義一個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;
    }

    /**
     * 將一個沒有關(guān)閉的SwipeLayout加入集合
     * @param layout
     */
    public void add(SwipeLayout layout) {
        mUnClosedSwipeLayouts.add(layout);
    }

    /**
     * 將一個沒有關(guān)閉的SwipeLayout移出集合
     * @param layout
     */
    public void remove(SwipeLayout layout){
        mUnClosedSwipeLayouts.remove(layout);
    }

    /**
     * 關(guān)閉已經(jīng)打開的SwipeLayout
     */
    public void closeUnCloseSwipeLayout() {
        if(mUnClosedSwipeLayouts.size() == 0){
            return;
        }

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

    /**
     * 關(guān)閉已經(jīng)打開的SwipeLayout
     */
    public void closeUnCloseSwipeLayout(boolean isSmooth) {
        if(mUnClosedSwipeLayouts.size() == 0){
            return;
        }

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

這樣就可以監(jiān)聽listView的滑動,然后在listView滑動的時候校镐,關(guān)閉所有的抽屜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) {
            }
        });

考慮到大多數(shù)時候,在我們打開抽屜View和關(guān)閉抽屜View的時候灭翔,外部需要知道SwipeLayout的狀態(tài)值魏烫,所以我們需要在SwipeLayout中增加幾個接口,告訴外部當(dāng)前SwipeLayout的狀態(tài)值:

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);
                // 當(dāng)前的Swipelayout已經(jīng)打開肝箱,需要讓Manager記錄
                swipeLayoutManager.add(this);
            } else if (currentState == SwipeState.Close) {
                listener.onClose(this);
                // 說明當(dāng)前的SwipeLayout已經(jīng)關(guān)閉哄褒,需要讓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準(zhǔn)備顯示之前,先將之前打開的的SwipeLayout全部關(guān)閉
                    swipeLayoutManager.closeUnCloseSwipeLayout();
                    swipeLayoutManager.add(this);
                }
            }
        } else {
            currentState = swipeState;
        }
    }

    /**
     * 獲取當(dāng)前控件狀態(tài)
     *
     * @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設(shè)置的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.");
    }
}

此時已經(jīng)基本實現(xiàn)了我們需要的大部分功能了煌张,但是當(dāng)我們滑動的時候呐赡,又發(fā)現(xiàn)新的問題,我們的SwipeLayout和listview滑動判斷有問題骏融。由于前面我們僅僅是將touch攔截事件簡簡單單的丟給了viewDragHelper.shouldInterceptTouchEvent(ev)來處理链嘀,導(dǎo)致SwipeLayout和listview攔截touch事件時的處理存在一定的問題,這里我們要提到一個知識點:Android view事件的傳遞档玻。
(1)首先由Activity分發(fā)怀泊,分發(fā)給根View谭贪,也就是DecorView(DecorView為整個Window界面的最頂層View)
(2)然后由根View分發(fā)到子的View
view事件攔截如下圖所示:


這里寫圖片描述

view事件的消費如下圖所示:


這里寫圖片描述

注:以上2張圖借鑒網(wǎng)上總結(jié)的比較經(jīng)典的圖

所以這里我們就要談到一開始出現(xiàn)的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的開關(guān)狀態(tài)以及控制SwipeLayout的關(guān)閉蟹倾,因此在再寫一個接口,用于ContentLayout獲取SwipeLayout的開關(guān)狀態(tài)以及更新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) {
            // 當(dāng)橫向移動距離大于等于縱向時饮亏,返回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的開關(guān)滑動,都已經(jīng)實現(xiàn)完成了楼雹。最后增加對應(yīng)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接口回調(diào)實現(xiàn)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);
    }

    /**
     * 設(shè)置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);//刪除要置頂?shù)膇tem
        list.add(0, newTitle);//根據(jù)adapter傳來的Title數(shù)據(jù)在list 0位置插入title字符串,達(dá)到置頂效果
        swipeAdapter.setList(list);//重新給Adapter設(shè)置list數(shù)據(jù)并更新
        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設(shè)置list數(shù)據(jù)并更新
    }
}

至此整個demo基本完成携悯,本次完成的功能基本能夠直接放到項目中使用。其實最麻煩的地方就在于view的touch事件攔截和處理筷笨,不過將本demo的log打開看一下對比之后憔鬼,也就能夠理解整個傳遞過程了龟劲。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市轴或,隨后出現(xiàn)的幾起案子昌跌,更是在濱河造成了極大的恐慌,老刑警劉巖照雁,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚕愤,死亡現(xiàn)場離奇詭異,居然都是意外死亡饺蚊,警方通過查閱死者的電腦和手機萍诱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來污呼,“玉大人裕坊,你說我怎么就攤上這事⊙嗫幔” “怎么了籍凝?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長苗缩。 經(jīng)常有香客問我饵蒂,道長,這世上最難降的妖魔是什么酱讶? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任退盯,我火速辦了婚禮,結(jié)果婚禮上浴麻,老公的妹妹穿的比我還像新娘得问。我一直安慰自己,他們只是感情好软免,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布宫纬。 她就那樣靜靜地躺著,像睡著了一般膏萧。 火紅的嫁衣襯著肌膚如雪漓骚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天榛泛,我揣著相機與錄音蝌蹂,去河邊找鬼。 笑死曹锨,一個胖子當(dāng)著我的面吹牛孤个,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沛简,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼齐鲤,長吁一口氣:“原來是場噩夢啊……” “哼斥废!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起给郊,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤牡肉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后淆九,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體统锤,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年炭庙,在試婚紗的時候發(fā)現(xiàn)自己被綠了饲窿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡煤搜,死狀恐怖免绿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情擦盾,我是刑警寧澤嘲驾,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站迹卢,受9級特大地震影響辽故,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腐碱,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一誊垢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧症见,春花似錦喂走、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至遵蚜,卻和暖如春帖池,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吭净。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工睡汹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寂殉。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓囚巴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子文兢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,117評論 25 707
  • 女兒晤斩,時間是一臺拋光機焕檬,任何人姆坚,降生那天,不由分說实愚,時間立即轉(zhuǎn)動鋒利的齒輪開始打磨兼呵,沒商量,沒有情愿不情愿腊敲,我們都...
    流砂阻水閱讀 192評論 2 1
  • 學(xué)習(xí)分為主動學(xué)習(xí)和被動學(xué)習(xí)击喂,這兩者有很大的差別。 這兩者的區(qū)別很好理解碰辅。我認(rèn)為懂昂,一個人成長進步的標(biāo)志是能夠主動學(xué)習(xí)...
    滑鐵沒有盧閱讀 920評論 0 0
  • 警察在辦?協(xié)議調(diào)解時,以人情作為砝碼没宾,說幾百元就當(dāng)作請我吃飯凌彬,如你不同意,據(jù)理力爭就認(rèn)為你不講理循衰,心想我都為你爭取...
    香水茉莉閱讀 432評論 0 0
  • 俗話說的好:你不理財铲敛,財不理你!作為一個小平民会钝,與其讓閑錢在銀行睡大覺伐蒋,不如動用點小手段,賺個飯錢也是極好的迁酸。這里...
    kingpine閱讀 425評論 0 2