迷你轻量级全方向完美滑动处理侧滑控件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();
}
}
}


[url]https://github.com/yanbober/SlideLayout[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值