Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MultiSwipeActionView for multiple actions #33

Merged
merged 9 commits into from
Oct 29, 2023
6 changes: 2 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.0'
ext.kotlin_version = '1.9.10'
repositories {
mavenCentral()
jcenter()
google()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:8.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
}
Expand All @@ -16,7 +15,6 @@ buildscript {
allprojects {
repositories {
mavenCentral()
jcenter()
google()
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Thu Mar 07 19:06:55 CET 2019
#Wed Oct 04 09:22:37 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
59 changes: 34 additions & 25 deletions library/build.gradle
Original file line number Diff line number Diff line change
@@ -1,57 +1,66 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'

plugins {
id 'com.android.library'
id 'kotlin-android'
id 'maven-publish'
}

android {
compileSdkVersion 28
buildToolsVersion "28.0.3"

defaultConfig {
namespace = "me.thanel.swipeactionview"
minSdkVersion 14
targetSdkVersion 28
targetSdkVersion 33
compileSdk 33
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
}

publishing {
publications {
release(MavenPublication) {
artifact(bundleReleaseAar)
artifact sourcesJar
artifact javadocJar
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

groupId = 'com.github.Tunous'
artifactId = 'SwipeActionView'
version = '1.4.0-SNAPSHOT'
}
kotlinOptions {
jvmTarget = "17"
}
}

/*
project.afterEvaluate {
publishing {
publications {
aar(MavenPublication) {
groupId = 'com.github.Tunous'
artifactId = 'SwipeActionView'
version = '1.4.0-SNAPSHOT'

artifact bundleReleaseAar
artifact sourcesJar
artifact javadocJar
}
}
}
}*/

dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}


task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
archiveClassifier = 'sources'
}

task javadoc(type: Javadoc) {
failOnError false
source = android.sourceSets.main.java.sourceFiles
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.compile
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
archiveClassifier = 'javadoc'
from javadoc.destinationDir
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.thanel.swipeactionview

import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout

class MultiSwipeActionView : LinearLayout {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
}
109 changes: 102 additions & 7 deletions library/src/main/kotlin/me/thanel/swipeactionview/SwipeActionView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import android.os.Build
import android.os.Handler
import android.os.Message
import android.util.AttributeSet
import android.util.Log
import android.view.*
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
Expand All @@ -41,6 +42,8 @@ import me.thanel.swipeactionview.utils.marginStart
import me.thanel.swipeactionview.utils.radius
import me.thanel.swipeactionview.utils.setBoundsFrom
import me.thanel.swipeactionview.utils.totalWidth
import kotlin.math.absoluteValue


/**
* View that allows users to perform various actions by swiping it to the left or right sides.
Expand Down Expand Up @@ -186,6 +189,14 @@ class SwipeActionView : FrameLayout {
*/
private var maxRightSwipeDistance = 0f


/**
* The radius in which dragging should be increased because the first element is gone.
* Only used for MultiSwipeActionView
* Should be coordinated with resistance-multiplier
*/
private val distanceFromCenterpoint = 8

/**
* The minimum distance required to execute swipe callbacks when swiping to the left side.
*/
Expand Down Expand Up @@ -760,6 +771,37 @@ class SwipeActionView : FrameLayout {
else -> false
}

/**
* Tell whether the user has swiped the view far enough to perform a swipe callback
* for the first element in a MultiSwipeActionView
*
* @param swipeDistance The performed swipe distance.
*
* @return Whether the user has swiped far enough
*/
private fun hasSwipedFarEnoughMultiContainerFirstElement(swipeDistance: Float): Boolean {
newhinton marked this conversation as resolved.
Show resolved Hide resolved

val normalizedTranslation = swipeDistance.absoluteValue
//swiping right
if(swipeDistance>0){
if (rightSwipeView is MultiSwipeActionView) {
val halfway = (rightSwipeView as MultiSwipeActionView).getChildAt(0)?.width ?: 0
if(halfway-distanceFromCenterpoint < normalizedTranslation) {
return true
}
}
} else {
if (leftSwipeView is MultiSwipeActionView) {
val vg = (leftSwipeView as MultiSwipeActionView)
val halfway = vg.getChildAt(vg.childCount-1)?.width ?: 0
if(halfway-distanceFromCenterpoint < normalizedTranslation) {
return true
}
}
}
return false
}

/**
* Perform the drag by horizontally moving the view by movement delta.
*
Expand All @@ -768,10 +810,34 @@ class SwipeActionView : FrameLayout {
private fun performDrag(e: MotionEvent) {
var delta = e.rawX - lastX

var resistance = dragResistance
val normalizedTranslation = container.translationX.absoluteValue
val resistanceMulitplier = 4

//swiping right
if(container.translationX>0){
if (rightSwipeView is MultiSwipeActionView) {
val halfway = (rightSwipeView as MultiSwipeActionView).getChildAt(0)?.width ?: 0
if(halfway+distanceFromCenterpoint > normalizedTranslation &&
normalizedTranslation > halfway-distanceFromCenterpoint) {
resistance *=resistanceMulitplier
}
}
} else {
if (leftSwipeView is MultiSwipeActionView) {
val vg = (leftSwipeView as MultiSwipeActionView)
val halfway = vg.getChildAt(vg.childCount-1)?.width ?: 0
if(halfway+distanceFromCenterpoint > normalizedTranslation &&
normalizedTranslation > halfway-distanceFromCenterpoint) {
resistance *=resistanceMulitplier
}
}
}

// If we are swiping view away from view's default position make the swiping feel much
// harder to drag.
if (delta > 0 == container.translationX > 0 || container.translationX == 0f) {
delta /= dragResistance
delta /= resistance
}

container.translationX += delta
Expand Down Expand Up @@ -812,6 +878,8 @@ class SwipeActionView : FrameLayout {

if (hasSwipedFarEnough(container.translationX) || swipedFastEnough) {
activate(container.translationX > 0)
} else if (hasSwipedFarEnoughMultiContainerFirstElement(container.translationX)) {
activate(container.translationX > 0, true)
} else {
animateToOriginalPosition()
}
Expand All @@ -838,7 +906,7 @@ class SwipeActionView : FrameLayout {
*
* @param swipedRight Tells whether the view was swiped to the right instead of left side.
*/
private fun activate(swipedRight: Boolean) {
private fun activate(swipedRight: Boolean, isHalfwaySwipe: Boolean = false) {
// If activation animation didn't finish, move the view to original position without
// executing activate callback.
if (!canPerformSwipeAction) {
Expand All @@ -853,12 +921,19 @@ class SwipeActionView : FrameLayout {
leftSwipeRipple.restart()
}

val targetTranslationX = if (swipedRight) maxRightSwipeDistance else -maxLeftSwipeDistance
animateContainer(targetTranslationX, swipeAnimationDuration) {
animateContainer(getTargetTranslationX(swipedRight, isHalfwaySwipe), swipeAnimationDuration) {
val shouldFinish = if (swipedRight) {
swipeGestureListener?.onSwipedRight(this)
if(isHalfwaySwipe) {
swipeGestureListener?.onSwipedHalfwayRight(this)
} else {
swipeGestureListener?.onSwipedRight(this)
}
} else {
swipeGestureListener?.onSwipedLeft(this)
if(isHalfwaySwipe) {
swipeGestureListener?.onSwipedHalfwayLeft(this)
} else {
swipeGestureListener?.onSwipedLeft(this)
}
}

if (shouldFinish != false) {
Expand Down Expand Up @@ -911,7 +986,7 @@ class SwipeActionView : FrameLayout {
setFloatValues(targetTranslationX)
removeAllListeners()
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
override fun onAnimationEnd(animation: Animator) {
onEnd()
}
})
Expand Down Expand Up @@ -962,6 +1037,26 @@ class SwipeActionView : FrameLayout {
else -> maxRightSwipeDistance
}

/**
* Get the translation distance for the animate container
*
* @param swipedRight whether the user swiped right or left (true for right, false for left)
* @param isHalfwaySwipe whether the user swiped ony the first item
*
* @return Translation Value
*/
private fun getTargetTranslationX(swipedRight: Boolean, isHalfwaySwipe: Boolean): Float {
return if(isHalfwaySwipe) {
return if (swipedRight) {
((rightSwipeView as MultiSwipeActionView).getChildAt(0)?.width ?: maxRightSwipeDistance).toFloat()
} else {
-((leftSwipeView as MultiSwipeActionView).getChildAt(0)?.width ?: maxLeftSwipeDistance).toFloat()
}
} else {
if (swipedRight) maxRightSwipeDistance else -maxLeftSwipeDistance
}
}

/**
* Get min activation distance for the specified delta. Once users swipes view above this
* distance swipe callback will be called upon view release.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@ interface SwipeGestureListener {
*/
fun onSwipedRight(swipeActionView: SwipeActionView): Boolean {return true}

/**
* Callback method to be invoked when user swipes the [SwipeActionView] to the
* left, but only the first element. Requires MultiSwipeActionView as a Container.
*
* @param swipeActionView The [SwipeActionView] from which this method was invoked.
*
* @return Whether the container should return to default position. When `false`, then you
* should manually call `reset` method to return to default position.
* @see SwipeActionView
*/
fun onSwipedHalfwayLeft(swipeActionView: SwipeActionView): Boolean {return true}

/**
* Callback method to be invoked when user swipes the [SwipeActionView] to the
* right, but only the first element. Requires MultiSwipeActionView as a Container.
*
* @param swipeActionView The [SwipeActionView] from which this method was invoked.
*
* @return Whether the container should return to default position. When `false`, then you
* should manually call `reset` method to return to default position.
* @see SwipeActionView
*/
fun onSwipedHalfwayRight(swipeActionView: SwipeActionView): Boolean {return true}

/**
* Callback method to be invoked when the left swipe is complete.
* A swipe is considered complete once the view returns back to its original position.
Expand Down
19 changes: 12 additions & 7 deletions sample/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
apply plugin: 'com.android.application'
plugins {
id 'com.android.application'
id 'kotlin-android'
//id 'maven-publish'
}


android {
compileSdkVersion 28
buildToolsVersion "28.0.3"

defaultConfig {
applicationId "me.thanel.swipeactionview.sample"
namespace = "me.thanel.swipeactionview.sample"
minSdkVersion 14
targetSdkVersion 28
versionCode 2
versionName "0.2"
targetSdkVersion 33
compileSdk 33
versionCode 3
versionName "0.3"
}
buildTypes {
release {
Expand All @@ -20,7 +25,7 @@ android {
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'

implementation project(':library')
Expand Down
Loading