| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- package me.imid.swipebacklayout.lib;
- import android.app.Activity;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Rect;
- import android.graphics.drawable.Drawable;
- import android.support.v4.view.ViewCompat;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.FrameLayout;
- import java.util.ArrayList;
- import java.util.List;
- public class SwipeBackLayout extends FrameLayout {
- /**
- * Minimum velocity that will be detected as a fling
- */
- private static final int MIN_FLING_VELOCITY = 400; // dips per second
- private static final int DEFAULT_SCRIM_COLOR = 0x99000000;
- private static final int FULL_ALPHA = 255;
- /**
- * Edge flag indicating that the left edge should be affected.
- */
- public static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT;
- /**
- * Edge flag indicating that the right edge should be affected.
- */
- public static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT;
- /**
- * Edge flag indicating that the bottom edge should be affected.
- */
- public static final int EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM;
- /**
- * Edge flag set indicating all edges should be affected.
- */
- public static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT | EDGE_BOTTOM;
- /**
- * A view is not currently being dragged or animating as a result of a
- * fling/snap.
- */
- public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
- /**
- * A view is currently being dragged. The position is currently changing as
- * a result of user input or simulated user input.
- */
- public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
- /**
- * A view is currently settling into place as a result of a fling or
- * predefined non-interactive motion.
- */
- public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
- /**
- * Default threshold of scroll
- */
- private static final float DEFAULT_SCROLL_THRESHOLD = 0.3f;
- private static final int OVERSCROLL_DISTANCE = 10;
- private static final int[] EDGE_FLAGS = {
- EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL
- };
- private int mEdgeFlag;
- /**
- * Threshold of scroll, we will close the activity, when scrollPercent over
- * this value;
- */
- private float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD;
- private Activity mActivity;
- private boolean mEnable = true;
- private View mContentView;
- private ViewDragHelper mDragHelper;
- private float mScrollPercent;
- private int mContentLeft;
- private int mContentTop;
- /**
- * The set of listeners to be sent events through.
- */
- private List<SwipeListener> mListeners;
- private Drawable mShadowLeft;
- private Drawable mShadowRight;
- private Drawable mShadowBottom;
- private float mScrimOpacity;
- private int mScrimColor = DEFAULT_SCRIM_COLOR;
- private boolean mInLayout;
- private Rect mTmpRect = new Rect();
- /**
- * Edge being dragged
- */
- private int mTrackingEdge;
- public SwipeBackLayout(Context context) {
- this(context, null);
- }
- public SwipeBackLayout(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.SwipeBackLayoutStyle);
- }
- public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs);
- mDragHelper = ViewDragHelper.create(this, new ViewDragCallback());
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeBackLayout, defStyle,
- R.style.SwipeBackLayout);
- int edgeSize = a.getDimensionPixelSize(R.styleable.SwipeBackLayout_edge_size, -1);
- if (edgeSize > 0)
- setEdgeSize(edgeSize);
- int mode = EDGE_FLAGS[a.getInt(R.styleable.SwipeBackLayout_edge_flag, 0)];
- setEdgeTrackingEnabled(mode);
- int shadowLeft = a.getResourceId(R.styleable.SwipeBackLayout_shadow_left,
- R.drawable.shadow_left);
- int shadowRight = a.getResourceId(R.styleable.SwipeBackLayout_shadow_right,
- R.drawable.shadow_right);
- int shadowBottom = a.getResourceId(R.styleable.SwipeBackLayout_shadow_bottom,
- R.drawable.shadow_bottom);
- setShadow(shadowLeft, EDGE_LEFT);
- setShadow(shadowRight, EDGE_RIGHT);
- setShadow(shadowBottom, EDGE_BOTTOM);
- a.recycle();
- final float density = getResources().getDisplayMetrics().density;
- final float minVel = MIN_FLING_VELOCITY * density;
- mDragHelper.setMinVelocity(minVel);
- mDragHelper.setMaxVelocity(minVel * 2f);
- }
- /**
- * Sets the sensitivity of the NavigationLayout.
- *
- * @param context The application context.
- * @param sensitivity value between 0 and 1, the final value for touchSlop =
- * ViewConfiguration.getScaledTouchSlop * (1 / s);
- */
- public void setSensitivity(Context context, float sensitivity) {
- mDragHelper.setSensitivity(context, sensitivity);
- }
- /**
- * Set up contentView which will be moved by user gesture
- *
- * @param view
- */
- private void setContentView(View view) {
- mContentView = view;
- }
- public void setEnableGesture(boolean enable) {
- mEnable = enable;
- }
- /**
- * Enable edge tracking for the selected edges of the parent view. The
- * callback's
- * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeTouched(int, int)}
- * and
- * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeDragStarted(int, int)}
- * methods will only be invoked for edges for which edge tracking has been
- * enabled.
- *
- * @param edgeFlags Combination of edge flags describing the edges to watch
- * @see #EDGE_LEFT
- * @see #EDGE_RIGHT
- * @see #EDGE_BOTTOM
- */
- public void setEdgeTrackingEnabled(int edgeFlags) {
- mEdgeFlag = edgeFlags;
- mDragHelper.setEdgeTrackingEnabled(mEdgeFlag);
- }
- /**
- * Set a color to use for the scrim that obscures primary content while a
- * drawer is open.
- *
- * @param color Color to use in 0xAARRGGBB format.
- */
- public void setScrimColor(int color) {
- mScrimColor = color;
- invalidate();
- }
- /**
- * Set the size of an edge. This is the range in pixels along the edges of
- * this view that will actively detect edge touches or drags if edge
- * tracking is enabled.
- *
- * @param size The size of an edge in pixels
- */
- public void setEdgeSize(int size) {
- mDragHelper.setEdgeSize(size);
- }
- /**
- * Register a callback to be invoked when a swipe event is sent to this
- * view.
- *
- * @param listener the swipe listener to attach to this view
- * @deprecated use {@link #addSwipeListener} instead
- */
- @Deprecated
- public void setSwipeListener(SwipeListener listener) {
- addSwipeListener(listener);
- }
- /**
- * Add a callback to be invoked when a swipe event is sent to this view.
- *
- * @param listener the swipe listener to attach to this view
- */
- public void addSwipeListener(SwipeListener listener) {
- if (mListeners == null) {
- mListeners = new ArrayList<SwipeListener>();
- }
- mListeners.add(listener);
- }
- /**
- * Removes a listener from the set of listeners
- *
- * @param listener
- */
- public void removeSwipeListener(SwipeListener listener) {
- if (mListeners == null) {
- return;
- }
- mListeners.remove(listener);
- }
- public static interface SwipeListener {
- /**
- * Invoke when state change
- *
- * @param state flag to describe scroll state
- * @param scrollPercent scroll percent of this view
- * @see #STATE_IDLE
- * @see #STATE_DRAGGING
- * @see #STATE_SETTLING
- */
- public void onScrollStateChange(int state, float scrollPercent);
- /**
- * Invoke when edge touched
- *
- * @param edgeFlag edge flag describing the edge being touched
- * @see #EDGE_LEFT
- * @see #EDGE_RIGHT
- * @see #EDGE_BOTTOM
- */
- public void onEdgeTouch(int edgeFlag);
- /**
- * Invoke when scroll percent over the threshold for the first time
- */
- public void onScrollOverThreshold();
- }
- /**
- * Set scroll threshold, we will close the activity, when scrollPercent over
- * this value
- *
- * @param threshold
- */
- public void setScrollThresHold(float threshold) {
- if (threshold >= 1.0f || threshold <= 0) {
- throw new IllegalArgumentException("Threshold value should be between 0 and 1.0");
- }
- mScrollThreshold = threshold;
- }
- /**
- * Set a drawable used for edge shadow.
- *
- * @param shadow Drawable to use
- * @param edgeFlags Combination of edge flags describing the edge to set
- * @see #EDGE_LEFT
- * @see #EDGE_RIGHT
- * @see #EDGE_BOTTOM
- */
- public void setShadow(Drawable shadow, int edgeFlag) {
- if ((edgeFlag & EDGE_LEFT) != 0) {
- mShadowLeft = shadow;
- } else if ((edgeFlag & EDGE_RIGHT) != 0) {
- mShadowRight = shadow;
- } else if ((edgeFlag & EDGE_BOTTOM) != 0) {
- mShadowBottom = shadow;
- }
- invalidate();
- }
- /**
- * Set a drawable used for edge shadow.
- *
- * @param resId Resource of drawable to use
- * @param edgeFlags Combination of edge flags describing the edge to set
- * @see #EDGE_LEFT
- * @see #EDGE_RIGHT
- * @see #EDGE_BOTTOM
- */
- public void setShadow(int resId, int edgeFlag) {
- setShadow(getResources().getDrawable(resId), edgeFlag);
- }
- /**
- * Scroll out contentView and finish the activity
- */
- public void scrollToFinishActivity() {
- final int childWidth = mContentView.getWidth();
- final int childHeight = mContentView.getHeight();
- int left = 0, top = 0;
- if ((mEdgeFlag & EDGE_LEFT) != 0) {
- left = childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE;
- mTrackingEdge = EDGE_LEFT;
- } else if ((mEdgeFlag & EDGE_RIGHT) != 0) {
- left = -childWidth - mShadowRight.getIntrinsicWidth() - OVERSCROLL_DISTANCE;
- mTrackingEdge = EDGE_RIGHT;
- } else if ((mEdgeFlag & EDGE_BOTTOM) != 0) {
- top = -childHeight - mShadowBottom.getIntrinsicHeight() - OVERSCROLL_DISTANCE;
- mTrackingEdge = EDGE_BOTTOM;
- }
- mDragHelper.smoothSlideViewTo(mContentView, left, top);
- invalidate();
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if (!mEnable) {
- return false;
- }
- try {
- return mDragHelper.shouldInterceptTouchEvent(event);
- } catch (ArrayIndexOutOfBoundsException e) {
- // FIXME: handle exception
- // issues #9
- return false;
- }
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!mEnable) {
- return false;
- }
- mDragHelper.processTouchEvent(event);
- return true;
- }
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mInLayout = true;
- if (mContentView != null)
- mContentView.layout(mContentLeft, mContentTop,
- mContentLeft + mContentView.getMeasuredWidth(),
- mContentTop + mContentView.getMeasuredHeight());
- mInLayout = false;
- }
- @Override
- public void requestLayout() {
- if (!mInLayout) {
- super.requestLayout();
- }
- }
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- final boolean drawContent = child == mContentView;
- boolean ret = super.drawChild(canvas, child, drawingTime);
- if (mScrimOpacity > 0 && drawContent
- && mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {
- drawShadow(canvas, child);
- drawScrim(canvas, child);
- }
- return ret;
- }
- private void drawScrim(Canvas canvas, View child) {
- final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;
- final int alpha = (int) (baseAlpha * mScrimOpacity);
- final int color = alpha << 24 | (mScrimColor & 0xffffff);
- if ((mTrackingEdge & EDGE_LEFT) != 0) {
- canvas.clipRect(0, 0, child.getLeft(), getHeight());
- } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {
- canvas.clipRect(child.getRight(), 0, getRight(), getHeight());
- } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {
- canvas.clipRect(child.getLeft(), child.getBottom(), getRight(), getHeight());
- }
- canvas.drawColor(color);
- }
- private void drawShadow(Canvas canvas, View child) {
- final Rect childRect = mTmpRect;
- child.getHitRect(childRect);
- if ((mEdgeFlag & EDGE_LEFT) != 0) {
- mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top,
- childRect.left, childRect.bottom);
- mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA));
- mShadowLeft.draw(canvas);
- }
- if ((mEdgeFlag & EDGE_RIGHT) != 0) {
- mShadowRight.setBounds(childRect.right, childRect.top,
- childRect.right + mShadowRight.getIntrinsicWidth(), childRect.bottom);
- mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA));
- mShadowRight.draw(canvas);
- }
- if ((mEdgeFlag & EDGE_BOTTOM) != 0) {
- mShadowBottom.setBounds(childRect.left, childRect.bottom, childRect.right,
- childRect.bottom + mShadowBottom.getIntrinsicHeight());
- mShadowBottom.setAlpha((int) (mScrimOpacity * FULL_ALPHA));
- mShadowBottom.draw(canvas);
- }
- }
- public void attachToActivity(Activity activity) {
- mActivity = activity;
- TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{
- android.R.attr.windowBackground
- });
- int background = a.getResourceId(0, 0);
- a.recycle();
- ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
- ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
- decorChild.setBackgroundResource(background);
- decor.removeView(decorChild);
- addView(decorChild);
- setContentView(decorChild);
- decor.addView(this);
- }
- @Override
- public void computeScroll() {
- mScrimOpacity = 1 - mScrollPercent;
- if (mDragHelper.continueSettling(true)) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
- private class ViewDragCallback extends ViewDragHelper.Callback {
- private boolean mIsScrollOverValid;
- @Override
- public boolean tryCaptureView(View view, int i) {
- boolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i);
- if (ret) {
- if (mDragHelper.isEdgeTouched(EDGE_LEFT, i)) {
- mTrackingEdge = EDGE_LEFT;
- } else if (mDragHelper.isEdgeTouched(EDGE_RIGHT, i)) {
- mTrackingEdge = EDGE_RIGHT;
- } else if (mDragHelper.isEdgeTouched(EDGE_BOTTOM, i)) {
- mTrackingEdge = EDGE_BOTTOM;
- }
- if (mListeners != null && !mListeners.isEmpty()) {
- for (SwipeListener listener : mListeners) {
- listener.onEdgeTouch(mTrackingEdge);
- }
- }
- mIsScrollOverValid = true;
- }
- boolean directionCheck = false;
- if (mEdgeFlag == EDGE_LEFT || mEdgeFlag == EDGE_RIGHT) {
- directionCheck = !mDragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL, i);
- } else if (mEdgeFlag == EDGE_BOTTOM) {
- directionCheck = !mDragHelper
- .checkTouchSlop(ViewDragHelper.DIRECTION_HORIZONTAL, i);
- } else if (mEdgeFlag == EDGE_ALL) {
- directionCheck = true;
- }
- return ret & directionCheck;
- }
- @Override
- public int getViewHorizontalDragRange(View child) {
- return mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT);
- }
- @Override
- public int getViewVerticalDragRange(View child) {
- return mEdgeFlag & EDGE_BOTTOM;
- }
- @Override
- public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
- super.onViewPositionChanged(changedView, left, top, dx, dy);
- if ((mTrackingEdge & EDGE_LEFT) != 0) {
- mScrollPercent = Math.abs((float) left
- / (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth()));
- } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {
- mScrollPercent = Math.abs((float) left
- / (mContentView.getWidth() + mShadowRight.getIntrinsicWidth()));
- } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {
- mScrollPercent = Math.abs((float) top
- / (mContentView.getHeight() + mShadowBottom.getIntrinsicHeight()));
- }
- mContentLeft = left;
- mContentTop = top;
- invalidate();
- if (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) {
- mIsScrollOverValid = true;
- }
- if (mListeners != null && !mListeners.isEmpty()
- && mDragHelper.getViewDragState() == STATE_DRAGGING
- && mScrollPercent >= mScrollThreshold && mIsScrollOverValid) {
- mIsScrollOverValid = false;
- for (SwipeListener listener : mListeners) {
- listener.onScrollOverThreshold();
- }
- }
- if (mScrollPercent >= 1) {
- if (!mActivity.isFinishing()) {
- mActivity.finish();
- mActivity.overridePendingTransition(0, 0);
- }
- }
- }
- @Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
- final int childWidth = releasedChild.getWidth();
- final int childHeight = releasedChild.getHeight();
- int left = 0, top = 0;
- if ((mTrackingEdge & EDGE_LEFT) != 0) {
- left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? childWidth
- + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE : 0;
- } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {
- left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? -(childWidth
- + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0;
- } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {
- top = yvel < 0 || yvel == 0 && mScrollPercent > mScrollThreshold ? -(childHeight
- + mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE) : 0;
- }
- mDragHelper.settleCapturedViewAt(left, top);
- invalidate();
- }
- @Override
- public int clampViewPositionHorizontal(View child, int left, int dx) {
- int ret = 0;
- if ((mTrackingEdge & EDGE_LEFT) != 0) {
- ret = Math.min(child.getWidth(), Math.max(left, 0));
- } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {
- ret = Math.min(0, Math.max(left, -child.getWidth()));
- }
- return ret;
- }
- @Override
- public int clampViewPositionVertical(View child, int top, int dy) {
- int ret = 0;
- if ((mTrackingEdge & EDGE_BOTTOM) != 0) {
- ret = Math.min(0, Math.max(top, -child.getHeight()));
- }
- return ret;
- }
- @Override
- public void onViewDragStateChanged(int state) {
- super.onViewDragStateChanged(state);
- if (mListeners != null && !mListeners.isEmpty()) {
- for (SwipeListener listener : mListeners) {
- listener.onScrollStateChange(state, mScrollPercent);
- }
- }
- }
- }
- }
|