Skip to content

Commit 93521aa

Browse files
authored
Add a way to animate swiping from code (#15)
1 parent 0bb1645 commit 93521aa

File tree

5 files changed

+141
-54
lines changed

5 files changed

+141
-54
lines changed

README.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SwipeActionView is a swipe-able view, which allows users to perform actions by s
1919
- [Disabling gestures](#disabling-gestures)
2020
- [Ripple animations](#ripple-animations)
2121
- [Click listeners](#click-listeners)
22+
- [Animate from code](#code-animation)
2223
- [Attributes](#attr)
2324
- [sav_rippleTakesPadding](#attr-rippleTakesPadding)
2425
- [sav_swipeLeftRippleColor](#attr-swipeLeftRippleColor)
@@ -170,7 +171,7 @@ In order to be able to perform actions when users swipe the `SwipeActionView` yo
170171

171172
Each of these methods returns `Boolean` as a result. Most of the time you'll want to return `true` here. Returning `false` is designed for advanced usage. By doing so, the view won't be automatically animated to the original position but will stay at the full translation. This allows you to manipulate the content of the visible background view. One great example of this is displaying progress wheel and manually returning the view to the original position once some long action finishes execution.
172173

173-
To return the view to its original position you can call the `moveToOriginalPosition()` method at any time.
174+
To return the view to its original position you can call the `animateToOriginalPosition()` method at any time.
174175

175176
```java
176177
swipeView.setSwipeGestureListener(new SwipeGestureListener() {
@@ -186,12 +187,12 @@ swipeView.setSwipeGestureListener(new SwipeGestureListener() {
186187
public boolean onSwipedRight(@NonNull SwipeActionView swipeActionView) {
187188
showToast("Swiped right");
188189

189-
// Returning false requires calling moveToOriginalPosition() manually to
190+
// Returning false requires calling animateToOriginalPosition() manually to
190191
// reset view to original position
191192

192193
// Here we are using optional parameter which will move our view to
193194
// original position after delay in ms. In this case it's 2s.
194-
swipeActionView.moveToOriginalPosition(2000);
195+
swipeActionView.animateToOriginalPosition(2000);
195196

196197
return false;
197198
}
@@ -203,7 +204,6 @@ If you want to dynamically enable or disable gesture in a specific direction you
203204

204205
**Note:** The gesture enabling part is controlled by presence and visibility of background views. This method is only provided for convenience since it can also be changed by switching visibility of background views. It was coded like this so the specific swipe directions can be disabled by default from XML using `visibility` attribute on views corresponding to these directions.
205206

206-
207207
```java
208208
swipeView.setDirectionEnabled(SwipeDirection.Left, false);
209209
```
@@ -222,6 +222,29 @@ swipeView.setRippleColor(SwipeDirection.Right, Color.BLUE);
222222

223223
The only exception is that you shouldn't add click listeners for background views. This library wasn't designed to add support for this behavior. If it's possible then, that's only a positive side effect. You are better of with using libraries such as [AndroidSwipeLayout] instead.
224224

225+
# <a id="code-animation">Animate from code</a>
226+
When needed you can manually animate the view to desired position from code by using one of the dedicated methods.
227+
228+
Method `animateInDirection(swipeDirection: SwipeDirection, animateBack: Boolean, delayBeforeAnimatingBack: Long)` animates the view in the specified direction and optionally animates it back after a delay.
229+
230+
```java
231+
// Animate the view to the right and then animate it back after 1s
232+
swipeView.animateInDirection(SwipeDirection.Right, true, 1000);
233+
234+
// Animate the view to the left and do not animate it back
235+
swipeView.animateInDirection(SwipeDirection.Left, false);
236+
```
237+
238+
Method `animateToOriginalPosition(startDelay: Long)` animates the view to its original position with optional start delay.
239+
240+
```java
241+
// Wait 500ms and then animate the view to its original position
242+
swipeView.animateToOriginalPosition(500)
243+
244+
// Instantly animate the view to its original position
245+
swipeView.animateToOriginalPosition()
246+
```
247+
225248
# <a id="attr">Attributes</a>
226249

227250
#### <a id="attr-rippleTakesPadding">`app:sav_rippleTakesPadding="true|false"`</a>
@@ -256,7 +279,7 @@ He also created `SwipeRippleDrawable` and allowed me to [reimplement][SwipeRippl
256279

257280
# <a id="license">License</a>
258281
```
259-
Copyright © 2016-2018 Łukasz Rutkowski
282+
Copyright © 2016-2019 Łukasz Rutkowski
260283
261284
Licensed under the Apache License, Version 2.0 (the "License");
262285
you may not use this file except in compliance with the License.

library/src/main/kotlin/me/thanel/swipeactionview/SwipeActionView.kt

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import me.thanel.swipeactionview.utils.totalWidth
4848
/**
4949
* View that allows users to perform various actions by swiping it to the left or right sides.
5050
*/
51+
@Suppress("MemberVisibilityCanBePrivate")
5152
class SwipeActionView : FrameLayout {
5253
/**
5354
* Distance from edges in pixels that prevents starting of drag movement.
@@ -114,6 +115,11 @@ class SwipeActionView : FrameLayout {
114115
*/
115116
private val rippleAnimationDuration = 400L
116117

118+
/**
119+
* The duration of swipe animation.
120+
*/
121+
private val swipeAnimationDuration = 250L
122+
117123
/**
118124
* Bounds for the ripple animations.
119125
*/
@@ -262,13 +268,13 @@ class SwipeActionView : FrameLayout {
262268
val swipeRightRippleColor =
263269
typedArray.getColorStateList(R.styleable.SwipeActionView_sav_swipeRightRippleColor)
264270
rippleTakesPadding =
265-
typedArray.getBoolean(R.styleable.SwipeActionView_sav_rippleTakesPadding, false)
271+
typedArray.getBoolean(R.styleable.SwipeActionView_sav_rippleTakesPadding, false)
266272

267273
if (isInEditMode) {
268274
previewBackground =
269-
typedArray.getInt(R.styleable.SwipeActionView_sav_tools_previewBackground, 0)
275+
typedArray.getInt(R.styleable.SwipeActionView_sav_tools_previewBackground, 0)
270276
previewRipple =
271-
typedArray.getInt(R.styleable.SwipeActionView_sav_tools_previewRipple, 0)
277+
typedArray.getInt(R.styleable.SwipeActionView_sav_tools_previewRipple, 0)
272278
}
273279

274280
typedArray.recycle()
@@ -435,25 +441,54 @@ class SwipeActionView : FrameLayout {
435441
view.visibility = if (enabled) View.VISIBLE else View.GONE
436442
}
437443

438-
/**
439-
* Move the view to its original position.
440-
*/
441-
fun moveToOriginalPosition() {
442-
moveToOriginalPosition(0)
444+
@JvmOverloads
445+
@Deprecated(
446+
message = "use animateToOriginalPosition instead",
447+
replaceWith = ReplaceWith("animateToOriginalPosition(startDelay)")
448+
)
449+
fun moveToOriginalPosition(startDelay: Long = 0) {
450+
animateToOriginalPosition(startDelay)
443451
}
444452

445453
/**
446-
* Move the view to its original position.
454+
* Animate the view to its original position.
447455
*
448-
* @param startDelay
449-
* The amount of delay, in milliseconds, to wait before starting the movement animation.
456+
* @param startDelay The amount of delay, in milliseconds, to wait before starting the
457+
* movement animation. (Defaults to 0)
450458
*/
451-
fun moveToOriginalPosition(startDelay: Long) {
452-
animateContainer(0f, 350, startDelay) {
459+
@JvmOverloads
460+
fun animateToOriginalPosition(startDelay: Long = 0) {
461+
animateContainer(0f, swipeAnimationDuration, startDelay) {
453462
canPerformSwipeAction = true
454463
}
455464
}
456465

466+
/**
467+
* Animate the view in the specified [swipeDirection].
468+
*
469+
* @param animateBack Tells whether the view should be automatically swiped back to original
470+
* position after initial animation finishes.
471+
* @param delayBeforeAnimatingBack Delay in milliseconds before the view animates back to its
472+
* original position. Has no effect if [animateBack] is set to `false`. (Defaults to 200ms)
473+
*/
474+
@JvmOverloads
475+
fun animateInDirection(
476+
swipeDirection: SwipeDirection,
477+
animateBack: Boolean,
478+
delayBeforeAnimatingBack: Long = 200
479+
) {
480+
val distance = if (swipeDirection == SwipeDirection.Right) {
481+
maxRightSwipeDistance
482+
} else {
483+
-maxLeftSwipeDistance
484+
}
485+
animateContainer(distance, swipeAnimationDuration, 0) {
486+
if (animateBack) {
487+
animateToOriginalPosition(delayBeforeAnimatingBack)
488+
}
489+
}
490+
}
491+
457492
/**
458493
* Set ripple color for the specified swipe direction. Use -1 to disable ripple.
459494
*
@@ -484,7 +519,7 @@ class SwipeActionView : FrameLayout {
484519
MotionEvent.ACTION_UP,
485520
MotionEvent.ACTION_CANCEL -> {
486521
cancelDrag()
487-
moveToOriginalPosition()
522+
animateToOriginalPosition()
488523
}
489524
}
490525

@@ -523,7 +558,7 @@ class SwipeActionView : FrameLayout {
523558

524559
MotionEvent.ACTION_CANCEL -> {
525560
cancelDrag()
526-
moveToOriginalPosition()
561+
animateToOriginalPosition()
527562
}
528563
}
529564

@@ -738,14 +773,14 @@ class SwipeActionView : FrameLayout {
738773
val swipedFastEnough = Math.abs(velocityTracker.xVelocity) > minActivationSpeed
739774

740775
if (swipedFastEnough && !isValidDelta(velocityTracker.xVelocity)) {
741-
moveToOriginalPosition()
776+
animateToOriginalPosition()
742777
return
743778
}
744779

745780
if (hasSwipedFarEnough(container.translationX) || swipedFastEnough) {
746781
activate(container.translationX > 0)
747782
} else {
748-
moveToOriginalPosition()
783+
animateToOriginalPosition()
749784
}
750785
}
751786

@@ -774,7 +809,7 @@ class SwipeActionView : FrameLayout {
774809
// If activation animation didn't finish, move the view to original position without
775810
// executing activate callback.
776811
if (!canPerformSwipeAction) {
777-
moveToOriginalPosition()
812+
animateToOriginalPosition()
778813
return
779814
}
780815
canPerformSwipeAction = false
@@ -785,15 +820,16 @@ class SwipeActionView : FrameLayout {
785820
leftSwipeRipple.restart()
786821
}
787822

788-
animateContainer(if (swipedRight) maxRightSwipeDistance else -maxLeftSwipeDistance, 250) {
823+
val targetTranslationX = if (swipedRight) maxRightSwipeDistance else -maxLeftSwipeDistance
824+
animateContainer(targetTranslationX, swipeAnimationDuration) {
789825
val shouldFinish = if (swipedRight) {
790826
swipeGestureListener?.onSwipedRight(this)
791827
} else {
792828
swipeGestureListener?.onSwipedLeft(this)
793829
}
794830

795831
if (shouldFinish != false) {
796-
moveToOriginalPosition(200)
832+
animateToOriginalPosition(200)
797833
}
798834
}
799835
}

sample/src/main/java/me/thanel/swipeactionview/sample/MainActivity.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@
1717
package me.thanel.swipeactionview.sample;
1818

1919
import android.os.Bundle;
20-
import androidx.annotation.NonNull;
21-
import androidx.appcompat.app.AppCompatActivity;
2220
import android.view.View;
23-
import android.widget.Button;
2421
import android.widget.Toast;
2522

23+
import androidx.annotation.NonNull;
24+
import androidx.appcompat.app.AppCompatActivity;
2625
import me.thanel.swipeactionview.SwipeActionView;
26+
import me.thanel.swipeactionview.SwipeDirection;
2727
import me.thanel.swipeactionview.SwipeGestureListener;
2828

2929
public class MainActivity extends AppCompatActivity {
3030

31+
private SwipeActionView swipeCustomLayout;
32+
3133
@Override
3234
protected void onCreate(final Bundle savedInstanceState) {
3335
super.onCreate(savedInstanceState);
@@ -65,35 +67,35 @@ public boolean onSwipedRight(@NonNull SwipeActionView swipeActionView) {
6567
SwipeGestureListener delayedSwipeGestureListener = new SwipeGestureListener() {
6668
@Override
6769
public boolean onSwipedLeft(@NonNull SwipeActionView swipeActionView) {
68-
swipeActionView.moveToOriginalPosition(1000);
70+
swipeActionView.animateToOriginalPosition(1000);
6971
return false;
7072
}
7173

7274
@Override
7375
public boolean onSwipedRight(@NonNull SwipeActionView swipeActionView) {
74-
swipeActionView.moveToOriginalPosition(500);
76+
swipeActionView.animateToOriginalPosition(500);
7577
return false;
7678
}
7779
};
7880

7981
SwipeActionView swipeDelayed = findViewById(R.id.swipe_delayed);
8082
swipeDelayed.setSwipeGestureListener(delayedSwipeGestureListener);
8183

82-
SwipeActionView swipeCustomLayout = findViewById(R.id.swipe_layout);
84+
swipeCustomLayout = findViewById(R.id.swipe_layout);
8385
swipeCustomLayout.setSwipeGestureListener(swipeGestureListener);
84-
85-
Button button = findViewById(R.id.button);
86-
button.setOnClickListener(new View.OnClickListener() {
87-
@Override
88-
public void onClick(View v) {
89-
Toast.makeText(MainActivity.this, R.string.clicked, Toast.LENGTH_SHORT).show();
90-
}
91-
});
9286
}
9387

9488
private void showToast(Boolean swipedRight) {
9589
int resId = swipedRight ? R.string.swiped_right : R.string.swiped_left;
9690

9791
Toast.makeText(this, resId, Toast.LENGTH_SHORT).show();
9892
}
93+
94+
public void swipeLeft(View view) {
95+
swipeCustomLayout.animateInDirection(SwipeDirection.Left, true);
96+
}
97+
98+
public void swipeRight(View view) {
99+
swipeCustomLayout.animateInDirection(SwipeDirection.Right, true);
100+
}
99101
}

sample/src/main/res/layout/activity_main.xml

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,29 @@
168168
android:layout_height="wrap_content"
169169
android:text="@string/full_control" />
170170

171-
<Button
172-
android:id="@+id/button"
171+
172+
<LinearLayout
173173
android:layout_width="match_parent"
174174
android:layout_height="wrap_content"
175-
android:text="@string/click_me" />
175+
android:orientation="horizontal">
176+
177+
<Button
178+
android:id="@+id/swipe_left_button"
179+
android:layout_width="match_parent"
180+
android:layout_height="wrap_content"
181+
android:layout_weight="1"
182+
android:onClick="swipeLeft"
183+
android:text="@string/swipe_left" />
184+
185+
<Button
186+
android:id="@+id/swipe_right_button"
187+
android:layout_width="match_parent"
188+
android:layout_height="wrap_content"
189+
android:layout_weight="1"
190+
android:onClick="swipeRight"
191+
android:text="@string/swipe_right" />
192+
193+
</LinearLayout>
176194
</LinearLayout>
177195
</LinearLayout>
178196
</me.thanel.swipeactionview.SwipeActionView>
Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
12
<!--
23
~ Copyright © 2016-2018 Łukasz Rutkowski
34
~
@@ -12,21 +13,28 @@
1213
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1314
~ See the License for the specific language governing permissions and
1415
~ limitations under the License.
15-
-->
16+
--><resources>
17+
<string name="advanced_layout">Advanced layout</string>
18+
19+
<string name="animated_ripple">Displays animated ripple when swiped</string>
1620

17-
<resources>
1821
<string name="app_name">SwipeActionView</string>
19-
<string name="can_swipe_right">Can swipe right</string>
20-
<string name="can_swipe_left">Can swipe left</string>
22+
23+
<string name="can_be_customized">This can also be customized</string>
2124
<string name="can_swipe_in_both_directions">Can swipe in both directions</string>
25+
<string name="can_swipe_left">Can swipe left</string>
26+
<string name="can_swipe_right">Can swipe right</string>
27+
28+
<string name="clicked">Clicked</string>
29+
30+
<string name="full_control">You have full control over look of SwipeActionView</string>
31+
32+
<string name="returns_after_delay">Returns to original position after delay</string>
33+
34+
<string name="swipe_able_card_view">Swipe-able CardView</string>
35+
<string name="swipe_left">Swipe left</string>
36+
<string name="swipe_right">Swipe right</string>
37+
2238
<string name="swiped_left">Swiped left</string>
2339
<string name="swiped_right">Swiped right</string>
24-
<string name="swipe_able_card_view">Swipe-able CardView</string>
25-
<string name="animated_ripple">Displays animated ripple when swiped</string>
26-
<string name="returns_after_delay">Returns to original position after delay</string>
27-
<string name="can_be_customized">This can also be customized</string>
28-
<string name="advanced_layout">Advanced layout</string>
29-
<string name="full_control">You have full control over look of SwipeActionView</string>
30-
<string name="click_me">Click me!</string>
31-
<string name="clicked">Clicked</string>
3240
</resources>

0 commit comments

Comments
 (0)