public class SlidingLayout extends ViewGroup implements GestureDetector.OnGestureListener {
private static final int HORIZONTAL_SCROLL_TRIGGER_DISTANCE = 150;
private static final int VERTICAL_SCROLL_TRIGGER_DISTANCE = 60;
private View contentView;
private View menuView;
private boolean isMenuOpened;
private boolean isScrolling;
private Scroller scroller;
private int distance;
private GestureDetector gestureDetector;
private int screenWidth;
private int screenHeight;
private int duration;
private boolean isHorizontalScroll;
private float touchDownX;
private float touchDownY;
public SlidingLayout(Context context) {
super(context);
init(context);
}
public SlidingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SlidingLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
scroller = new Scroller(context);
gestureDetector = new GestureDetector(context, this);
isMenuOpened = false;
isScrolling = false;
duration = 300;
distance = 700;
}
public void setDistance(int distance) {
this.distance = distance;
}
public void setScreenWidth(int screenWidth) {
this.screenWidth = screenWidth;
}
public void setScreenHeight(int screenHeight) {
this.screenHeight = screenHeight;
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
super.computeScroll();
}
/**
* When the scroll distance more than HORIZONTAL_SCROLL_TRIGGER_DISTANCE in
* the horizontal direction, the menu view start to scroll.
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Logger.i("sliding layout", "dispatchTouchEvent");
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
touchDownX = ev.getX();
touchDownY = ev.getY();
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
if (Math.abs(touchDownX - ev.getX()) > HORIZONTAL_SCROLL_TRIGGER_DISTANCE
&& Math.abs(touchDownY - ev.getY()) <= VERTICAL_SCROLL_TRIGGER_DISTANCE) {
isHorizontalScroll = true;
}
} else if (ev.getAction() == MotionEvent.ACTION_UP && isScrolling) {
slideMenu();
isHorizontalScroll = false;
}
gestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
Logger.i("sliding layout", "onLayout");
contentView = getChildAt(0);
menuView = getChildAt(1);
// Set content view full screen.
contentView.measure(0, 0);
contentView.layout(0, 0, screenWidth, screenHeight);
}
}
void closeMenu() {
isMenuOpened = false;
scroller.startScroll(getScrollX(), 0, -distance, 0, duration);
invalidate();
}
/**
* Check whether the menu view is opened.
*
* @return true if opened, or false.
*/
public boolean isOpen() {
return isMenuOpened;
}
private void slideMenu() {
if (getScrollX() < distance / 2) {
scroller.startScroll(getScrollX(), 0, -getScrollX(), 0, duration);
isMenuOpened = false;
} else if (getScrollX() > distance / 2) {
scroller.startScroll(getScrollX(), 0, distance - getScrollX(), 0, duration);
isMenuOpened = true;
menuView.setClickable(true);
}
invalidate();
}
/**
* Init menu view.
*/
@Override
public boolean onDown(MotionEvent e) {
menuView.measure(0, 0);
menuView.layout(screenWidth, 0, screenWidth + distance, screenHeight);
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Logger.i("sliding layout", "onScroll: " + getScrollX() + "," + distanceX + ","
+ isMenuOpened);
if (((getScrollX() > 0 && getScrollX() < distance)
|| (getScrollX() == 0 && distanceX >= 0) || (getScrollX() >= distance && distanceX < 0))
&& isHorizontalScroll) {
isScrolling = true;
scrollBy((int) distanceX, 0);
} else if (getScrollX() > distance && isMenuOpened && distanceX > 0) {
isScrolling = false;
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
}