19 |
20 | ## Особенности
21 | - Выполнение свайпа влево
22 | - Выполнение свайпа вправо
23 | - Выполнение свайпа и влево и вправо
24 | - Использование любого своего макета
25 | - Четыре режима свайпа, которые можно комбинировать между собой
26 |
27 | ## Интеграция
28 | **Вариант 1**
29 | Скопируйте файл attrs.xml в папку src/res/values и файл SwipeLayout.java в папку с исходным кодом вашего проекта из [gist](https://gist.github.com/zerobranch/64359efd499ed38f6996390e79a5eadc) и используйте.
30 |
31 | **Вариант 2**
32 |
33 | Добавьте в корневой build.gradle следующий репозиторий:
34 | ```groovy
35 | allprojects {
36 | repositories {
37 | ...
38 | maven { url 'https://jitpack.io' }
39 | }
40 | }
41 | ```
42 |
43 | Добавьте в build.gradle вашего модуля следующую зависимость:
44 | ```groovy
45 | dependencies {
46 | implementation 'com.github.zerobranch:SwipeLayout:1.3.0'
47 | }
48 | ```
49 |
50 | ## Как использовать ?
51 | Пример использования. Свайп влево
52 | ```xml
53 |
19 |
20 | ## Features
21 | - Executing the swipe to the left
22 | - Executing the swipe to the right
23 | - Executing the swipe and left and right
24 | - Using any your layouts
25 | - Four modes of swipe, which can be combined with each other
26 |
27 | ## Integration
28 | **Option 1**
29 | Copy the attrs.xml file to the src/res/values folder and the SwipeLayout.java file to the source code folder of your project from the [gist](https://gist.github.com/zerobranch/64359efd499ed38f6996390e79a5eadc) and to use.
30 |
31 | **Option 2**
32 |
33 | Add it in your root build.gradle at the end of repositories:
34 | ```groovy
35 | allprojects {
36 | repositories {
37 | ...
38 | maven { url 'https://jitpack.io' }
39 | }
40 | }
41 | ```
42 |
43 | Add the following dependency to your module's build.gradle:
44 | ```groovy
45 | dependencies {
46 | implementation 'com.github.zerobranch:SwipeLayout:1.3.1'
47 | }
48 | ```
49 |
50 | ## How to use ?
51 | Example of use. Swipe to the left
52 | ```xml
53 | 78 | * If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}), 79 | * and this flag is set, then {@link #isFreeDragAfterOpen} always will be true. 80 | *
81 | * If the left and right directions of the swipe are used simultaneously ({@link #HORIZONTAL}), 82 | * then this flag will be ignored 83 | */ 84 | private boolean isContinuousSwipe; 85 | 86 | /** 87 | * Moving the main view after it was open. 88 | *
89 | * if {@link #isEmptyLeftView()} or {@link #isEmptyRightView()}, 90 | * then this flag will be ignored 91 | */ 92 | private boolean isFreeDragAfterOpen; 93 | 94 | /** 95 | * If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}), 96 | * then this flag allows you to do the swipe in the opposite direction. 97 | *
98 | * If the horizontal direction is used ({@link #HORIZONTAL}), 99 | * this flag allows you to move the main view continuously in both directions 100 | */ 101 | private boolean isFreeHorizontalDrag; 102 | 103 | /** 104 | * The right bounding border of the swipe for the main view 105 | */ 106 | private int rightDragViewPadding; 107 | 108 | /** 109 | * The left bounding border of the swipe for the main view 110 | */ 111 | private int leftDragViewPadding; 112 | 113 | /** 114 | * Sensitivity of automatic closing of the main view 115 | */ 116 | private double autoOpenSpeed; 117 | 118 | /** 119 | * Disable intercept touch event for draggable view 120 | */ 121 | private boolean disallowIntercept; 122 | 123 | private int currentDraggingState = ViewDragHelper.STATE_IDLE; 124 | private ViewDragHelper dragHelper; 125 | private GestureDetectorCompat gestureDetector; 126 | private int draggingViewLeft; 127 | private int horizontalWidth; 128 | private boolean isLeftOpen; 129 | private boolean isRightOpen; 130 | private int staticRightViewId; 131 | private int staticLeftViewId; 132 | private int draggedViewId; 133 | private View draggedView; 134 | private View staticRightView; 135 | private View staticLeftView; 136 | private SwipeActionsListener actionsListener; 137 | 138 | public SwipeLayout(Context context, AttributeSet attrs) { 139 | super(context, attrs); 140 | isLeftOpen = false; 141 | isRightOpen = false; 142 | 143 | final TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SwipeLayout); 144 | currentDirection = typedArray.getInteger(R.styleable.SwipeLayout_swipeDirection, LEFT); 145 | isFreeDragAfterOpen = typedArray.getBoolean(R.styleable.SwipeLayout_isFreeDragAfterOpen, false); 146 | isFreeHorizontalDrag = typedArray.getBoolean(R.styleable.SwipeLayout_isFreeHorizontalDrag, false); 147 | isContinuousSwipe = typedArray.getBoolean(R.styleable.SwipeLayout_isContinuousSwipe, false); 148 | isTogether = typedArray.getBoolean(R.styleable.SwipeLayout_isTogether, false); 149 | isEnabledSwipe = typedArray.getBoolean(R.styleable.SwipeLayout_isEnabledSwipe, true); 150 | staticLeftViewId = typedArray.getResourceId(R.styleable.SwipeLayout_leftItem, 0); 151 | staticRightViewId = typedArray.getResourceId(R.styleable.SwipeLayout_rightItem, 0); 152 | draggedViewId = typedArray.getResourceId(R.styleable.SwipeLayout_draggedItem, 0); 153 | autoOpenSpeed = typedArray.getInt(R.styleable.SwipeLayout_autoMovingSensitivity, DEFAULT_AUTO_OPEN_SPEED); 154 | rightDragViewPadding = (int) typedArray.getDimension(R.styleable.SwipeLayout_rightDragViewPadding, 0); 155 | leftDragViewPadding = (int) typedArray.getDimension(R.styleable.SwipeLayout_leftDragViewPadding, 0); 156 | 157 | parametersAdjustment(); 158 | typedArray.recycle(); 159 | } 160 | 161 | @Override 162 | protected void onSizeChanged(int w, int h, int oldW, int oldH) { 163 | horizontalWidth = w; 164 | super.onSizeChanged(w, h, oldW, oldH); 165 | } 166 | 167 | @Override 168 | public boolean onInterceptTouchEvent(MotionEvent event) { 169 | if (disallowIntercept && isViewGroup(draggedView)) { 170 | final View neededScrollView = getNeededTouchView(event, (ViewGroup) draggedView); 171 | final Point touchPoint = new Point((int) event.getX(), (int) event.getY()); 172 | 173 | if (neededScrollView != null && isViewTouchTarget(neededScrollView, touchPoint)) { 174 | return false; 175 | } 176 | } 177 | 178 | return isSwipeViewTarget(event) && dragHelper.shouldInterceptTouchEvent(event); 179 | } 180 | 181 | @Override 182 | public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 183 | super.requestDisallowInterceptTouchEvent(disallowIntercept); 184 | this.disallowIntercept = disallowIntercept; 185 | } 186 | 187 | @Override 188 | protected void onFinishInflate() { 189 | if (draggedViewId != 0) { 190 | draggedView = findViewById(draggedViewId); 191 | } 192 | 193 | if (staticLeftViewId != 0) { 194 | staticLeftView = findViewById(staticLeftViewId); 195 | } 196 | 197 | if (staticRightViewId != 0) { 198 | staticRightView = findViewById(staticRightViewId); 199 | } 200 | 201 | if (draggedView == null) { 202 | throw new RuntimeException("'draggedItem' must be specified"); 203 | } else if (isTogether && currentDirection == LEFT && staticRightView == null) { 204 | throw new RuntimeException("If 'isTogether = true' 'rightItem' must be specified"); 205 | } else if (isTogether && currentDirection == RIGHT && staticLeftView == null) { 206 | throw new RuntimeException("If 'isTogether = true' 'leftItem' must be specified"); 207 | } else if (currentDirection == LEFT && !isContinuousSwipe && staticRightView == null) { 208 | throw new RuntimeException("Must be specified 'rightItem' or flag isContinuousSwipe = true"); 209 | } else if (currentDirection == RIGHT && !isContinuousSwipe && staticLeftView == null) { 210 | throw new RuntimeException("Must be specified 'leftItem' or flag isContinuousSwipe = true"); 211 | } else if (currentDirection == HORIZONTAL && (staticRightView == null || staticLeftView == null)) { 212 | throw new RuntimeException("'leftItem' and 'rightItem' must be specified"); 213 | } 214 | 215 | dragHelper = ViewDragHelper.create(this, 1.0f, dragHelperCallback); 216 | gestureDetector = new GestureDetectorCompat(getContext(), gestureDetectorCallBack); 217 | 218 | setupPost(); 219 | super.onFinishInflate(); 220 | } 221 | 222 | @Override 223 | public boolean onTouchEvent(MotionEvent event) { 224 | if (isSwipeViewTarget(event) || isMoving()) { 225 | gestureDetector.onTouchEvent(event); 226 | dragHelper.processTouchEvent(event); 227 | return true; 228 | } else { 229 | return super.onTouchEvent(event); 230 | } 231 | } 232 | 233 | @Override 234 | public void computeScroll() { 235 | if (dragHelper.continueSettling(true)) { 236 | ViewCompat.postInvalidateOnAnimation(this); 237 | } 238 | } 239 | 240 | /** 241 | * Is enabled Swipe 242 | * 243 | * @return True if swipe is enabled, false otherwise. 244 | */ 245 | public boolean isEnabledSwipe() { 246 | return isEnabledSwipe; 247 | } 248 | 249 | /** 250 | * Set the enabled swipe. 251 | * 252 | * @param enabledSwipe True if swipe is enabled, false otherwise. 253 | */ 254 | public void setEnabledSwipe(boolean enabledSwipe) { 255 | this.isEnabledSwipe = enabledSwipe; 256 | } 257 | 258 | /** 259 | * Performs manual swipe to the left 260 | * 261 | * @param animated - flag to animate opening 262 | */ 263 | public void openRight(boolean animated) { 264 | if (animated) { 265 | openRight(); 266 | } else if (isDragIdle(currentDraggingState) && ((currentDirection == LEFT && !isEmptyRightView()) 267 | || currentDirection == HORIZONTAL) && !isRightOpen) { 268 | if (isTogether) { 269 | staticRightView.offsetLeftAndRight(-1 * (isLeftOpen ? getRightViewWidth() * 2 : getRightViewWidth())); 270 | } 271 | 272 | draggedView.offsetLeftAndRight(-1 * (isLeftOpen ? getRightViewWidth() * 2 : getRightViewWidth())); 273 | draggingViewLeft -= (isLeftOpen ? getRightViewWidth() * 2 : getRightViewWidth()); 274 | updateState(); 275 | } 276 | } 277 | 278 | /** 279 | * Performs a full manual swipe to the left 280 | * 281 | * @param animated - flag to animate opening 282 | */ 283 | public void openRightCompletely(boolean animated) { 284 | if (animated) { 285 | openRightCompletely(); 286 | } else { 287 | if (isDragIdle(currentDraggingState) && currentDirection == LEFT) { 288 | if (isTogether) { 289 | staticRightView.offsetLeftAndRight(-horizontalWidth); 290 | } 291 | 292 | draggedView.offsetLeftAndRight(-horizontalWidth); 293 | draggingViewLeft -= horizontalWidth; 294 | updateState(); 295 | } 296 | } 297 | } 298 | 299 | /** 300 | * Performs manual swipe to the right 301 | * 302 | * @param animated - flag to animate opening 303 | */ 304 | public void openLeft(boolean animated) { 305 | if (animated) { 306 | openLeft(); 307 | } else if (isDragIdle(currentDraggingState) && ((currentDirection == RIGHT && !isEmptyLeftView()) 308 | || currentDirection == HORIZONTAL) && !isLeftOpen) { 309 | if (isTogether) { 310 | staticLeftView.offsetLeftAndRight((isRightOpen ? getLeftViewWidth() * 2 : getLeftViewWidth())); 311 | } 312 | 313 | draggedView.offsetLeftAndRight((isRightOpen ? getLeftViewWidth() * 2 : getLeftViewWidth())); 314 | draggingViewLeft += (isRightOpen ? getLeftViewWidth() * 2 : getLeftViewWidth()); 315 | updateState(); 316 | } 317 | } 318 | 319 | /** 320 | * Performs a full manual swipe to the right 321 | * 322 | * @param animated - flag to animate opening 323 | */ 324 | public void openLeftCompletely(boolean animated) { 325 | if (animated) { 326 | openRightCompletely(); 327 | } else { 328 | if (isDragIdle(currentDraggingState) && currentDirection == RIGHT) { 329 | if (isTogether) { 330 | staticRightView.offsetLeftAndRight(horizontalWidth); 331 | } 332 | 333 | draggedView.offsetLeftAndRight(horizontalWidth); 334 | draggingViewLeft += horizontalWidth; 335 | updateState(); 336 | } 337 | } 338 | } 339 | 340 | /** 341 | * Performs manual close 342 | * 343 | * @param animated - flag to animate closing 344 | */ 345 | public void close(boolean animated) { 346 | if (animated) { 347 | close(); 348 | } else { 349 | if (isTogether) { 350 | if (staticLeftView != null && currentDirection == RIGHT) { 351 | staticLeftView.layout(CLOSE_POSITION, staticLeftView.getTop(), 352 | staticLeftView.getWidth(), staticLeftView.getBottom()); 353 | } else if (staticRightView != null && currentDirection == LEFT) { 354 | staticRightView.layout(horizontalWidth - staticRightView.getWidth(), staticRightView.getTop(), 355 | horizontalWidth, staticRightView.getBottom()); 356 | } else if (currentDirection == HORIZONTAL && staticRightView != null && staticLeftView != null) { 357 | staticLeftView.layout(CLOSE_POSITION, staticLeftView.getTop(), 358 | staticLeftView.getWidth(), staticLeftView.getBottom()); 359 | staticRightView.layout(horizontalWidth - staticRightView.getWidth(), staticRightView.getTop(), 360 | horizontalWidth, staticRightView.getBottom()); 361 | } 362 | } 363 | 364 | draggedView.layout(CLOSE_POSITION, 365 | draggedView.getTop(), 366 | draggedView.getWidth(), 367 | draggedView.getBottom()); 368 | 369 | draggingViewLeft = CLOSE_POSITION; 370 | updateState(); 371 | } 372 | } 373 | 374 | /** 375 | * Performs manual swipe to the right 376 | */ 377 | public void openLeft() { 378 | if (isDragIdle(currentDraggingState) && ((currentDirection == RIGHT && !isEmptyLeftView()) 379 | || currentDirection == HORIZONTAL)) { 380 | moveTo(getLeftViewWidth()); 381 | } 382 | } 383 | 384 | /** 385 | * Performs manual swipe to the left 386 | */ 387 | public void openRight() { 388 | if (isDragIdle(currentDraggingState) && ((currentDirection == LEFT && !isEmptyRightView()) 389 | || currentDirection == HORIZONTAL)) { 390 | moveTo(-getRightViewWidth()); 391 | } 392 | } 393 | 394 | /** 395 | * Performs a full manual swipe to the right 396 | */ 397 | public void openLeftCompletely() { 398 | if (isDragIdle(currentDraggingState) && currentDirection == RIGHT) { 399 | moveTo(horizontalWidth); 400 | } 401 | } 402 | 403 | /** 404 | * Performs a full manual swipe to the left 405 | */ 406 | public void openRightCompletely() { 407 | if (isDragIdle(currentDraggingState) && currentDirection == LEFT) { 408 | moveTo(-horizontalWidth); 409 | } 410 | } 411 | 412 | /** 413 | * Performs manual close 414 | */ 415 | public void close() { 416 | moveTo(CLOSE_POSITION); 417 | } 418 | 419 | /** 420 | * Is moving main view 421 | */ 422 | public boolean isMoving() { 423 | return (currentDraggingState == ViewDragHelper.STATE_DRAGGING || 424 | currentDraggingState == ViewDragHelper.STATE_SETTLING); 425 | } 426 | 427 | /** 428 | * Is closed main view 429 | */ 430 | public boolean isClosed() { 431 | return draggingViewLeft == CLOSE_POSITION; 432 | } 433 | 434 | /** 435 | * Get current direction of a swipe 436 | */ 437 | public int getCurrentDirection() { 438 | return currentDirection; 439 | } 440 | 441 | /** 442 | * Set current direction of a swipe 443 | */ 444 | public SwipeLayout setCurrentDirection(int currentDirection) { 445 | this.currentDirection = currentDirection; 446 | return this; 447 | } 448 | 449 | /** 450 | * Is move the secondary view along with the main view 451 | */ 452 | public boolean isTogether() { 453 | return isTogether; 454 | } 455 | 456 | /** 457 | * The secondary view will move along with the main view 458 | */ 459 | public SwipeLayout setTogether(boolean together) { 460 | isTogether = together; 461 | return this; 462 | } 463 | 464 | /** 465 | * Swipe to the end of the screen. 466 | * Can work without a secondary view {@link #staticLeftView} and {@link #staticRightView} 467 | *
468 | * If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}), 469 | * and this flag is set, then {@link #isFreeDragAfterOpen} always will be true. 470 | *
471 | * If the left and right directions of the swipe are used simultaneously ({@link #HORIZONTAL}), 472 | * then this flag will be ignored 473 | */ 474 | public boolean isContinuousSwipe() { 475 | return isContinuousSwipe; 476 | } 477 | 478 | /** 479 | * Swipe to the end of the screen. 480 | * Can work without a secondary view {@link #staticLeftView} and {@link #staticRightView} 481 | *
482 | * If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}), 483 | * and this flag is set, then {@link #isFreeDragAfterOpen} always will be true. 484 | *
485 | * If the left and right directions of the swipe are used simultaneously ({@link #HORIZONTAL}), 486 | * then this flag will be ignored 487 | */ 488 | public SwipeLayout setContinuousSwipe(boolean continuousSwipe) { 489 | isContinuousSwipe = continuousSwipe; 490 | parametersAdjustment(); 491 | return this; 492 | } 493 | 494 | /** 495 | * Moving the main view after it was open. 496 | *
497 | * if {@link #isEmptyLeftView()} or {@link #isEmptyRightView()}, 498 | * then this flag will be ignored 499 | */ 500 | public boolean isFreeDragAfterOpen() { 501 | return isFreeDragAfterOpen; 502 | } 503 | 504 | /** 505 | * Moving the main view after it was open. 506 | *
507 | * if {@link #isEmptyLeftView()} or {@link #isEmptyRightView()}, 508 | * then this flag will be ignored 509 | */ 510 | public SwipeLayout setFreeDragAfterOpen(boolean freeDragAfterOpen) { 511 | isFreeDragAfterOpen = freeDragAfterOpen; 512 | parametersAdjustment(); 513 | return this; 514 | } 515 | 516 | /** 517 | * If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}), 518 | * then this flag allows you to do the swipe in the opposite direction. 519 | *
520 | * If the horizontal direction is used ({@link #HORIZONTAL}), 521 | * this flag allows you to move the main view continuously in both directions 522 | */ 523 | public boolean isFreeHorizontalDrag() { 524 | return isFreeHorizontalDrag; 525 | } 526 | 527 | /** 528 | * If a particular direction of the swipe is used ({@link #LEFT} or {@link #RIGHT}), 529 | * then this flag allows you to do the swipe in the opposite direction. 530 | *
531 | * If the horizontal direction is used ({@link #HORIZONTAL}), 532 | * this flag allows you to move the main view continuously in both directions 533 | */ 534 | public SwipeLayout setFreeHorizontalDrag(boolean freeHorizontalDrag) { 535 | isFreeHorizontalDrag = freeHorizontalDrag; 536 | return this; 537 | } 538 | 539 | /** 540 | * Is open right view 541 | */ 542 | public boolean isRightOpen() { 543 | return isRightOpen; 544 | } 545 | 546 | /** 547 | * Is open left view 548 | */ 549 | public boolean isLeftOpen() { 550 | return isLeftOpen; 551 | } 552 | 553 | /** 554 | * Set swipe actions listener 555 | */ 556 | public SwipeLayout setOnActionsListener(@Nullable SwipeActionsListener actionsListener) { 557 | this.actionsListener = actionsListener; 558 | return this; 559 | } 560 | 561 | /** 562 | * Get the right bounding border of the swipe for the main view 563 | */ 564 | public int getRightDragViewPadding() { 565 | return rightDragViewPadding; 566 | } 567 | 568 | /** 569 | * Set the right bounding border of the swipe for the main view 570 | */ 571 | public SwipeLayout setRightDragViewPadding(int minRightDragViewPadding) { 572 | this.rightDragViewPadding = minRightDragViewPadding; 573 | parametersAdjustment(); 574 | return this; 575 | } 576 | 577 | /** 578 | * Get the left bounding border of the swipe for the main view 579 | */ 580 | public int getLeftDragViewPadding() { 581 | return leftDragViewPadding; 582 | } 583 | 584 | /** 585 | * Set the left bounding border of the swipe for the main view 586 | */ 587 | public SwipeLayout setLeftDragViewPadding(int minLeftDragViewPadding) { 588 | this.leftDragViewPadding = minLeftDragViewPadding; 589 | parametersAdjustment(); 590 | return this; 591 | } 592 | 593 | /** 594 | * Enable touch for ViewGroup 595 | */ 596 | public void enableTouchForViewGroup(@NonNull final ViewGroup viewGroup) { 597 | viewGroup.setOnTouchListener(new OnTouchListener() { 598 | @Override 599 | public boolean onTouch(View v, MotionEvent event) { 600 | requestDisallowInterceptTouchEvent(true); 601 | return false; 602 | } 603 | }); 604 | } 605 | 606 | private void updateState() { 607 | if (isClosed()) { 608 | isLeftOpen = false; 609 | isRightOpen = false; 610 | 611 | if (actionsListener != null) { 612 | actionsListener.onClose(); 613 | } 614 | } else if (isLeftOpenCompletely() || isLeftViewOpen()) { 615 | isLeftOpen = true; 616 | isRightOpen = false; 617 | 618 | if (actionsListener != null) { 619 | actionsListener.onOpen(RIGHT, isLeftOpenCompletely()); 620 | } 621 | } else if (isRightOpenCompletely() || isRightViewOpen()) { 622 | isLeftOpen = false; 623 | isRightOpen = true; 624 | 625 | if (actionsListener != null) { 626 | actionsListener.onOpen(LEFT, isRightOpenCompletely()); 627 | } 628 | } 629 | } 630 | 631 | private GestureDetector.OnGestureListener gestureDetectorCallBack = new GestureDetector.SimpleOnGestureListener() { 632 | @Override 633 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 634 | if (getParent() != null) { 635 | getParent().requestDisallowInterceptTouchEvent(true); 636 | } 637 | return false; 638 | } 639 | }; 640 | 641 | private final ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() { 642 | @Override 643 | public void onViewDragStateChanged(int state) { 644 | if (state == currentDraggingState) 645 | return; 646 | 647 | if (isIdleAfterMoving(state)) { 648 | updateState(); 649 | } 650 | 651 | currentDraggingState = state; 652 | } 653 | 654 | @Override 655 | public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) { 656 | draggingViewLeft = left; 657 | 658 | if (isTogether) { 659 | if (currentDirection == LEFT) { 660 | staticRightView.offsetLeftAndRight(dx); 661 | } else if (currentDirection == RIGHT) { 662 | staticLeftView.offsetLeftAndRight(dx); 663 | } else if (currentDirection == HORIZONTAL) { 664 | staticLeftView.offsetLeftAndRight(dx); 665 | staticRightView.offsetLeftAndRight(dx); 666 | } 667 | } 668 | } 669 | 670 | @Override 671 | public int getViewHorizontalDragRange(@NonNull View child) { 672 | return horizontalWidth; 673 | } 674 | 675 | @Override 676 | public boolean tryCaptureView(@NonNull View view, int pointerId) { 677 | return view.getId() == draggedView.getId(); 678 | } 679 | 680 | @Override 681 | public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) { 682 | if (!isEnabledSwipe) { 683 | return CLOSE_POSITION; 684 | } 685 | 686 | switch (currentDirection) { 687 | case LEFT: 688 | return clampLeftViewPosition(left); 689 | case RIGHT: 690 | return clampRightViewPosition(left); 691 | case HORIZONTAL: 692 | return clampHorizontalViewPosition(left, dx); 693 | default: 694 | return CLOSE_POSITION; 695 | } 696 | } 697 | 698 | @Override 699 | public void onViewReleased(@NonNull View releasedChild, float xVel, float yVel) { 700 | int finalXDraggingView = CLOSE_POSITION; 701 | 702 | if (currentDirection == LEFT) { 703 | finalXDraggingView = getFinalXLeftDirection(xVel); 704 | } else if (currentDirection == RIGHT) { 705 | finalXDraggingView = getFinalXRightDirection(xVel); 706 | } else if (currentDirection == HORIZONTAL) { 707 | finalXDraggingView = getFinalXHorizontalDirection(xVel); 708 | 709 | if (finalXDraggingView == NO_POSITION) { 710 | finalXDraggingView = getPreviousPosition(); 711 | } 712 | } 713 | 714 | if (dragHelper.settleCapturedViewAt(finalXDraggingView, draggedView.getTop())) { 715 | ViewCompat.postInvalidateOnAnimation(SwipeLayout.this); 716 | } 717 | } 718 | }; 719 | 720 | private int clampLeftViewPosition(int left) { 721 | if (isContinuousSwipe && isEmptyRightView()) { 722 | if (isFreeHorizontalDrag) { 723 | return left > horizontalWidth ? CLOSE_POSITION : Math.max(left, -horizontalWidth); 724 | } else { 725 | return left > CLOSE_POSITION ? CLOSE_POSITION : Math.max(left, -horizontalWidth); 726 | } 727 | } 728 | 729 | if (isFreeDragAfterOpen) { 730 | if (isFreeHorizontalDrag) { 731 | return left > horizontalWidth ? CLOSE_POSITION 732 | : Math.max(left, leftDragViewPadding - horizontalWidth); 733 | } 734 | return left > CLOSE_POSITION ? CLOSE_POSITION 735 | : Math.max(left, leftDragViewPadding - horizontalWidth); 736 | } 737 | 738 | if (isFreeHorizontalDrag) { 739 | return left > horizontalWidth ? CLOSE_POSITION : Math.max(left, -getRightViewWidth()); 740 | } 741 | return left > CLOSE_POSITION ? CLOSE_POSITION : Math.max(left, -getRightViewWidth()); 742 | } 743 | 744 | private int clampRightViewPosition(int left) { 745 | if (isContinuousSwipe && isEmptyLeftView()) { 746 | if (isFreeHorizontalDrag) { 747 | return left < -horizontalWidth ? -horizontalWidth : Math.min(left, horizontalWidth); 748 | } else { 749 | return left < CLOSE_POSITION ? CLOSE_POSITION : Math.min(left, horizontalWidth); 750 | } 751 | } 752 | 753 | if (isFreeDragAfterOpen) { 754 | if (isFreeHorizontalDrag) { 755 | return left < -horizontalWidth ? -horizontalWidth 756 | : Math.min(left, horizontalWidth - rightDragViewPadding); 757 | } 758 | return left < CLOSE_POSITION ? CLOSE_POSITION 759 | : Math.min(left, horizontalWidth - rightDragViewPadding); 760 | } 761 | 762 | if (isFreeHorizontalDrag) { 763 | return left < -horizontalWidth ? -horizontalWidth : Math.min(left, getLeftViewWidth()); 764 | } 765 | return left < CLOSE_POSITION ? CLOSE_POSITION : Math.min(left, getLeftViewWidth()); 766 | } 767 | 768 | private int clampHorizontalViewPosition(int left, int dx) { 769 | if (!isFreeHorizontalDrag && isLeftOpen && dx < 0) { 770 | return Math.max(left, CLOSE_POSITION); 771 | } 772 | 773 | if (!isFreeHorizontalDrag && isRightOpen && dx > 0) { 774 | return Math.min(left, CLOSE_POSITION); 775 | } 776 | 777 | if (!isFreeDragAfterOpen && left > CLOSE_POSITION) { 778 | return Math.min(left, getLeftViewWidth()); 779 | } 780 | 781 | if (!isFreeDragAfterOpen && left < CLOSE_POSITION) { 782 | return Math.max(left, -getRightViewWidth()); 783 | } 784 | 785 | return left < CLOSE_POSITION ? Math.max(left, leftDragViewPadding - horizontalWidth) 786 | : Math.min(left, horizontalWidth - rightDragViewPadding); 787 | } 788 | 789 | private int getPreviousPosition() { 790 | if (isLeftOpen) { 791 | return getLeftViewWidth(); 792 | } else if (isRightOpen) { 793 | return -getRightViewWidth(); 794 | } else { 795 | return CLOSE_POSITION; 796 | } 797 | } 798 | 799 | private int getFinalXLeftDirection(float xVel) { 800 | if (isContinuousSwipe) { 801 | if (isEmptyRightView()) { 802 | if ((draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2) 803 | || xVel < -autoOpenSpeed) { 804 | return -horizontalWidth; 805 | } 806 | return CLOSE_POSITION; 807 | } 808 | 809 | if (isContinuousSwipeToLeft(xVel)) { 810 | return -horizontalWidth; 811 | } 812 | } 813 | 814 | final boolean settleToOpen; 815 | if (xVel > autoOpenSpeed) { 816 | settleToOpen = false; 817 | } else if (xVel < -autoOpenSpeed) { 818 | settleToOpen = true; 819 | } else if (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > getRightViewWidth() / 2) { 820 | settleToOpen = true; 821 | } else if (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) < getRightViewWidth() / 2) { 822 | settleToOpen = false; 823 | } else { 824 | settleToOpen = false; 825 | } 826 | return settleToOpen ? -getRightViewWidth() : CLOSE_POSITION; 827 | } 828 | 829 | private int getFinalXRightDirection(float xVel) { 830 | if (isContinuousSwipe) { 831 | if (isEmptyLeftView()) { 832 | if ((draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2) 833 | || xVel > autoOpenSpeed) { 834 | return horizontalWidth; 835 | } 836 | return CLOSE_POSITION; 837 | } 838 | 839 | if (isContinuousSwipeToRight(xVel)) { 840 | return horizontalWidth; 841 | } 842 | } 843 | 844 | final boolean settleToOpen; 845 | if (xVel > autoOpenSpeed) { 846 | settleToOpen = true; 847 | } else if (xVel < -autoOpenSpeed) { 848 | settleToOpen = false; 849 | } else if (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > getLeftViewWidth() / 2) { 850 | settleToOpen = true; 851 | } else if (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) < getLeftViewWidth() / 2) { 852 | settleToOpen = false; 853 | } else { 854 | settleToOpen = false; 855 | } 856 | return settleToOpen ? getLeftViewWidth() : CLOSE_POSITION; 857 | } 858 | 859 | private int getFinalXHorizontalDirection(float xVel) { 860 | if (isSwipeToOpenLeft(xVel)) { 861 | return getLeftViewWidth(); 862 | } else if (isSwipeToOpenRight(xVel)) { 863 | return -getRightViewWidth(); 864 | } else if (isSwipeToClose(xVel)) { 865 | return CLOSE_POSITION; 866 | } 867 | 868 | return NO_POSITION; 869 | } 870 | 871 | private boolean isContinuousSwipeToRight(float xVel) { 872 | return (xVel > autoOpenSpeed && Math.abs(draggingViewLeft) > getLeftViewWidth()) 873 | || (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2); 874 | 875 | } 876 | 877 | private boolean isContinuousSwipeToLeft(float xVel) { 878 | return (xVel < -autoOpenSpeed && Math.abs(draggingViewLeft) > getRightViewWidth()) 879 | || (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > horizontalWidth / 2); 880 | } 881 | 882 | private boolean isSwipeToOpenRight(float xVel) { 883 | if (xVel > 0) { 884 | return false; 885 | } 886 | return (draggingViewLeft < CLOSE_POSITION && xVel < -autoOpenSpeed) 887 | || (draggingViewLeft < CLOSE_POSITION && Math.abs(draggingViewLeft) > getRightViewWidth() / 2); 888 | } 889 | 890 | private boolean isSwipeToOpenLeft(float xVel) { 891 | if (xVel < 0) { 892 | return false; 893 | } 894 | return (draggingViewLeft > CLOSE_POSITION && xVel > autoOpenSpeed) 895 | || (draggingViewLeft > CLOSE_POSITION && Math.abs(draggingViewLeft) > getLeftViewWidth() / 2); 896 | } 897 | 898 | private boolean isSwipeToClose(float xVel) { 899 | return (draggingViewLeft >= CLOSE_POSITION && xVel < -autoOpenSpeed) 900 | || (draggingViewLeft <= CLOSE_POSITION && xVel > autoOpenSpeed) 901 | || (draggingViewLeft >= CLOSE_POSITION && Math.abs(draggingViewLeft) < getLeftViewWidth() / 2) 902 | || (draggingViewLeft <= CLOSE_POSITION && Math.abs(draggingViewLeft) < getRightViewWidth() / 2); 903 | } 904 | 905 | private void setupPost() { 906 | if (isTogether) { 907 | post(new Runnable() { 908 | @Override 909 | public void run() { 910 | if (currentDirection == LEFT) { 911 | staticRightView.setX(horizontalWidth); 912 | } else if (currentDirection == RIGHT) { 913 | staticLeftView.setX(-staticLeftView.getWidth()); 914 | } else if (currentDirection == HORIZONTAL) { 915 | staticRightView.setX(horizontalWidth); 916 | staticLeftView.setX(-staticLeftView.getWidth()); 917 | } 918 | } 919 | }); 920 | } 921 | } 922 | 923 | private boolean isViewTouchTarget(View view, Point point) { 924 | return point.y >= view.getTop() 925 | && point.y < view.getBottom() 926 | && point.x >= view.getLeft() 927 | && point.y < view.getRight(); 928 | } 929 | 930 | private View getNeededTouchView(MotionEvent event, ViewGroup rootView) { 931 | if (rootView.onInterceptTouchEvent(event)) { 932 | return rootView; 933 | } 934 | 935 | int count = rootView.getChildCount(); 936 | 937 | for (int i = 0; i < count; i++) { 938 | View view = rootView.getChildAt(i); 939 | 940 | if (!isViewGroup(view)) { 941 | continue; 942 | } 943 | 944 | View neededScrollView = getNeededTouchView(event, (ViewGroup) view); 945 | 946 | if (neededScrollView != null) { 947 | return neededScrollView; 948 | } 949 | } 950 | 951 | return null; 952 | } 953 | 954 | private boolean isViewGroup(View view) { 955 | return view instanceof ViewGroup; 956 | } 957 | 958 | private boolean isSwipeViewTarget(MotionEvent event) { 959 | final int[] swipeViewLocation = new int[2]; 960 | draggedView.getLocationOnScreen(swipeViewLocation); 961 | final int upperLimit = swipeViewLocation[1] + draggedView.getMeasuredHeight(); 962 | final int lowerLimit = swipeViewLocation[1]; 963 | final int y = (int) event.getRawY(); 964 | return (y > lowerLimit && y < upperLimit); 965 | } 966 | 967 | private boolean isIdleAfterMoving(int state) { 968 | return (currentDraggingState == ViewDragHelper.STATE_DRAGGING 969 | || currentDraggingState == ViewDragHelper.STATE_SETTLING) 970 | && state == ViewDragHelper.STATE_IDLE; 971 | } 972 | 973 | private boolean isDragIdle(int state) { 974 | return state == ViewDragHelper.STATE_IDLE; 975 | } 976 | 977 | private boolean isRightViewOpen() { 978 | return staticRightView != null && draggingViewLeft == -getRightViewWidth(); 979 | } 980 | 981 | private boolean isLeftViewOpen() { 982 | return staticLeftView != null && draggingViewLeft == getLeftViewWidth(); 983 | } 984 | 985 | private boolean isRightOpenCompletely() { 986 | return draggingViewLeft == -horizontalWidth; 987 | } 988 | 989 | private boolean isLeftOpenCompletely() { 990 | return draggingViewLeft == horizontalWidth; 991 | } 992 | 993 | private int getLeftViewWidth() { 994 | return staticLeftView.getWidth(); 995 | } 996 | 997 | private int getRightViewWidth() { 998 | return staticRightView.getWidth(); 999 | } 1000 | 1001 | private boolean isEmptyLeftView() { 1002 | return staticLeftView == null; 1003 | } 1004 | 1005 | private boolean isEmptyRightView() { 1006 | return staticRightView == null; 1007 | } 1008 | 1009 | private void parametersAdjustment() { 1010 | if (isContinuousSwipe && currentDirection != HORIZONTAL) { 1011 | isFreeDragAfterOpen = true; 1012 | } 1013 | 1014 | if (currentDirection == HORIZONTAL) { 1015 | rightDragViewPadding = 0; 1016 | leftDragViewPadding = 0; 1017 | } 1018 | } 1019 | 1020 | private void moveTo(int x) { 1021 | dragHelper.smoothSlideViewTo(draggedView, x, draggedView.getTop()); 1022 | ViewCompat.postInvalidateOnAnimation(this); 1023 | } 1024 | 1025 | public interface SwipeActionsListener { 1026 | void onOpen(int direction, boolean isContinuous); 1027 | void onClose(); 1028 | } 1029 | } 1030 | --------------------------------------------------------------------------------