Skip to content

Instantly share code, notes, and snippets.

@zerobranch
Last active February 16, 2021 21:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerobranch/64359efd499ed38f6996390e79a5eadc to your computer and use it in GitHub Desktop.
Save zerobranch/64359efd499ed38f6996390e79a5eadc to your computer and use it in GitHub Desktop.
This custom view provides an opportunity to perform swipe for any layout. SwipeLayout is just a 'Custom ViewGroup', extended from FrameLayout, which provides easy and fast to use the 'swipe to dismiss' function, without using the ItemTouchHelper, for any layout.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SwipeLayout">
<attr name="swipeDirection" format="integer">
<flag name="left" value="1"/>
<flag name="right" value="2"/>
</attr>
<attr name="isEnabledSwipe" format="boolean"/>
<attr name="isFreeDragAfterOpen" format="boolean"/>
<attr name="isFreeHorizontalDrag" format="boolean"/>
<attr name="isContinuousSwipe" format="boolean"/>
<attr name="isTogether" format="boolean"/>
<attr name="rightItem" format="reference"/>
<attr name="leftItem" format="reference"/>
<attr name="draggedItem" format="reference"/>
<attr name="autoMovingSensitivity" format="integer"/>
<attr name="rightDragViewPadding" format="dimension"/>
<attr name="leftDragViewPadding" format="dimension"/>
</declare-styleable>
</resources>
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Arman Sargsyan
*
* 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:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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.zerobranch.layout;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.GestureDetectorCompat;
import androidx.core.view.ViewCompat;
import androidx.customview.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
/**
* This project provides an opportunity to perform swipe for any layout,
* in the direction specified by you.
* <p>
* Date: 2018-09-27
* Repository #https://github.com/zerobranch/SwipeLayout
*
* @author Arman Sargsyan
*/
public class SwipeLayout extends FrameLayout {
public static final int LEFT = 1;
public static final int RIGHT = LEFT << 1;
public static final int HORIZONTAL = LEFT | RIGHT;
private static final int CLOSE_POSITION = 0;
private static final int NO_POSITION = -1;
private static final int DEFAULT_AUTO_OPEN_SPEED = 1000;
/**
* Current direction of a swipe
*/
private int currentDirection;
/**
* The secondary view will move along with the main view
*/
private boolean isTogether;
/**
* Is enabled Swipe
*/
private boolean isEnabledSwipe;
/**
* Swipe to the end of the screen.
* Can work without a secondary view {@link #staticLeftView} and {@link #staticRightView}
* <p>
* If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}),
* and this flag is set, then {@link #isFreeDragAfterOpen} always will be true.
* <p>
* If the left and right directions of the swipe are used simultaneously ({@link #HORIZONTAL}),
* then this flag will be ignored
*/
private boolean isContinuousSwipe;
/**
* Moving the main view after it was open.
* <p>
* if {@link #isEmptyLeftView()} or {@link #isEmptyRightView()},
* then this flag will be ignored
*/
private boolean isFreeDragAfterOpen;
/**
* If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}),
* then this flag allows you to do the swipe in the opposite direction.
* <p>
* If the horizontal direction is used ({@link #HORIZONTAL}),
* this flag allows you to move the main view continuously in both directions
*/
private boolean isFreeHorizontalDrag;
/**
* The right bounding border of the swipe for the main view
*/
private int rightDragViewPadding;
/**
* The left bounding border of the swipe for the main view
*/
private int leftDragViewPadding;
/**
* Sensitivity of automatic closing of the main view
*/
private double autoOpenSpeed;
/**
* Disable intercept touch event for draggable view
*/
private boolean disallowIntercept;
private int currentDraggingState = ViewDragHelper.STATE_IDLE;
private ViewDragHelper dragHelper;
private GestureDetectorCompat gestureDetector;
private int draggingViewLeft;
private int horizontalWidth;
private boolean isLeftOpen;
private boolean isRightOpen;
private int staticRightViewId;
private int staticLeftViewId;
private int draggedViewId;
private View draggedView;
private View staticRightView;
private View staticLeftView;
private SwipeActionsListener actionsListener;
public SwipeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
isLeftOpen = false;
isRightOpen = false;
final TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SwipeLayout);
currentDirection = typedArray.getInteger(R.styleable.SwipeLayout_swipeDirection, LEFT);
isFreeDragAfterOpen = typedArray.getBoolean(R.styleable.SwipeLayout_isFreeDragAfterOpen, false);
isFreeHorizontalDrag = typedArray.getBoolean(R.styleable.SwipeLayout_isFreeHorizontalDrag, false);
isContinuousSwipe = typedArray.getBoolean(R.styleable.SwipeLayout_isContinuousSwipe, false);
isTogether = typedArray.getBoolean(R.styleable.SwipeLayout_isTogether, false);
isEnabledSwipe = typedArray.getBoolean(R.styleable.SwipeLayout_isEnabledSwipe, true);
staticLeftViewId = typedArray.getResourceId(R.styleable.SwipeLayout_leftItem, 0);
staticRightViewId = typedArray.getResourceId(R.styleable.SwipeLayout_rightItem, 0);
draggedViewId = typedArray.getResourceId(R.styleable.SwipeLayout_draggedItem, 0);
autoOpenSpeed = typedArray.getInt(R.styleable.SwipeLayout_autoMovingSensitivity, DEFAULT_AUTO_OPEN_SPEED);
rightDragViewPadding = (int) typedArray.getDimension(R.styleable.SwipeLayout_rightDragViewPadding, 0);
leftDragViewPadding = (int) typedArray.getDimension(R.styleable.SwipeLayout_leftDragViewPadding, 0);
parametersAdjustment();
typedArray.recycle();
}
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
horizontalWidth = w;
super.onSizeChanged(w, h, oldW, oldH);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (disallowIntercept && isViewGroup(draggedView)) {
final View neededScrollView = getNeededTouchView(event, (ViewGroup) draggedView);
final Point touchPoint = new Point((int) event.getX(), (int) event.getY());
if (neededScrollView != null && isViewTouchTarget(neededScrollView, touchPoint)) {
return false;
}
}
return isSwipeViewTarget(event) && dragHelper.shouldInterceptTouchEvent(event);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
this.disallowIntercept = disallowIntercept;
}
@Override
protected void onFinishInflate() {
if (draggedViewId != 0) {
draggedView = findViewById(draggedViewId);
}
if (staticLeftViewId != 0) {
staticLeftView = findViewById(staticLeftViewId);
}
if (staticRightViewId != 0) {
staticRightView = findViewById(staticRightViewId);
}
if (draggedView == null) {
throw new RuntimeException("'draggedItem' must be specified");
} else if (isTogether && currentDirection == LEFT && staticRightView == null) {
throw new RuntimeException("If 'isTogether = true' 'rightItem' must be specified");
} else if (isTogether && currentDirection == RIGHT && staticLeftView == null) {
throw new RuntimeException("If 'isTogether = true' 'leftItem' must be specified");
} else if (currentDirection == LEFT && !isContinuousSwipe && staticRightView == null) {
throw new RuntimeException("Must be specified 'rightItem' or flag isContinuousSwipe = true");
} else if (currentDirection == RIGHT && !isContinuousSwipe && staticLeftView == null) {
throw new RuntimeException("Must be specified 'leftItem' or flag isContinuousSwipe = true");
} else if (currentDirection == HORIZONTAL && (staticRightView == null || staticLeftView == null)) {
throw new RuntimeException("'leftItem' and 'rightItem' must be specified");
}
dragHelper = ViewDragHelper.create(this, 1.0f, dragHelperCallback);
gestureDetector = new GestureDetectorCompat(getContext(), gestureDetectorCallBack);
setupPost();
super.onFinishInflate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isSwipeViewTarget(event) || isMoving()) {
gestureDetector.onTouchEvent(event);
dragHelper.processTouchEvent(event);
return true;
} else {
return super.onTouchEvent(event);
}
}
@Override
public void computeScroll() {
if (dragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
/**
* Is enabled Swipe
*
* @return True if swipe is enabled, false otherwise.
*/
public boolean isEnabledSwipe() {
return isEnabledSwipe;
}
/**
* Set the enabled swipe.
*
* @param enabledSwipe True if swipe is enabled, false otherwise.
*/
public void setEnabledSwipe(boolean enabledSwipe) {
this.isEnabledSwipe = enabledSwipe;
}
/**
* Performs manual swipe to the left
*
* @param animated - flag to animate opening
*/
public void openRight(boolean animated) {
if (animated) {
openRight();
} else if (isDragIdle(currentDraggingState) && ((currentDirection == LEFT && !isEmptyRightView())
|| currentDirection == HORIZONTAL) && !isRightOpen) {
if (isTogether) {
staticRightView.offsetLeftAndRight(-1 * (isLeftOpen ? getRightViewWidth() * 2 : getRightViewWidth()));
}
draggedView.offsetLeftAndRight(-1 * (isLeftOpen ? getRightViewWidth() * 2 : getRightViewWidth()));
draggingViewLeft -= (isLeftOpen ? getRightViewWidth() * 2 : getRightViewWidth());
updateState();
}
}
/**
* Performs a full manual swipe to the left
*
* @param animated - flag to animate opening
*/
public void openRightCompletely(boolean animated) {
if (animated) {
openRightCompletely();
} else {
if (isDragIdle(currentDraggingState) && currentDirection == LEFT) {
if (isTogether) {
staticRightView.offsetLeftAndRight(-horizontalWidth);
}
draggedView.offsetLeftAndRight(-horizontalWidth);
draggingViewLeft -= horizontalWidth;
updateState();
}
}
}
/**
* Performs manual swipe to the right
*
* @param animated - flag to animate opening
*/
public void openLeft(boolean animated) {
if (animated) {
openLeft();
} else if (isDragIdle(currentDraggingState) && ((currentDirection == RIGHT && !isEmptyLeftView())
|| currentDirection == HORIZONTAL) && !isLeftOpen) {
if (isTogether) {
staticLeftView.offsetLeftAndRight((isRightOpen ? getLeftViewWidth() * 2 : getLeftViewWidth()));
}
draggedView.offsetLeftAndRight((isRightOpen ? getLeftViewWidth() * 2 : getLeftViewWidth()));
draggingViewLeft += (isRightOpen ? getLeftViewWidth() * 2 : getLeftViewWidth());
updateState();
}
}
/**
* Performs a full manual swipe to the right
*
* @param animated - flag to animate opening
*/
public void openLeftCompletely(boolean animated) {
if (animated) {
openRightCompletely();
} else {
if (isDragIdle(currentDraggingState) && currentDirection == RIGHT) {
if (isTogether) {
staticRightView.offsetLeftAndRight(horizontalWidth);
}
draggedView.offsetLeftAndRight(horizontalWidth);
draggingViewLeft += horizontalWidth;
updateState();
}
}
}
/**
* Performs manual close
*
* @param animated - flag to animate closing
*/
public void close(boolean animated) {
if (animated) {
close();
} else {
if (isTogether) {
if (staticLeftView != null && currentDirection == RIGHT) {
staticLeftView.layout(CLOSE_POSITION, staticLeftView.getTop(),
staticLeftView.getWidth(), staticLeftView.getBottom());
} else if (staticRightView != null && currentDirection == LEFT) {
staticRightView.layout(horizontalWidth - staticRightView.getWidth(), staticRightView.getTop(),
horizontalWidth, staticRightView.getBottom());
} else if (currentDirection == HORIZONTAL && staticRightView != null && staticLeftView != null) {
staticLeftView.layout(CLOSE_POSITION, staticLeftView.getTop(),
staticLeftView.getWidth(), staticLeftView.getBottom());
staticRightView.layout(horizontalWidth - staticRightView.getWidth(), staticRightView.getTop(),
horizontalWidth, staticRightView.getBottom());
}
}
draggedView.layout(CLOSE_POSITION,
draggedView.getTop(),
draggedView.getWidth(),
draggedView.getBottom());
draggingViewLeft = CLOSE_POSITION;
updateState();
}
}
/**
* Performs manual swipe to the right
*/
public void openLeft() {
if (isDragIdle(currentDraggingState) && ((currentDirection == RIGHT && !isEmptyLeftView())
|| currentDirection == HORIZONTAL)) {
moveTo(getLeftViewWidth());
}
}
/**
* Performs manual swipe to the left
*/
public void openRight() {
if (isDragIdle(currentDraggingState) && ((currentDirection == LEFT && !isEmptyRightView())
|| currentDirection == HORIZONTAL)) {
moveTo(-getRightViewWidth());
}
}
/**
* Performs a full manual swipe to the right
*/
public void openLeftCompletely() {
if (isDragIdle(currentDraggingState) && currentDirection == RIGHT) {
moveTo(horizontalWidth);
}
}
/**
* Performs a full manual swipe to the left
*/
public void openRightCompletely() {
if (isDragIdle(currentDraggingState) && currentDirection == LEFT) {
moveTo(-horizontalWidth);
}
}
/**
* Performs manual close
*/
public void close() {
moveTo(CLOSE_POSITION);
}
/**
* Is moving main view
*/
public boolean isMoving() {
return (currentDraggingState == ViewDragHelper.STATE_DRAGGING ||
currentDraggingState == ViewDragHelper.STATE_SETTLING);
}
/**
* Is closed main view
*/
public boolean isClosed() {
return draggingViewLeft == CLOSE_POSITION;
}
/**
* Get current direction of a swipe
*/
public int getCurrentDirection() {
return currentDirection;
}
/**
* Set current direction of a swipe
*/
public SwipeLayout setCurrentDirection(int currentDirection) {
this.currentDirection = currentDirection;
return this;
}
/**
* Is move the secondary view along with the main view
*/
public boolean isTogether() {
return isTogether;
}
/**
* The secondary view will move along with the main view
*/
public SwipeLayout setTogether(boolean together) {
isTogether = together;
return this;
}
/**
* Swipe to the end of the screen.
* Can work without a secondary view {@link #staticLeftView} and {@link #staticRightView}
* <p>
* If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}),
* and this flag is set, then {@link #isFreeDragAfterOpen} always will be true.
* <p>
* If the left and right directions of the swipe are used simultaneously ({@link #HORIZONTAL}),
* then this flag will be ignored
*/
public boolean isContinuousSwipe() {
return isContinuousSwipe;
}
/**
* Swipe to the end of the screen.
* Can work without a secondary view {@link #staticLeftView} and {@link #staticRightView}
* <p>
* If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}),
* and this flag is set, then {@link #isFreeDragAfterOpen} always will be true.
* <p>
* If the left and right directions of the swipe are used simultaneously ({@link #HORIZONTAL}),
* then this flag will be ignored
*/
public SwipeLayout setContinuousSwipe(boolean continuousSwipe) {
isContinuousSwipe = continuousSwipe;
parametersAdjustment();
return this;
}
/**
* Moving the main view after it was open.
* <p>
* if {@link #isEmptyLeftView()} or {@link #isEmptyRightView()},
* then this flag will be ignored
*/
public boolean isFreeDragAfterOpen() {
return isFreeDragAfterOpen;
}
/**
* Moving the main view after it was open.
* <p>
* if {@link #isEmptyLeftView()} or {@link #isEmptyRightView()},
* then this flag will be ignored
*/
public SwipeLayout setFreeDragAfterOpen(boolean freeDragAfterOpen) {
isFreeDragAfterOpen = freeDragAfterOpen;
parametersAdjustment();
return this;
}
/**
* If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}),
* then this flag allows you to do the swipe in the opposite direction.
* <p>
* If the horizontal direction is used ({@link #HORIZONTAL}),
* this flag allows you to move the main view continuously in both directions
*/
public boolean isFreeHorizontalDrag() {
return isFreeHorizontalDrag;
}
/**
* If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}),
* then this flag allows you to do the swipe in the opposite direction.
* <p>
* If the horizontal direction is used ({@link #HORIZONTAL}),
* this flag allows you to move the main view continuously in both directions
*/
public SwipeLayout setFreeHorizontalDrag(boolean freeHorizontalDrag) {
isFreeHorizontalDrag = freeHorizontalDrag;
return this;
}
/**
* Is open right view
*/
public boolean isRightOpen() {
return isRightOpen;
}
/**
* Is open left view
*/
public boolean isLeftOpen() {
return isLeftOpen;
}
/**
* Set swipe actions listener
*/
public SwipeLayout setOnActionsListener(@Nullable SwipeActionsListener actionsListener) {
this.actionsListener = actionsListener;
return this;
}
/**
* Get the right bounding border of the swipe for the main view
*/
public int getRightDragViewPadding() {
return rightDragViewPadding;
}
/**
* Set the right bounding border of the swipe for the main view
*/
public SwipeLayout setRightDragViewPadding(int minRightDragViewPadding) {
this.rightDragViewPadding = minRightDragViewPadding;
parametersAdjustment();
return this;
}
/**
* Get the left bounding border of the swipe for the main view
*/
public int getLeftDragViewPadding() {
return leftDragViewPadding;
}
/**
* Set the left bounding border of the swipe for the main view
*/
public SwipeLayout setLeftDragViewPadding(int minLeftDragViewPadding) {
this.leftDragViewPadding = minLeftDragViewPadding;
parametersAdjustment();
return this;
}
/**
* Enable touch for ViewGroup
*/
public void enableTouchForViewGroup(@NonNull final ViewGroup viewGroup) {
viewGroup.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
requestDisallowInterceptTouchEvent(true);
return false;
}
});
}
private void updateState() {
if (isClosed()) {
isLeftOpen = false;
isRightOpen = false;
if (actionsListener != null) {
actionsListener.onClose();
}
} else if (isLeftOpenCompletely() || isLeftViewOpen()) {
isLeftOpen = true;
isRightOpen = false;
if (actionsListener != null) {
actionsListener.onOpen(RIGHT, isLeftOpenCompletely());
}
} else if (isRightOpenCompletely() || isRightViewOpen()) {
isLeftOpen = false;
isRightOpen = true;
if (actionsListener != null) {
actionsListener.onOpen(LEFT, isRightOpenCompletely());
}
}
}
private GestureDetector.OnGestureListener gestureDetectorCallBack = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
return false;
}
};
private final ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() {
@Override
public void onViewDragStateChanged(int state) {
if (state == currentDraggingState)
return;
if (isIdleAfterMoving(state)) {
updateState();
}
currentDraggingState = state;
}
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
draggingViewLeft = left;
if (isTogether) {
if (currentDirection == LEFT) {
staticRightView.offsetLeftAndRight(dx);
} else if (currentDirection == RIGHT) {
staticLeftView.offsetLeftAndRight(dx);
} else if (currentDirection == HORIZONTAL) {
staticLeftView.offsetLeftAndRight(dx);
staticRightView.offsetLeftAndRight(dx);
}
}
}
@Override
public int getViewHorizontalDragRange(@NonNull View child) {
return horizontalWidth;
}
@Override
public boolean tryCaptureView(@NonNull View view, int pointerId) {
return view.getId() == draggedView.getId();
}
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
if (!isEnabledSwipe) {
return CLOSE_POSITION;
}
switch (currentDirection) {
case LEFT:
return clampLeftViewPosition(left);
case RIGHT:
return clampRightViewPosition(left);
case HORIZONTAL:
return clampHorizontalViewPosition(left, dx);
default:
return CLOSE_POSITION;
}
}
@Override
public void onViewReleased(@NonNull View releasedChild, float xVel, float yVel) {
int finalXDraggingView = CLOSE_POSITION;
if (currentDirection == LEFT) {
finalXDraggingView = getFinalXLeftDirection(xVel);
} else if (currentDirection == RIGHT) {
finalXDraggingView = getFinalXRightDirection(xVel);
} else if (currentDirection == HORIZONTAL) {
finalXDraggingView = getFinalXHorizontalDirection(xVel);
if (finalXDraggingView == NO_POSITION) {
finalXDraggingView = getPreviousPosition();
}
}
if (dragHelper.settleCapturedViewAt(finalXDraggingView, draggedView.getTop())) {
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
}
};
private int clampLeftViewPosition(int left) {
if (isContinuousSwipe && isEmptyRightView()) {
if (isFreeHorizontalDrag) {
return left > horizontalWidth ? CLOSE_POSITION : Math.max(left, -horizontalWidth);
} else {
return left > CLOSE_POSITION ? CLOSE_POSITION : Math.max(left, -horizontalWidth);
}
}
if (isFreeDragAfterOpen) {
if (isFreeHorizontalDrag) {
return left > horizontalWidth ? CLOSE_POSITION
: Math.max(left, leftDragViewPadding - horizontalWidth);
}
return left > CLOSE_POSITION ? CLOSE_POSITION
: Math.max(left, leftDragViewPadding - horizontalWidth);
}
if (isFreeHorizontalDrag) {
return left > horizontalWidth ? CLOSE_POSITION : Math.max(left, -getRightViewWidth());
}
return left > CLOSE_POSITION ? CLOSE_POSITION : Math.max(left, -getRightViewWidth());
}
private int clampRightViewPosition(int left) {
if (isContinuousSwipe && isEmptyLeftView()) {
if (isFreeHorizontalDrag) {
return left < -horizontalWidth ? -horizontalWidth : Math.min(left, horizontalWidth);
} else {
return left < CLOSE_POSITION ? CLOSE_POSITION : Math.min(left, horizontalWidth);
}
}
if (isFreeDragAfterOpen) {
if (isFreeHorizontalDrag) {
return left < -horizontalWidth ? -horizontalWidth
: Math.min(left, horizontalWidth - rightDragViewPadding);
}
return left < CLOSE_POSITION ? CLOSE_POSITION
: Math.min(left, horizontalWidth - rightDragViewPadding);
}
if (isFreeHorizontalDrag) {
return left < -horizontalWidth ? -horizontalWidth : Math.min(left, getLeftViewWidth());
}
return left < CLOSE_POSITION ? CLOSE_POSITION : Math.min(left, getLeftViewWidth());
}
private int clampHorizontalViewPosition(int left, int dx) {
if (!isFreeHorizontalDrag && isLeftOpen && dx < 0) {
return Math.max(left, CLOSE_POSITION);
}
if (!isFreeHorizontalDrag && isRightOpen && dx > 0) {
return Math.min(left, CLOSE_POSITION);
}
if (!isFreeDragAfterOpen && left > CLOSE_POSITION) {
return Math.min(left, getLeftViewWidth());
}
if (!isFreeDragAfterOpen && left < CLOSE_POSITION) {
return Math.max(left, -getRightViewWidth());
}
return left < CLOSE_POSITION ? Math.max(left, leftDragViewPadding - horizontalWidth)
: Math.min(left, horizontalWidth - rightDragViewPadding);
}
private int getPreviousPosition() {
if (isLeftOpen) {
return getLeftViewWidth();
} else if (isRightOpen) {
return -getRightViewWidth();
} else {
return CLOSE_POSITION;
}
}
private int getFinalXLeftDirection(float xVel) {
if (isContinuousSwipe) {
if (isEmptyRightView()) {
if ((draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2)
|| xVel < -autoOpenSpeed) {
return -horizontalWidth;
}
return CLOSE_POSITION;
}
if (isContinuousSwipeToLeft(xVel)) {
return -horizontalWidth;
}
}
final boolean settleToOpen;
if (xVel > autoOpenSpeed) {
settleToOpen = false;
} else if (xVel < -autoOpenSpeed) {
settleToOpen = true;
} else if (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > getRightViewWidth() / 2) {
settleToOpen = true;
} else if (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) < getRightViewWidth() / 2) {
settleToOpen = false;
} else {
settleToOpen = false;
}
return settleToOpen ? -getRightViewWidth() : CLOSE_POSITION;
}
private int getFinalXRightDirection(float xVel) {
if (isContinuousSwipe) {
if (isEmptyLeftView()) {
if ((draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2)
|| xVel > autoOpenSpeed) {
return horizontalWidth;
}
return CLOSE_POSITION;
}
if (isContinuousSwipeToRight(xVel)) {
return horizontalWidth;
}
}
final boolean settleToOpen;
if (xVel > autoOpenSpeed) {
settleToOpen = true;
} else if (xVel < -autoOpenSpeed) {
settleToOpen = false;
} else if (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > getLeftViewWidth() / 2) {
settleToOpen = true;
} else if (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) < getLeftViewWidth() / 2) {
settleToOpen = false;
} else {
settleToOpen = false;
}
return settleToOpen ? getLeftViewWidth() : CLOSE_POSITION;
}
private int getFinalXHorizontalDirection(float xVel) {
if (isSwipeToOpenLeft(xVel)) {
return getLeftViewWidth();
} else if (isSwipeToOpenRight(xVel)) {
return -getRightViewWidth();
} else if (isSwipeToClose(xVel)) {
return CLOSE_POSITION;
}
return NO_POSITION;
}
private boolean isContinuousSwipeToRight(float xVel) {
return (xVel > autoOpenSpeed && Math.abs(draggingViewLeft) > getLeftViewWidth())
|| (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2);
}
private boolean isContinuousSwipeToLeft(float xVel) {
return (xVel < -autoOpenSpeed && Math.abs(draggingViewLeft) > getRightViewWidth())
|| (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2);
}
private boolean isSwipeToOpenRight(float xVel) {
if (xVel > 0) {
return false;
}
return (draggingViewLeft < CLOSE_POSITION && xVel < -autoOpenSpeed)
|| (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > getRightViewWidth() / 2);
}
private boolean isSwipeToOpenLeft(float xVel) {
if (xVel < 0) {
return false;
}
return (draggingViewLeft > CLOSE_POSITION && xVel > autoOpenSpeed)
|| (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > getLeftViewWidth() / 2);
}
private boolean isSwipeToClose(float xVel) {
return (draggingViewLeft >= CLOSE_POSITION && xVel < -autoOpenSpeed)
|| (draggingViewLeft <= CLOSE_POSITION && xVel > autoOpenSpeed)
|| (draggingViewLeft >= CLOSE_POSITION && Math.abs(draggingViewLeft) < getLeftViewWidth() / 2)
|| (draggingViewLeft <= CLOSE_POSITION && Math.abs(draggingViewLeft) < getRightViewWidth() / 2);
}
private void setupPost() {
if (isTogether) {
post(new Runnable() {
@Override
public void run() {
if (currentDirection == LEFT) {
staticRightView.setX(horizontalWidth);
} else if (currentDirection == RIGHT) {
staticLeftView.setX(-staticLeftView.getWidth());
} else if (currentDirection == HORIZONTAL) {
staticRightView.setX(horizontalWidth);
staticLeftView.setX(-staticLeftView.getWidth());
}
}
});
}
}
private boolean isViewTouchTarget(View view, Point point) {
return point.y >= view.getTop()
&& point.y < view.getBottom()
&& point.x >= view.getLeft()
&& point.y < view.getRight();
}
private View getNeededTouchView(MotionEvent event, ViewGroup rootView) {
if (rootView.onInterceptTouchEvent(event)) {
return rootView;
}
int count = rootView.getChildCount();
for (int i = 0; i < count; i++) {
View view = rootView.getChildAt(i);
if (!isViewGroup(view)) {
continue;
}
View neededScrollView = getNeededTouchView(event, (ViewGroup) view);
if (neededScrollView != null) {
return neededScrollView;
}
}
return null;
}
private boolean isViewGroup(View view) {
return view instanceof ViewGroup;
}
private boolean isSwipeViewTarget(MotionEvent event) {
final int[] swipeViewLocation = new int[2];
draggedView.getLocationOnScreen(swipeViewLocation);
final int upperLimit = swipeViewLocation[1] + draggedView.getMeasuredHeight();
final int lowerLimit = swipeViewLocation[1];
final int y = (int) event.getRawY();
return (y > lowerLimit && y < upperLimit);
}
private boolean isIdleAfterMoving(int state) {
return (currentDraggingState == ViewDragHelper.STATE_DRAGGING
|| currentDraggingState == ViewDragHelper.STATE_SETTLING)
&& state == ViewDragHelper.STATE_IDLE;
}
private boolean isDragIdle(int state) {
return state == ViewDragHelper.STATE_IDLE;
}
private boolean isRightViewOpen() {
return staticRightView != null && draggingViewLeft == -getRightViewWidth();
}
private boolean isLeftViewOpen() {
return staticLeftView != null && draggingViewLeft == getLeftViewWidth();
}
private boolean isRightOpenCompletely() {
return draggingViewLeft == -horizontalWidth;
}
private boolean isLeftOpenCompletely() {
return draggingViewLeft == horizontalWidth;
}
private int getLeftViewWidth() {
return staticLeftView.getWidth();
}
private int getRightViewWidth() {
return staticRightView.getWidth();
}
private boolean isEmptyLeftView() {
return staticLeftView == null;
}
private boolean isEmptyRightView() {
return staticRightView == null;
}
private void parametersAdjustment() {
if (isContinuousSwipe && currentDirection != HORIZONTAL) {
isFreeDragAfterOpen = true;
}
if (currentDirection == HORIZONTAL) {
rightDragViewPadding = 0;
leftDragViewPadding = 0;
}
}
private void moveTo(int x) {
dragHelper.smoothSlideViewTo(draggedView, x, draggedView.getTop());
ViewCompat.postInvalidateOnAnimation(this);
}
public interface SwipeActionsListener {
void onOpen(int direction, boolean isContinuous);
void onClose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment