11package com.smarttoolfactory.tutorial1_1basics.chapter5_gesture
22
33import androidx.compose.animation.core.Animatable
4+ import androidx.compose.animation.core.tween
45import androidx.compose.foundation.Image
56import androidx.compose.foundation.background
67import androidx.compose.foundation.border
@@ -32,6 +33,7 @@ import androidx.compose.runtime.rememberCoroutineScope
3233import androidx.compose.runtime.setValue
3334import androidx.compose.ui.Modifier
3435import androidx.compose.ui.draw.clip
36+ import androidx.compose.ui.draw.shadow
3537import androidx.compose.ui.geometry.Offset
3638import androidx.compose.ui.graphics.Color
3739import androidx.compose.ui.graphics.TransformOrigin
@@ -46,14 +48,15 @@ import androidx.compose.ui.input.pointer.pointerInput
4648import androidx.compose.ui.layout.ContentScale
4749import androidx.compose.ui.res.painterResource
4850import androidx.compose.ui.tooling.preview.Preview
49- import androidx.compose.ui.unit.center
5051import androidx.compose.ui.unit.dp
5152import androidx.compose.ui.unit.sp
5253import com.smarttoolfactory.tutorial1_1basics.R
5354import com.smarttoolfactory.tutorial1_1basics.chapter2_material_widgets.CheckBoxWithTextRippleFullRow
5455import com.smarttoolfactory.tutorial1_1basics.chapter5_gesture.gesture.detectDragGesture
5556import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.ExposedSelectionMenu
5657import com.smarttoolfactory.tutorial1_1basics.ui.Blue400
58+ import com.smarttoolfactory.tutorial1_1basics.ui.Green400
59+ import com.smarttoolfactory.tutorial1_1basics.ui.Red400
5760import com.smarttoolfactory.tutorial1_1basics.ui.backgroundColor
5861import kotlinx.coroutines.launch
5962
@@ -99,14 +102,12 @@ fun DragPagerAndSwipeTest() {
99102 .padding(16 .dp)
100103 ) {
101104
102- var text by remember {
103- mutableStateOf(" " )
104- }
105-
106105 var isDraggable by remember {
107106 mutableStateOf(false )
108107 }
109108
109+ val angleThreshold = 18f
110+
110111 Box {
111112 items.forEachIndexed { index, _ ->
112113
@@ -115,61 +116,72 @@ fun DragPagerAndSwipeTest() {
115116 currentList.size
116117 }
117118
118- val animatable = remember {
119+ val angleAnimatable = remember {
119120 Animatable (0f )
120121 }
121122
122- val offset = remember {
123+ val offsetAnimatable = remember {
123124 Animatable (0f )
124125 }
125126
127+ var text by remember {
128+ mutableStateOf(
129+ " drag from left to right on first page or right left on last page" +
130+ " \n to swipe item off the list when threshold angle is passed"
131+ )
132+ }
133+
126134 val dragModifier = Modifier
127135 .pointerInput(Unit ) {
128136 val width = size.width.toFloat()
129- val centerX = size.center.x.toFloat()
130137
131138 detectDragGesture(
132- requireUnconsumed = true ,
133- shouldAwaitTouchSlop = false ,
139+ requireUnconsumed = false ,
140+ shouldAwaitTouchSlop = true ,
134141 passForSlopDetection = PointerEventPass .Main ,
135142 passForDrag = PointerEventPass .Initial ,
136- onDragStart = { change, offset ->
137- text = " onDragStart..."
138- isDraggable =
139- change.position.x <= centerX &&
140- pagerState.currentPage == 0 &&
141- offset.x <= 0f
142- println (" onDragStart offset: ${offset.x} " )
143+ onDragStart = { _, offset ->
144+ text = " onDragStart...offset: $offset "
145+
146+ val firstPageDraggable = pagerState.currentPage == 0 && offset.x > 0
147+ val lastPageDraggable =
148+ pagerState.currentPage == pagerState.pageCount - 1 && offset.x < 0
149+
150+ isDraggable = firstPageDraggable || lastPageDraggable
143151 },
144152 onDrag = { change, dragAmount ->
145153
154+ var currentOffset = offsetAnimatable.value
155+
146156 if (isDraggable) {
147157 change.consume()
148- val posX = change.position.x.coerceIn(0f , width)
149- val tempPos = (posX - centerX).coerceAtMost(0f )
150-
151- if (tempPos == 0f ) {
152- isDraggable = false
153- } else {
154- var currentOffset = offset.value
155- currentOffset + = dragAmount.x * 1.25f
158+ currentOffset - = dragAmount.x * 2f
159+ var angle = 0f
160+ if (pagerState.currentPage == 0 ) {
156161 currentOffset = currentOffset.coerceAtMost(0f )
157- var angle = 60f * (currentOffset) / centerX
162+ angle = 80f * (currentOffset) / width
158163 angle = angle.coerceAtMost(0f )
164+ }
159165
160- coroutineScope.launch {
161- animatable.animateTo(angle)
162- }
166+ if (pagerState.currentPage == pagerState.pageCount - 1 ) {
167+ currentOffset = currentOffset.coerceAtLeast(0f )
168+ angle = 60f * (currentOffset) / width
169+ angle = angle.coerceAtLeast(0f )
170+ }
171+
172+ coroutineScope.launch {
173+ angleAnimatable.animateTo(angle)
174+ }
163175
164- coroutineScope.launch {
165- offset.animateTo(currentOffset)
166- }
176+ coroutineScope.launch {
177+ offsetAnimatable.animateTo(currentOffset)
167178 }
168179 }
169180
170181 text = " onDrag...$dragAmount \n " +
171- " offset: ${offset.value} , " +
172- " angle: ${animatable.value} "
182+ " offset: ${offsetAnimatable.value} , " +
183+ " angle: ${angleAnimatable.value} "
184+
173185
174186 },
175187 onDragCancel = {
@@ -178,21 +190,27 @@ fun DragPagerAndSwipeTest() {
178190 },
179191 onDragEnd = {
180192
181- val angle = animatable .value
182- if (angle < - 20f ) {
193+ val angle = angleAnimatable .value
194+ if (angle < - angleThreshold || angle > angleThreshold ) {
183195 coroutineScope.launch {
184- animatable.animateTo(- 70f )
196+ angleAnimatable.animateTo(
197+ targetValue = angle * 2f ,
198+ animationSpec = tween(250 )
199+ )
185200 items.remove(items.lastIndex)
186201 }
187202 coroutineScope.launch {
188- offset.animateTo(offset.value * 3f )
203+ offsetAnimatable.animateTo(
204+ targetValue = offsetAnimatable.value * 2f ,
205+ animationSpec = tween(250 )
206+ )
189207 }
190208 } else {
191209 coroutineScope.launch {
192- animatable .animateTo(0f )
210+ angleAnimatable .animateTo(0f )
193211 }
194212 coroutineScope.launch {
195- offset .animateTo(0f )
213+ offsetAnimatable .animateTo(0f )
196214 }
197215 }
198216 isDraggable = false
@@ -202,8 +220,8 @@ fun DragPagerAndSwipeTest() {
202220 )
203221 }
204222 .graphicsLayer {
205- val angle = animatable .value
206- val pos = offset .value
223+ val angle = angleAnimatable .value
224+ val pos = offsetAnimatable .value
207225 translationX = pos
208226 translationY = - pos * .25f
209227 rotationZ = angle
@@ -216,11 +234,21 @@ fun DragPagerAndSwipeTest() {
216234
217235 ) {
218236
237+ val shape = RoundedCornerShape (16 .dp)
238+ val angle = angleAnimatable.value
239+
240+ val borderColor = if (angle < - angleThreshold) {
241+ Red400
242+ } else if (angle > angleThreshold) {
243+ Green400
244+ } else {
245+ Color .LightGray
246+ }
219247 Column (
220248 modifier = Modifier
221- .clip( RoundedCornerShape ( 16 .dp) )
222- .border(1 .dp, Color . LightGray , RoundedCornerShape ( 16 .dp) )
223- .background(Color .White , RoundedCornerShape ( 16 .dp) )
249+ .shadow(elevation = 0.1 .dp, shape = shape )
250+ .border(width = 2 .dp, borderColor, shape = shape )
251+ .background(Color .White , shape )
224252 ) {
225253 HorizontalPager (
226254 modifier = Modifier ,
0 commit comments