最简单的仿QQ滑动删除控件

滑动控件的编写,首先你需要清楚地了解你需要设计的这个控件具体实现怎样功能,控件是如何操作的。

既然是模仿QQ滑动删除组件,那么你最好在动手写代码之前,先反复操作一下QQ中的滑动组件并对其做清晰的认知。

我来描述一下我是如何分析的:

1、这是一个组控件

    所以我们需要继承ViewGroup基类,并且需要实现 onMeasure、onLayout方法。

2、这是一个需要实现滑动效果的控件

   滑动控件,我们一般要用到scrollTo()、scrollBy()方法。

   在多次尝试过程中,我发现当控件滑动到 “置顶” 和 “删除” 之间有一个特殊情况,当滑动距离刚刚超过 “置顶” 时,将手抬起,控件会自动将 “置顶” 和 “删除” 显示完全,当滑动的距离不超过 “置顶”控件的大小时,如果你抬起手,控件将恢复原状。所以我们要知道,我们需要在代码中我们需要注意这个临界点,自己设置。

    其次,控件实现了两种滑动:惯性滑动(非拖动滑动,即当触摸终止时,控件仍能自己滑动的滑动形式)以及拖动滑动,拖动滑动我们需要用到上面提到的scrollTo(x,y)、scrollBy(x,y).而惯性滑动我们需要用到 帮助类 Scroller(Scroller并不实现滑动,只是用于 计算滑动过程)。

在使用之前最好熟悉一下一些方法,参考博客:https://blog.csdn.net/bigconvience/article/details/26697645

我下面做一下简述:

getScrollX()   获取当前控件在X轴方向的滑动距离相对距离:向左为正  向右为负,与触摸事件中的相对位移相反(当相对位移为负时,说明向左移动,当相对位移为正是,说明向右移动)

getScrollY()  获取当前控件在Y轴方向的滑动距离相对距离:向上为正  向下为负,与触摸事件中的相对位移相反(当相对位移为负时,说明向上移动,当相对位移为正是,说明向下移动)

scrollTo(x,y)   X 为正时,表示向左滑动,为负时,向右滑动。Y为正时,表示向上滑动,为负时,向下滑动。 滑动距离为Math.abs(X) 或 Math.abs(X),以为滑动之前为基础

scrollBy(x,y)    相对位移,以上一次滑动为基础。

直接上代码 :

SwipeLayout.java

QQSwipeActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.first.hdz.customview.R;

public class QQSwipeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qqswipe);
    }


    public void onTop(View view) {
        Toast.makeText(this, "置顶", Toast.LENGTH_SHORT).show();
    }

    public void onDelete(View view) {
        Toast.makeText(this, "删除", Toast.LENGTH_SHORT).show();
    }
}

    

activity_qqswipe.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".swipelayout.QQSwipeActivity">

    <com.first.hdz.customview.swipelayout.SwipeLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#ffffff00"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="您好"
                android:textColor="#ff000000"
                android:textSize="20sp" />

        </LinearLayout>

        <TextView
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:background="#ff00ff00"
            android:gravity="center"
            android:onClick="onTop"
            android:text="置顶"
            android:textColor="#ffffffff"
            android:textSize="20sp" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:background="#ffff0000"
            android:gravity="center"
            android:onClick="onDelete"
            android:text="删除"
            android:textColor="#ffffffff"
            android:textSize="20sp" />

    </com.first.hdz.customview.swipelayout.SwipeLayout>

</android.support.constraint.ConstraintLayout>
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;


public class SwipeLayout extends ViewGroup {
    private Scroller mScroller;

    private int mLastX;

    //用于决定是否将 SwipeLayout 退回的标志距离,即滑动第二个子空间的长度
    private int FlagWidthBack = 0;
    private int FlagWidthIntercept = 0;

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

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

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

    private void init(Context context) {
        mScroller = new Scroller(context);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            this.postInvalidate();
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int realWidth = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            if (i == 0) {
                FlagWidthBack = 0;
                FlagWidthIntercept = 0;
            }
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            if (i == 1) {
                FlagWidthBack = child.getMeasuredWidth();
            }
            if (i != 0) {
                FlagWidthIntercept += child.getMeasuredWidth();
            }
            realWidth += child.getMeasuredWidth();
        }
        setMeasuredDimension(realWidth, height);
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        int top = 0;
        int left = 0;
        int paddingTop = getPaddingTop();
        int paddingLeft = getPaddingLeft();
        top += paddingTop;
        left += paddingLeft;
        int childCount = getChildCount();
        for (int index = 0; index < childCount; index++) {
            View child = getChildAt(index);
            child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
            left += child.getMeasuredWidth();
        }
        scrollTo(0, 0);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(ev);
    }

    /**
     * 事件拦截
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            return false;
        } else {
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    mLastX = (int) ev.getRawX();
                    return false;
                case MotionEvent.ACTION_MOVE:
                    int offset = (int) ev.getRawX() - mLastX;
                    int scrollX = getScrollX();
                    if (offset < 0 && scrollX > FlagWidthIntercept) {

                        return true;
                    }
                    if (offset > 0 && scrollX <= 0) {
                        return true;
                    }
            }
            return false;
        }
    }

    /**
     * 事件消费
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = (int) event.getRawX();
                break;
            case MotionEvent.ACTION_UP: {
                int scrollX = getScrollX();
                if (scrollX > FlagWidthIntercept) {
                    mScroller.startScroll(getScrollX(), getScrollY(), FlagWidthIntercept - getScrollX(), 0);
                } else if (scrollX <= FlagWidthIntercept && scrollX >= FlagWidthBack) {
                    mScroller.startScroll(getScrollX(), getScrollY(), FlagWidthIntercept - getScrollX(), 0);
                } else if (scrollX < FlagWidthBack && scrollX > 0) {
                    mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), 0);
                }
                invalidate();
            }
            break;
            case MotionEvent.ACTION_MOVE: {
                int currentX = (int) event.getRawX();
                int scrollX = getScrollX();
                int offsetX = currentX - mLastX;
                if (scrollX == FlagWidthIntercept) {
                    // 左
                    if (offsetX <= 0) {
                        scrollTo(FlagWidthIntercept, 0);
                    } else {
                        scrollBy(-offsetX, 0);
                    }
                } else if (scrollX < FlagWidthIntercept) {
                    if (offsetX <= 0) {
                        if (scrollX + Math.abs(offsetX) <= FlagWidthIntercept) {
                            scrollBy(-offsetX, 0);
                        } else {
                            scrollTo(FlagWidthIntercept, 0);
                        }
                    } else {
                        if (scrollX - Math.abs(offsetX) <= 0) {
                            scrollTo(0, 0);
                        } else {
                            scrollBy(-offsetX, 0);
                        }
                    }
                }
                invalidate();
                mLastX = currentX;
            }
            break;
            case MotionEvent.ACTION_CANCEL:
                scrollTo(0, 0);
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35920289/article/details/81458429