`

迷你轻量级全方向完美滑动处理侧滑控件SlideLayout

 
阅读更多

纯手工超级迷你轻量级全方向完美滑动处理侧滑控件(比官方 support v4 包 SlidingPaneLayout 控件更加 Q 迷你,累计代码不足 300 行),支持上下左右有各种侧拉,可配置侧拉松手临界距离,支持单独使用、ListView、GridView、RecycleView、ScrollView、ViewPager 等各种嵌套(作为 item 使用或者作为以上所有控件的父容器使用),具体不同配置展示效果如下图。

like SlidingPaneLayout, all direction support.
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * like SlidingPaneLayout, all direction support.
 */
public class SlideLayout extends ViewGroup {
    public static final int STATE_CLOSE = 0;
    public static final int STATE_SLIDING = 1;
    public static final int STATE_OPEN = 2;

    private static final int SLIDE_RIGHT = 0;
    private static final int SLIDE_LEFT = 1;
    private static final int SLIDE_TOP = 2;
    private static final int SLIDE_BOTTOM = 3;

    private View mContentView;
    private View mSlideView;

    private Scroller mScroller;

    private int mLastX = 0;
    private int mLastY = 0;

    private int mSlideCriticalValue = 0;
    private boolean mIsScrolling = false;
    private int mSlideDirection;

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

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

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

    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SlideLayout);
        mSlideDirection = typedArray.getInt(R.styleable.SlideLayout_slideDirection, SLIDE_RIGHT);
        mSlideCriticalValue = typedArray.getDimensionPixelSize(R.styleable.SlideLayout_slideCriticalValue, 0);
        typedArray.recycle();

        mScroller = new Scroller(context);
    }

    public int getSlideState() {
        int retValue = STATE_CLOSE;
        if (mIsScrolling) {
            retValue = STATE_SLIDING;
        } else {
            int scrollOffset = (mSlideDirection == SLIDE_LEFT || mSlideDirection == SLIDE_RIGHT) ?
                                getScrollX() : getScrollY();
            retValue = (scrollOffset == 0) ? STATE_CLOSE : STATE_OPEN;
        }
        return retValue;
    }

    public void smoothCloseSlide() {
        smoothScrollTo(0, 0);
    }

    public void smoothOpenSlide() {
        switch (mSlideDirection) {
            case SLIDE_RIGHT:
                smoothScrollTo(mSlideView.getMeasuredWidth(), 0);
                break;
            case SLIDE_LEFT:
                smoothScrollTo(-mSlideView.getMeasuredWidth(), 0);
                break;
            case SLIDE_TOP:
                smoothScrollTo(0, -mSlideView.getMeasuredHeight());
                break;
            case SLIDE_BOTTOM:
                smoothScrollTo(0, mSlideView.getMeasuredHeight());
                break;
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalArgumentException("SlideLayout only need contains two child (content and slide).");
        }

        mContentView = getChildAt(0);
        mSlideView = getChildAt(1);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mContentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
        switch (mSlideDirection) {
            case SLIDE_LEFT:
                mSlideView.layout(-mSlideView.getMeasuredWidth(), 0, 0, getMeasuredHeight());
                break;
            case SLIDE_RIGHT:
                mSlideView.layout(getMeasuredWidth(), 0,
                        mSlideView.getMeasuredWidth() + getMeasuredWidth(), getMeasuredHeight());
                break;
            case SLIDE_TOP:
                mSlideView.layout(0, -mSlideView.getMeasuredHeight(), getMeasuredWidth(), 0);
                break;
            case SLIDE_BOTTOM:
                mSlideView.layout(0, getMeasuredHeight(),
                        getMeasuredWidth(), mSlideView.getMeasuredHeight() + getMeasuredHeight());
                break;
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return mIsScrolling || super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int eventX = (int) event.getX();
        int eventY = (int) event.getY();
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                mIsScrolling = false;
                //Maybe child not set OnClickListener, so ACTION_DOWN need to return true and use super.
                super.dispatchTouchEvent(event);
                return true;
            case MotionEvent.ACTION_MOVE:
                int offsetX = eventX - mLastX;
                int offsetY = eventY - mLastY;
                int directionMoveOffset = 0;
                if (mSlideDirection == SLIDE_LEFT || mSlideDirection == SLIDE_RIGHT) {
                    directionMoveOffset = Math.abs(offsetX) - Math.abs(offsetY);
                } else {
                    directionMoveOffset = Math.abs(offsetY) - Math.abs(offsetX);
                }
                if (!mIsScrolling && directionMoveOffset < ViewConfiguration.getTouchSlop()) {
                    break;
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                mIsScrolling = true;
                int newScrollX = 0;
                int newScrollY = 0;
                switch (mSlideDirection) {
                    case SLIDE_RIGHT:
                        newScrollX = scrollX - offsetX;
                        if (newScrollX < 0) {
                            newScrollX = 0;
                        } else if (newScrollX > mSlideView.getMeasuredWidth()) {
                            newScrollX = mSlideView.getMeasuredWidth();
                        }
                        break;
                    case SLIDE_LEFT:
                        newScrollX = scrollX - offsetX;
                        if (newScrollX < -mSlideView.getMeasuredWidth()) {
                            newScrollX = -mSlideView.getMeasuredWidth();
                        } else if (newScrollX > 0) {
                            newScrollX = 0;
                        }
                        break;
                    case SLIDE_TOP:
                        newScrollY = scrollY - offsetY;
                        if (newScrollY < -mSlideView.getMeasuredHeight()) {
                            newScrollY = -mSlideView.getMeasuredHeight();
                        } else if (newScrollY > 0) {
                            newScrollY = 0;
                        }
                        break;
                    case SLIDE_BOTTOM:
                        newScrollY = scrollY - offsetY;
                        if (newScrollY < 0) {
                            newScrollY = 0;
                        } else if (newScrollY > mSlideView.getMeasuredHeight()) {
                            newScrollY = mSlideView.getMeasuredHeight();
                        }
                        break;
                }
                scrollTo(newScrollX, newScrollY);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsScrolling = false;
                getParent().requestDisallowInterceptTouchEvent(false);
                int finalScrollX = 0;
                int finalScrollY = 0;
                switch (mSlideDirection) {
                    case SLIDE_RIGHT:
                        if (scrollX > getSlideCriticalValue()) {
                            finalScrollX = mSlideView.getMeasuredWidth();
                        }
                        break;
                    case SLIDE_LEFT:
                        if (scrollX < -getSlideCriticalValue()) {
                            finalScrollX = -mSlideView.getMeasuredWidth();
                        }
                        break;
                    case SLIDE_TOP:
                        if (scrollY < -getSlideCriticalValue()) {
                            finalScrollY = -mSlideView.getMeasuredHeight();
                        }
                        break;
                    case SLIDE_BOTTOM:
                        if (scrollY > getSlideCriticalValue()) {
                            finalScrollY = mSlideView.getMeasuredHeight();
                        }
                        break;
                }
                smoothScrollTo(finalScrollX, finalScrollY);
                break;
        }

        mLastX = eventX;
        mLastY = eventY;
        return super.dispatchTouchEvent(event);
    }

    //TODO  when mSlideCriticalValue != 0, slide critical need fix.
    private int getSlideCriticalValue() {
        if (mSlideDirection == SLIDE_LEFT || mSlideDirection == SLIDE_RIGHT) {
            if (mSlideCriticalValue == 0) {
                mSlideCriticalValue = mSlideView.getMeasuredWidth() / 2;
            }
        } else {
            if (mSlideCriticalValue == 0) {
                mSlideCriticalValue = mSlideView.getMeasuredHeight() / 2;
            }
        }
        return mSlideCriticalValue;
    }

    private void smoothScrollTo(int destX, int destY) {
        int scrollX = getScrollX();
        int deltaX = destX - scrollX;
        int scrollY = getScrollY();
        int deltaY = destY - scrollY;
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY,
                (int) (Math.abs(Math.sqrt(deltaX*deltaX + deltaY*deltaY)) * 3));
        postInvalidate();
    }

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


自定义属性:
<declare-styleable name="SlideLayout">
        <attr name="slideDirection">
            <enum name="fromRight" value="0"/>
            <enum name="fromLeft" value="1"/>
            <enum name="fromTop" value="2"/>
            <enum name="fromBottom" value="3"/>
        </attr>

        <attr name="slideCriticalValue" format="dimension"/>
    </declare-styleable>


like SlidingPaneLayout, but this used to mini lib and only support right slide.
used to no support v4 import.

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
 * like SlidingPaneLayout, but this used to mini lib and only support right slide.
 * used to no support v4 import.
 */
public class MiniSlideRightLayout extends LinearLayout {
    public static final int STATE_CLOSE = 0;
    public static final int STATE_SLIDING = 1;
    public static final int STATE_OPEN = 2;

    private View mContentView;
    private View mSlideView;

    private Scroller mScroller;

    private int mLastX = 0;
    private int mLastY = 0;

    private int mSlideSensitiveWidth = 0;

    private boolean mIsScrolling = false;

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

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

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

    private void init(Context context, AttributeSet attrs) {
        setOrientation(HORIZONTAL);
        mScroller = new Scroller(context);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalArgumentException("SlideLayout only need contains two child (content and slide).");
        }

        mContentView = getChildAt(0);
        mSlideView = getChildAt(1);
    }

    public int getSlideState() {
        int retValue = STATE_CLOSE;
        if (mIsScrolling) {
            retValue = STATE_SLIDING;
        } else {
            retValue = (getScrollX() == 0) ? STATE_CLOSE : STATE_OPEN;
        }
        return retValue;
    }

    public void smoothCloseSlide() {
        smoothScrollTo(0, 0);
    }

    public void smoothOpenSlide() {
        smoothScrollTo(mSlideView.getMeasuredWidth(), 0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return mIsScrolling || super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int eventX = (int) event.getX();
        int eventY = (int) event.getY();
        int scrollX = getScrollX();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                mIsScrolling = false;
                //Maybe child not set OnClickListener, so ACTION_DOWN need to return true and use super.
                super.dispatchTouchEvent(event);
                return true;
            case MotionEvent.ACTION_MOVE:
                int offsetX = eventX - mLastX;
                int offsetY = eventY - mLastY;
                if (Math.abs(offsetX) - Math.abs(offsetY) < 1) {
                    break;
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                mIsScrolling = true;
                int newScrollX = scrollX - offsetX;
                if (newScrollX < 0) {
                    newScrollX = 0;
                } else if (newScrollX > mSlideView.getMeasuredWidth()) {
                    newScrollX = mSlideView.getMeasuredWidth();
                }
                scrollTo(newScrollX, 0);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsScrolling = false;
                getParent().requestDisallowInterceptTouchEvent(false);
                int finalScrollX = 0;
                mSlideSensitiveWidth = mSlideView.getMeasuredWidth() / 2;
                if (scrollX > mSlideSensitiveWidth) {
                    finalScrollX = mSlideView.getMeasuredWidth();
                }
                smoothScrollTo(finalScrollX, 0);
                break;
        }

        mLastX = eventX;
        mLastY = eventY;
        return super.dispatchTouchEvent(event);
    }

    private void smoothScrollTo(int destX, int destY) {
        int scrollX = getScrollX();
        int deltaX = destX - scrollX;
        int scrollY = getScrollY();
        int deltaY = destY - scrollY;
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY,
                (int) (Math.abs(Math.sqrt(deltaX*deltaX + deltaY*deltaY)) * 3));
        postInvalidate();
    }

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


https://github.com/yanbober/SlideLayout
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics