Java tutorial
/** * The MIT License (MIT) * <p/> * Copyright (c) 2016 Chau Thai * <p/> * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * <p/> * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * <p/> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.center.mycode.view.swipelayout; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @SuppressLint("RtlHardcoded") public class SwipeRevealLayout extends ViewGroup { // These states are used only for ViewBindHelper public static final int STATE_CLOSE = 0; public static final int STATE_CLOSING = 1; public static final int STATE_OPEN = 2; public static final int STATE_OPENING = 3; public static final int STATE_DRAGGING = 4; private static final int DEFAULT_MIN_FLING_VELOCITY = 300; // dp per second public static final int DRAG_EDGE_LEFT = 0x1; public static final int DRAG_EDGE_RIGHT = 0x1 << 1; public static final int DRAG_EDGE_TOP = 0x1 << 2; public static final int DRAG_EDGE_BOTTOM = 0x1 << 3; /** * The secondary view will be under the main view. */ public static final int MODE_NORMAL = 0; /** * The secondary view will stick the edge of the main view. */ public static final int MODE_SAME_LEVEL = 1; /** * Main view is the view which is shown when the layout is closed. */ private View mMainView; /** * Secondary view is the view which is shown when the layout is opened. */ private View mSecondaryView; /** * The rectangle position of the main view when the layout is closed. */ private Rect mRectMainClose = new Rect(); /** * The rectangle position of the main view when the layout is opened. */ private Rect mRectMainOpen = new Rect(); /** * The rectangle position of the secondary view when the layout is closed. */ private Rect mRectSecClose = new Rect(); /** * The rectangle position of the secondary view when the layout is opened. */ private Rect mRectSecOpen = new Rect(); private boolean mIsOpenBeforeInit = false; private volatile boolean mAborted = false; private volatile boolean mIsScrolling = false; private volatile boolean mLockDrag = false; private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY; private int mState = STATE_CLOSE; private int mMode = MODE_NORMAL; private int mLastMainLeft = 0; private int mLastMainTop = 0; private int mDragEdge = DRAG_EDGE_LEFT; private ViewDragHelper mDragHelper; private GestureDetectorCompat mGestureDetector; private DragStateChangeListener mDragStateChangeListener; // only used for ViewBindHelper private SwipeListener mSwipeListener; interface DragStateChangeListener { void onDragStateChanged(int state); } /** * Listener for monitoring events about swipe layout. */ public interface SwipeListener { /** * Called when the main view becomes completely closed. */ void onClosed(SwipeRevealLayout view); /** * Called when the main view becomes completely opened. */ void onOpened(SwipeRevealLayout view); /** * Called when the main view's position changes. * * @param slideOffset The new offset of the main view within its range, from 0-1 */ void onSlide(SwipeRevealLayout view, float slideOffset); } /** * No-op stub for {@link SwipeListener}. If you only want ot implement a subset * of the listener methods, you can extend this instead of implement the full interface. */ public static class SimpleSwipeListener implements SwipeListener { @Override public void onClosed(SwipeRevealLayout view) { } @Override public void onOpened(SwipeRevealLayout view) { } @Override public void onSlide(SwipeRevealLayout view, float slideOffset) { } } public SwipeRevealLayout(Context context) { super(context); init(context, null); } public SwipeRevealLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public SwipeRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); mDragHelper.processTouchEvent(event); return true; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { mDragHelper.processTouchEvent(ev); mGestureDetector.onTouchEvent(ev); boolean settling = mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING; boolean idleAfterScrolled = mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE && mIsScrolling; return settling || idleAfterScrolled; } @Override protected void onFinishInflate() { super.onFinishInflate(); // get views if (getChildCount() >= 2) { mSecondaryView = getChildAt(0); mMainView = getChildAt(1); } else if (getChildCount() == 1) { mMainView = getChildAt(0); } } /** * {@inheritDoc} */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAborted = false; for (int index = 0; index < getChildCount(); index++) { final View child = getChildAt(index); int left, right, top, bottom; left = right = top = bottom = 0; final int minLeft = getPaddingLeft(); final int maxRight = Math.max(r - getPaddingRight() - l, 0); final int minTop = getPaddingTop(); final int maxBottom = Math.max(b - getPaddingBottom() - t, 0); switch (mDragEdge) { case DRAG_EDGE_RIGHT: left = Math.max(r - child.getMeasuredWidth() - getPaddingRight() - l, minLeft); top = Math.min(getPaddingTop(), maxBottom); right = Math.max(r - getPaddingRight() - l, minLeft); bottom = Math.min(child.getMeasuredHeight() + getPaddingTop(), maxBottom); break; case DRAG_EDGE_LEFT: left = Math.min(getPaddingLeft(), maxRight); top = Math.min(getPaddingTop(), maxBottom); right = Math.min(child.getMeasuredWidth() + getPaddingLeft(), maxRight); bottom = Math.min(child.getMeasuredHeight() + getPaddingTop(), maxBottom); break; case DRAG_EDGE_TOP: left = Math.min(getPaddingLeft(), maxRight); top = Math.min(getPaddingTop(), maxBottom); right = Math.min(child.getMeasuredWidth() + getPaddingLeft(), maxRight); bottom = Math.min(child.getMeasuredHeight() + getPaddingTop(), maxBottom); break; case DRAG_EDGE_BOTTOM: left = Math.min(getPaddingLeft(), maxRight); top = Math.max(b - child.getMeasuredHeight() - getPaddingBottom() - t, minTop); right = Math.min(child.getMeasuredWidth() + getPaddingLeft(), maxRight); bottom = Math.max(b - getPaddingBottom() - t, minTop); break; } child.layout(left, top, right, bottom); } // taking account offset when mode is SAME_LEVEL if (mMode == MODE_SAME_LEVEL) { switch (mDragEdge) { case DRAG_EDGE_LEFT: mSecondaryView.offsetLeftAndRight(-mSecondaryView.getWidth()); break; case DRAG_EDGE_RIGHT: mSecondaryView.offsetLeftAndRight(mSecondaryView.getWidth()); break; case DRAG_EDGE_TOP: mSecondaryView.offsetTopAndBottom(-mSecondaryView.getHeight()); break; case DRAG_EDGE_BOTTOM: mSecondaryView.offsetTopAndBottom(mSecondaryView.getHeight()); } } initRects(); if (mIsOpenBeforeInit) { open(false); } else { close(false); } mLastMainLeft = mMainView.getLeft(); mLastMainTop = mMainView.getTop(); } /** * {@inheritDoc} */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (getChildCount() < 2) { throw new RuntimeException("Layout must have two children"); } final LayoutParams params = getLayoutParams(); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); final int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); int desiredWidth = 0; int desiredHeight = 0; for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); final LayoutParams childParams = child.getLayoutParams(); if (childParams != null) { if (childParams.height == LayoutParams.MATCH_PARENT) { child.setMinimumHeight(measuredHeight); } if (childParams.width == LayoutParams.MATCH_PARENT) { child.setMinimumWidth(measuredWidth); } } measureChild(child, widthMeasureSpec, heightMeasureSpec); desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth); desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight); } // taking accounts of padding desiredWidth += getPaddingLeft() + getPaddingRight(); desiredHeight += getPaddingTop() + getPaddingBottom(); // adjust desired width if (widthMode == MeasureSpec.EXACTLY) { desiredWidth = measuredWidth; } else { if (params.width == LayoutParams.MATCH_PARENT) { desiredWidth = measuredWidth; } if (widthMode == MeasureSpec.AT_MOST) { desiredWidth = (desiredWidth > measuredWidth) ? measuredWidth : desiredWidth; } } // adjust desired height if (heightMode == MeasureSpec.EXACTLY) { desiredHeight = measuredHeight; } else { if (params.height == LayoutParams.MATCH_PARENT) { desiredHeight = measuredHeight; } if (heightMode == MeasureSpec.AT_MOST) { desiredHeight = (desiredHeight > measuredHeight) ? measuredHeight : desiredHeight; } } setMeasuredDimension(desiredWidth, desiredHeight); } @Override public void computeScroll() { if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } /** * Open the panel to show the secondary view * * @param animation true to animate the open motion. {@link SwipeListener} won't be * called if is animation is false. */ public void open(boolean animation) { mIsOpenBeforeInit = true; mAborted = false; if (animation) { mState = STATE_OPENING; mDragHelper.smoothSlideViewTo(mMainView, mRectMainOpen.left, mRectMainOpen.top); if (mDragStateChangeListener != null) { mDragStateChangeListener.onDragStateChanged(mState); } } else { mState = STATE_OPEN; mDragHelper.abort(); mMainView.layout(mRectMainOpen.left, mRectMainOpen.top, mRectMainOpen.right, mRectMainOpen.bottom); mSecondaryView.layout(mRectSecOpen.left, mRectSecOpen.top, mRectSecOpen.right, mRectSecOpen.bottom); } ViewCompat.postInvalidateOnAnimation(SwipeRevealLayout.this); } /** * Close the panel to hide the secondary view * * @param animation true to animate the close motion. {@link SwipeListener} won't be * called if is animation is false. */ public void close(boolean animation) { mIsOpenBeforeInit = false; mAborted = false; if (animation) { mState = STATE_CLOSING; mDragHelper.smoothSlideViewTo(mMainView, mRectMainClose.left, mRectMainClose.top); if (mDragStateChangeListener != null) { mDragStateChangeListener.onDragStateChanged(mState); } } else { mState = STATE_CLOSE; mDragHelper.abort(); mMainView.layout(mRectMainClose.left, mRectMainClose.top, mRectMainClose.right, mRectMainClose.bottom); mSecondaryView.layout(mRectSecClose.left, mRectSecClose.top, mRectSecClose.right, mRectSecClose.bottom); } ViewCompat.postInvalidateOnAnimation(SwipeRevealLayout.this); } /** * Set the minimum fling velocity to cause the layout to open/close. * * @param velocity dp per second */ public void setMinFlingVelocity(int velocity) { mMinFlingVelocity = velocity; } /** * Get the minimum fling velocity to cause the layout to open/close. * * @return dp per second */ public int getMinFlingVelocity() { return mMinFlingVelocity; } /** * Set the edge where the layout can be dragged from. * * @param dragEdge Can be one of these * <ul> * <li>{@link #DRAG_EDGE_LEFT}</li> * <li>{@link #DRAG_EDGE_TOP}</li> * <li>{@link #DRAG_EDGE_RIGHT}</li> * <li>{@link #DRAG_EDGE_BOTTOM}</li> * </ul> */ public void setDragEdge(int dragEdge) { mDragEdge = dragEdge; } /** * Get the edge where the layout can be dragged from. * * @return Can be one of these * <ul> * <li>{@link #DRAG_EDGE_LEFT}</li> * <li>{@link #DRAG_EDGE_TOP}</li> * <li>{@link #DRAG_EDGE_RIGHT}</li> * <li>{@link #DRAG_EDGE_BOTTOM}</li> * </ul> */ public int getDragEdge() { return mDragEdge; } public void setSwipeListener(SwipeListener listener) { mSwipeListener = listener; } /** * @param lock if set to true, the user cannot drag/swipe the layout. */ public void setLockDrag(boolean lock) { mLockDrag = lock; } /** * @return true if the drag/swipe motion is currently locked. */ public boolean isDragLocked() { return mLockDrag; } public int getState() { return mState; } /** * Only used for {@link ViewBinderHelper} */ void setDragStateChangeListener(DragStateChangeListener listener) { mDragStateChangeListener = listener; } /** * Abort current motion in progress. Only used for {@link ViewBinderHelper} */ protected void abort() { mAborted = true; mDragHelper.abort(); } private int getMainOpenLeft() { switch (mDragEdge) { case DRAG_EDGE_LEFT: return mRectMainClose.left + mSecondaryView.getWidth(); case DRAG_EDGE_RIGHT: return mRectMainClose.left - mSecondaryView.getWidth(); case DRAG_EDGE_TOP: return mRectMainClose.left; case DRAG_EDGE_BOTTOM: return mRectMainClose.left; default: return 0; } } private int getMainOpenTop() { switch (mDragEdge) { case DRAG_EDGE_LEFT: return mRectMainClose.top; case DRAG_EDGE_RIGHT: return mRectMainClose.top; case DRAG_EDGE_TOP: return mRectMainClose.top + mSecondaryView.getHeight(); case DRAG_EDGE_BOTTOM: return mRectMainClose.top - mSecondaryView.getHeight(); default: return 0; } } private int getSecOpenLeft() { if (mMode == MODE_NORMAL || mDragEdge == DRAG_EDGE_BOTTOM || mDragEdge == DRAG_EDGE_TOP) { return mRectSecClose.left; } if (mDragEdge == DRAG_EDGE_LEFT) { return mRectSecClose.left + mSecondaryView.getWidth(); } else { return mRectSecClose.left - mSecondaryView.getWidth(); } } private int getSecOpenTop() { if (mMode == MODE_NORMAL || mDragEdge == DRAG_EDGE_LEFT || mDragEdge == DRAG_EDGE_RIGHT) { return mRectSecClose.top; } if (mDragEdge == DRAG_EDGE_TOP) { return mRectSecClose.top + mSecondaryView.getHeight(); } else { return mRectSecClose.top - mSecondaryView.getHeight(); } } private void initRects() { // close position of main view mRectMainClose.set(mMainView.getLeft(), mMainView.getTop(), mMainView.getRight(), mMainView.getBottom()); // close position of secondary view mRectSecClose.set(mSecondaryView.getLeft(), mSecondaryView.getTop(), mSecondaryView.getRight(), mSecondaryView.getBottom()); // open position of the main view mRectMainOpen.set(getMainOpenLeft(), getMainOpenTop(), getMainOpenLeft() + mMainView.getWidth(), getMainOpenTop() + mMainView.getHeight()); // open position of the secondary view mRectSecOpen.set(getSecOpenLeft(), getSecOpenTop(), getSecOpenLeft() + mSecondaryView.getWidth(), getSecOpenTop() + mSecondaryView.getHeight()); } private void init(Context context, AttributeSet attrs) { if (attrs != null && context != null) { TypedArray a = context.getTheme().obtainStyledAttributes(attrs, com.center.mycode.R.styleable.SwipeRevealLayout, 0, 0); mDragEdge = a.getInteger(com.center.mycode.R.styleable.SwipeRevealLayout_dragEdge, DRAG_EDGE_LEFT); mMinFlingVelocity = a.getInteger(com.center.mycode.R.styleable.SwipeRevealLayout_flingVelocity, DEFAULT_MIN_FLING_VELOCITY); mMode = a.getInteger(com.center.mycode.R.styleable.SwipeRevealLayout_mode, MODE_NORMAL); } mDragHelper = ViewDragHelper.create(this, 1.0f, mDragHelperCallback); mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); mGestureDetector = new GestureDetectorCompat(context, mGestureListener); } private final GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { mIsScrolling = false; return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { mIsScrolling = true; return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mIsScrolling = true; if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } return false; } }; private int getHalfwayPivotHorizontal() { if (mDragEdge == DRAG_EDGE_LEFT) { return mRectMainClose.left + mSecondaryView.getWidth() / 2; } else { return mRectMainClose.right - mSecondaryView.getWidth() / 2; } } private int getHalfwayPivotVertical() { if (mDragEdge == DRAG_EDGE_TOP) { return mRectMainClose.top + mSecondaryView.getHeight() / 2; } else { return mRectMainClose.bottom - mSecondaryView.getHeight() / 2; } } private final ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { mAborted = false; if (mLockDrag) { return false; } mDragHelper.captureChildView(mMainView, pointerId); return false; } @Override public int clampViewPositionVertical(View child, int top, int dy) { switch (mDragEdge) { case DRAG_EDGE_TOP: return Math.max(Math.min(top, mRectMainClose.top + mSecondaryView.getHeight()), mRectMainClose.top); case DRAG_EDGE_BOTTOM: return Math.max(Math.min(top, mRectMainClose.top), mRectMainClose.top - mSecondaryView.getHeight()); default: return child.getTop(); } } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { switch (mDragEdge) { case DRAG_EDGE_RIGHT: return Math.max(Math.min(left, mRectMainClose.left), mRectMainClose.left - mSecondaryView.getWidth()); case DRAG_EDGE_LEFT: return Math.max(Math.min(left, mRectMainClose.left + mSecondaryView.getWidth()), mRectMainClose.left); default: return child.getLeft(); } } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { final boolean velRightExceeded = pxToDp((int) xvel) >= mMinFlingVelocity; final boolean velLeftExceeded = pxToDp((int) xvel) <= -mMinFlingVelocity; final boolean velUpExceeded = pxToDp((int) yvel) <= -mMinFlingVelocity; final boolean velDownExceeded = pxToDp((int) yvel) >= mMinFlingVelocity; final int pivotHorizontal = getHalfwayPivotHorizontal(); final int pivotVertical = getHalfwayPivotVertical(); switch (mDragEdge) { case DRAG_EDGE_RIGHT: if (velRightExceeded) { close(true); } else if (velLeftExceeded) { open(true); } else { if (mMainView.getRight() < pivotHorizontal) { open(true); } else { close(true); } } break; case DRAG_EDGE_LEFT: if (velRightExceeded) { open(true); } else if (velLeftExceeded) { close(true); } else { if (mMainView.getLeft() < pivotHorizontal) { close(true); } else { open(true); } } break; case DRAG_EDGE_TOP: if (velUpExceeded) { close(true); } else if (velDownExceeded) { open(true); } else { if (mMainView.getTop() < pivotVertical) { close(true); } else { open(true); } } break; case DRAG_EDGE_BOTTOM: if (velUpExceeded) { open(true); } else if (velDownExceeded) { close(true); } else { if (mMainView.getBottom() < pivotVertical) { open(true); } else { close(true); } } break; } } @Override public void onEdgeDragStarted(int edgeFlags, int pointerId) { super.onEdgeDragStarted(edgeFlags, pointerId); if (mLockDrag) { return; } boolean edgeStartLeft = (mDragEdge == DRAG_EDGE_RIGHT) && edgeFlags == ViewDragHelper.EDGE_LEFT; boolean edgeStartRight = (mDragEdge == DRAG_EDGE_LEFT) && edgeFlags == ViewDragHelper.EDGE_RIGHT; boolean edgeStartTop = (mDragEdge == DRAG_EDGE_BOTTOM) && edgeFlags == ViewDragHelper.EDGE_TOP; boolean edgeStartBottom = (mDragEdge == DRAG_EDGE_TOP) && edgeFlags == ViewDragHelper.EDGE_BOTTOM; if (edgeStartLeft || edgeStartRight || edgeStartTop || edgeStartBottom) { mDragHelper.captureChildView(mMainView, pointerId); } } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); if (mMode == MODE_SAME_LEVEL) { if (mDragEdge == DRAG_EDGE_LEFT || mDragEdge == DRAG_EDGE_RIGHT) { mSecondaryView.offsetLeftAndRight(dx); } else { mSecondaryView.offsetTopAndBottom(dy); } } boolean isMoved = (mMainView.getLeft() != mLastMainLeft) || (mMainView.getTop() != mLastMainTop); if (mSwipeListener != null && isMoved) { if (mMainView.getLeft() == mRectMainClose.left && mMainView.getTop() == mRectMainClose.top) { mSwipeListener.onClosed(SwipeRevealLayout.this); } else if (mMainView.getLeft() == mRectMainOpen.left && mMainView.getTop() == mRectMainOpen.top) { mSwipeListener.onOpened(SwipeRevealLayout.this); } else { mSwipeListener.onSlide(SwipeRevealLayout.this, getSlideOffset()); } } mLastMainLeft = mMainView.getLeft(); mLastMainTop = mMainView.getTop(); ViewCompat.postInvalidateOnAnimation(SwipeRevealLayout.this); } private float getSlideOffset() { switch (mDragEdge) { case DRAG_EDGE_LEFT: return (float) (mMainView.getLeft() - mRectMainClose.left) / mSecondaryView.getWidth(); case DRAG_EDGE_RIGHT: return (float) (mRectMainClose.left - mMainView.getLeft()) / mSecondaryView.getWidth(); case DRAG_EDGE_TOP: return (float) (mMainView.getTop() - mRectMainClose.top) / mSecondaryView.getHeight(); case DRAG_EDGE_BOTTOM: return (float) (mRectMainClose.top - mMainView.getTop()) / mSecondaryView.getHeight(); default: return 0; } } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); final int prevState = mState; switch (state) { case ViewDragHelper.STATE_DRAGGING: mState = STATE_DRAGGING; break; case ViewDragHelper.STATE_IDLE: // drag edge is left or right if (mDragEdge == DRAG_EDGE_LEFT || mDragEdge == DRAG_EDGE_RIGHT) { if (mMainView.getLeft() == mRectMainClose.left) { mState = STATE_CLOSE; } else { mState = STATE_OPEN; } } // drag edge is top or bottom else { if (mMainView.getTop() == mRectMainClose.top) { mState = STATE_CLOSE; } else { mState = STATE_OPEN; } } break; } if (mDragStateChangeListener != null && !mAborted && prevState != mState) { mDragStateChangeListener.onDragStateChanged(mState); } } }; public static String getStateString(int state) { switch (state) { case STATE_CLOSE: return "state_close"; case STATE_CLOSING: return "state_closing"; case STATE_OPEN: return "state_open"; case STATE_OPENING: return "state_opening"; case STATE_DRAGGING: return "state_dragging"; default: return "undefined"; } } private int pxToDp(int px) { Resources resources = getContext().getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); return (int) (px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)); } }