Last active
June 25, 2022 05:08
-
-
Save thebino/4c95e46cca53ec1d9baa29953f0ccb20 to your computer and use it in GitHub Desktop.
[Jetpack Compose] Using a swipeableState within a CoordinatorLayout will not detect any swipe gestures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context=".MainActivity"> | |
<com.google.android.material.appbar.AppBarLayout | |
android:id="@+id/app_bar" | |
android:layout_width="match_parent" | |
android:layout_height="48dp" | |
android:fitsSystemWindows="true" | |
android:theme="@style/AppTheme.AppBarOverlay"> | |
<com.google.android.material.appbar.CollapsingToolbarLayout | |
android:id="@+id/toolbar_layout" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:fitsSystemWindows="true" | |
android:minHeight="60dp" | |
app:contentScrim="?attr/colorPrimary" | |
app:layout_scrollFlags="scroll|exitUntilCollapsed" | |
app:toolbarId="@+id/toolbar"> | |
<androidx.appcompat.widget.AppCompatImageView | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:fitsSystemWindows="true" | |
android:scaleType="centerCrop" | |
android:src="@drawable/ic_launcher_foreground" | |
app:layout_collapseMode="parallax" /> | |
<androidx.appcompat.widget.Toolbar | |
android:id="@+id/toolbar" | |
android:layout_width="match_parent" | |
android:layout_height="?attr/actionBarSize" | |
app:layout_collapseMode="pin" | |
app:popupTheme="@style/AppTheme.PopupOverlay" /> | |
</com.google.android.material.appbar.CollapsingToolbarLayout> | |
</com.google.android.material.appbar.AppBarLayout> | |
<androidx.constraintlayout.widget.ConstraintLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
app:layout_behavior="@string/appbar_scrolling_view_behavior"> | |
<androidx.core.widget.NestedScrollView | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<androidx.compose.ui.platform.ComposeView | |
android:id="@+id/draggable_layout" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:layout_marginStart="8dp" | |
android:layout_marginTop="20dp" | |
android:layout_marginEnd="8dp" /> | |
</androidx.core.widget.NestedScrollView> | |
</androidx.constraintlayout.widget.ConstraintLayout> | |
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.compose | |
import android.os.Bundle | |
import androidx.activity.ComponentActivity | |
import androidx.compose.foundation.BorderStroke | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.border | |
import androidx.compose.foundation.gestures.Orientation | |
import androidx.compose.foundation.layout.Box | |
import androidx.compose.foundation.layout.height | |
import androidx.compose.foundation.layout.offset | |
import androidx.compose.foundation.layout.padding | |
import androidx.compose.foundation.layout.size | |
import androidx.compose.foundation.shape.CircleShape | |
import androidx.compose.material.FractionalThreshold | |
import androidx.compose.material.Icon | |
import androidx.compose.material.MaterialTheme | |
import androidx.compose.material.icons.Icons | |
import androidx.compose.material.icons.filled.Send | |
import androidx.compose.material.rememberSwipeableState | |
import androidx.compose.material.swipeable | |
import androidx.compose.runtime.Composable | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.draw.clip | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.platform.LocalDensity | |
import androidx.compose.ui.platform.ViewCompositionStrategy | |
import androidx.compose.ui.tooling.preview.Preview | |
import androidx.compose.ui.unit.Dp | |
import androidx.compose.ui.unit.IntOffset | |
import androidx.compose.ui.unit.dp | |
import com.example.compose.databinding.MainActivityBinding | |
import kotlin.math.roundToInt | |
class MainActivity : ComponentActivity() { | |
private lateinit var binding: MainActivityBinding | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
binding = MainActivityBinding.inflate(layoutInflater) | |
setContentView(binding.root) | |
binding.draggableLayout.apply { | |
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) | |
setContent { | |
SwipableContent() | |
} | |
} | |
} | |
} | |
@Composable | |
fun SwipableContent( | |
modifier: Modifier = Modifier, | |
onSwipeStateUpdated: (state: SwipeState) -> Unit = {} | |
) { | |
Box( | |
modifier = modifier | |
.border(border = BorderStroke(1.dp, Color.Green)) | |
.height(300.dp), | |
contentAlignment = Alignment.Center | |
) { | |
val slideWidth: Dp = 80.dp | |
val iconSize = slideWidth - 10.dp | |
val swipeableState = rememberSwipeableState( | |
initialValue = SwipeState.START, | |
confirmStateChange = { newValue: SwipeState -> | |
onSwipeStateUpdated(newValue) | |
true | |
} | |
) | |
val slideDistance = with(LocalDensity.current) { | |
(slideWidth - iconSize - 55.dp).toPx() | |
} | |
Box( | |
modifier = Modifier | |
.border(border = BorderStroke(1.dp, Color.Magenta)) | |
.offset { | |
IntOffset(0, swipeableState.offset.value.roundToInt()) | |
} | |
.swipeable( | |
state = swipeableState, | |
anchors = mapOf( | |
0f to SwipeState.START, | |
slideDistance to SwipeState.END | |
), | |
thresholds = { _, _ -> | |
FractionalThreshold(0.9f) | |
}, | |
orientation = Orientation.Vertical | |
) | |
.size(80.dp) | |
.clip(CircleShape) | |
.background(Color(0xFFFF0000)), | |
contentAlignment = Alignment.Center | |
) { | |
Icon( | |
imageVector = Icons.Default.Send, | |
contentDescription = "Icon", | |
tint = Color.White | |
) | |
} | |
} | |
} | |
@Preview | |
@Composable | |
fun SwipableContentPreview() { | |
MaterialTheme { | |
SwipableContent(modifier = Modifier.padding(16.dp)) | |
} | |
} | |
enum class SwipeState { | |
START, | |
END | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.compose | |
import androidx.compose.ui.geometry.Offset | |
import androidx.compose.ui.test.assertIsDisplayed | |
import androidx.compose.ui.test.junit4.createComposeRule | |
import androidx.compose.ui.test.onNodeWithContentDescription | |
import androidx.compose.ui.test.performTouchInput | |
import androidx.compose.ui.test.swipe | |
import com.example.compose.ui.theme.AppTheme | |
import com.google.common.truth.Truth | |
import org.junit.Rule | |
import org.junit.Test | |
class SwipeableContentStandaloneTest { | |
@get:Rule | |
val composeTestRule = createComposeRule() | |
@Test | |
fun testVerticalSwipe() { | |
// given | |
var swiState = SwipeState.START | |
// when | |
composeTestRule.setContent { | |
AppTheme { | |
SwipableContent( | |
onSwipeStateUpdated = { | |
swiState = it | |
} | |
) | |
} | |
} | |
// then | |
composeTestRule.onNodeWithContentDescription(label = "Icon").assertIsDisplayed() | |
Truth.assertThat(swiState).isEqualTo(SwipeState.START) | |
composeTestRule.onNodeWithContentDescription(label = "Icon").performTouchInput { | |
swipe( | |
start = center, | |
end = Offset(center.x, center.y - 500), | |
durationMillis = 3000 | |
) | |
} | |
Truth.assertThat(swiState).isEqualTo(SwipeState.END) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment