Android ViewDragHelper實現 側滑刪除效果

效果圖

gif

事件傳遞機制

圖片

帶碼

  • SwipeDeleteItem.java 自定義拖拽控件
public class SwipeDeleteItem extends FrameLayout {

    private ViewDragHelper viewDragHelper;
    private View contentView;
    private View deleteView;
    private int contentHeight;
    private int contentWidth;
    private int deleteWidth;
    float downX, downY;//按下的x y
    private float upX;
    private float upY;
    private float moveX;
    private float moveY;
    private float downIX;
    private float downIY;

    enum State {
        close, open
    }

    //默認狀態是關閉
    private State state = State.close;

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

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

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

    private void init() {
        viewDragHelper = ViewDragHelper.create(this, 1.0f, callback);
    }

    /**
     * 此控件的結束標籤讀取完畢
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        contentView = getChildAt(0);
        deleteView = getChildAt(1);
    }

    @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, contentHeight);
        Log.e("", "content" + contentWidth);
        deleteView.layout(contentWidth, 0, contentWidth + deleteWidth, deleteWidth);
    }

    /**
     * onMeasure 已經執行完畢 可以直接獲取孩子寬高
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        contentHeight = contentView.getMeasuredHeight();
        contentWidth = contentView.getMeasuredWidth();
        deleteWidth = deleteView.getMeasuredWidth();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.e("進入","onInterceptTouchEvent");
        boolean value = viewDragHelper.shouldInterceptTouchEvent(event);
        if (!SwipeDeleteManager.getInstance().haveOpened(this)) {
            //如果打開的不是當前的item 關閉
            SwipeDeleteManager.getInstance().close();
        }

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downIX = event.getX();
                downIY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                moveX = event.getX();
                moveY = event.getY();
                //如果 不是點擊事件 攔截事件 這裏判斷dx dy >1就是判斷是否是點擊事件
                if (Math.abs(moveX-downIX)>1||Math.abs(moveY-downIY)>1){
                    Log.e("onInterceptTouchEvent","不是點擊攔截事件");
                    value = true;
                }
                break;
        }
        return value;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("進入","onTouchEvent");
        //如果已經有item打開 不允許父控件攔截事件
        if (SwipeDeleteManager.getInstance().haveOpened(this)) {
            //如果觸摸的是打開的item 讓viewDragHelper處理觸摸事件
            //請求父控件不要攔截事件
            Log.e("onTouchEvent","不允許父控件攔截");
            requestDisallowInterceptTouchEvent(true);
        } else if (SwipeDeleteManager.getInstance().haveOpened()) {
            //如果觸摸的不是當前打開的item 直接消耗事件 不讓item滑動
            requestDisallowInterceptTouchEvent(true);
            return true;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                float dx = Math.abs(moveX - downX);
                float dy = Math.abs(moveY - downY);
                if (dx > dy) {
                    //如果 x距離大於y 則不允許父控件攔截事件
                    Log.e("Touch","dx>dy");
                    requestDisallowInterceptTouchEvent(true);
                }
                downX = moveX;
                downY = moveY;
                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 == deleteView;
        }

        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        @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.layout(deleteView.getLeft() + dx, deleteView.getTop() + dy,
                        deleteView.getRight() + dx, deleteView.getBottom() + dy);
            } else if (changedView == deleteView) {
                contentView.layout(contentView.getLeft() + dx, contentView.getTop() + dy,
                        contentView.getRight() + dx, contentView.getBottom() + dy);
            }

            //如果 item getLeft不等於0 就認爲item已經打開 不允許其它item滑動
            if (contentView.getLeft() != 0)
                SwipeDeleteManager.getInstance().setSwipeDeleteItem(SwipeDeleteItem.this);
            else SwipeDeleteManager.getInstance().clear();

            if (contentView.getLeft() == 0 && state != State.close) state = State.close;
            else if (contentView.getLeft() == -deleteWidth && state != State.open)
                state = State.open;
        }

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

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //限定滑動的範圍
            if (child == contentView) {
                if (left > 0) left = 0;
                if (left < -deleteWidth) left = -deleteWidth;
            } else if (child == deleteView) {
                if (left > contentWidth) left = contentWidth;
                if (left < contentWidth - deleteWidth) left = contentWidth - deleteWidth;
            }
            return left;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            //當速度達到一定的值的時候 直接打開或者關閉
            if (xvel < -2000) {
                open();
                return;
            }
            if (xvel > 2000) {
                close();
                return;
            }
            if (contentView.getLeft() > -deleteWidth / 2) {
                close();
            } else {
                open();
            }
        }
    };

    public void open() {
        Log.e("item","動畫打開");
        viewDragHelper.smoothSlideViewTo(contentView, -deleteWidth, contentView.getTop());
        state = State.open;
        SwipeDeleteManager.getInstance().setSwipeDeleteItem(SwipeDeleteItem.this);
        ViewCompat.postInvalidateOnAnimation(SwipeDeleteItem.this);
    }

    public void close() {
        Log.e("item","動畫關閉");
        viewDragHelper.smoothSlideViewTo(contentView, 0, contentView.getTop());
        ViewCompat.postInvalidateOnAnimation(SwipeDeleteItem.this);
    }

    /**
     * 重寫此方法刷新動畫
     */
    @Override
    public void computeScroll() {
        if (viewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
}
  • SwipeDeleteManager.java 管理控件 用來判斷是否有打開的item 關閉item
public class SwipeDeleteManager {
    private SwipeDeleteManager() {
    }

    private static SwipeDeleteManager swipeDeleteManager = new SwipeDeleteManager();

    public static SwipeDeleteManager getInstance() {
        return swipeDeleteManager;
    }

    private SwipeDeleteItem swipeDeleteItem;

    public void setSwipeDeleteItem(SwipeDeleteItem s) {
        swipeDeleteItem = s;
    }

    public void clear() {
        swipeDeleteItem = null;
    }

    public void close() {
        Log.e("Manager","關閉請求");
        if (swipeDeleteItem != null){
            swipeDeleteItem.close();
            Log.e("Manager","關閉");
        }
    }


    /**
     * 是否有item已經打開
     */
    public boolean haveOpened() {
        return swipeDeleteItem != null;
    }

    /**
     * 是否有item已經打開
     */
    public boolean haveOpened(SwipeDeleteItem s) {
        //如果爲空 表示沒有打開的item
        return swipeDeleteItem != null && swipeDeleteItem == s;
        // true 表示 兩個item不是同一個 有一個已經打開的item
    }
}
  • MainActivity.java
public class MainActivity extends AppCompatActivity {

    private ListView listView;

    private ArrayList<String> dataList = new ArrayList<>();

    private static Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.listView);
        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                    SwipeDeleteManager.getInstance().close();
            }

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

            }
        });
        mContext= this;
        for (int i = 0; i < 10; i++) {
            dataList.add("我是第" + i + "條數據");
        }

        listView.setAdapter(new MyAdapter());

    }



    private class MyAdapter extends BaseAdapter {

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

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

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = View.inflate(MainActivity.this, R.layout.item_swipe_delete, null);
            }
            ViewHolder holder = ViewHolder.getHolder(convertView);
            holder.name.setText(dataList.get(position));
            return convertView;
        }
    }

    static class ViewHolder {
        TextView name, top, delete;

        ViewHolder(View convertView) {
            name = (TextView) convertView.findViewById(R.id.tv_name);
            top = (TextView) convertView.findViewById(R.id.tv_top);
            delete = (TextView) convertView.findViewById(R.id.tv_delete);
            top.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"top",Toast.LENGTH_SHORT).show();
                    SwipeDeleteManager.getInstance().close();
                }
            });
        }

        static ViewHolder getHolder(View convertView) {
            ViewHolder holder = (ViewHolder) convertView.getTag();
            if (holder == null) {
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }
            return holder;
        }
    }
}
  • 佈局文件item_swipe_delete.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <com.reige.swipedeleteitem.SwipeDeleteItem
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                      android:layout_width="match_parent"
                      android:layout_height="80dp"
                      android:background="#11111111"
                      android:gravity="center_vertical"
                      android:orientation="horizontal"
                      android:paddingLeft="15dp">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@mipmap/ic_launcher"/>

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:text="名稱"
                android:textColor="#99000000"
                android:textSize="20sp"/>
        </LinearLayout>

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                      android:layout_width="wrap_content"
                      android:layout_height="80dp"
                      android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_top"
                android:layout_width="100dp"
                android:layout_height="match_parent"
                android:clickable="true"
                android:focusable="true"
                android:background="#aaC7C7CD"
                android:gravity="center"
                android:text="置頂"
                android:textColor="#ffffff"
                android:textSize="18sp"/>

            <TextView
                android:id="@+id/tv_delete"
                android:layout_width="100dp"
                android:layout_height="match_parent"
                android:background="#eeFF3A30"
                android:gravity="center"
                android:text="刪除"
                android:textColor="#ffffff"
                android:textSize="18sp"/>

        </LinearLayout>


    </com.reige.swipedeleteitem.SwipeDeleteItem>
</LinearLayout>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章