1 /*
<lambda>null2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.compose.ui.demos.gestures
18
19 import androidx.compose.foundation.background
20 import androidx.compose.foundation.border
21 import androidx.compose.foundation.layout.Box
22 import androidx.compose.foundation.layout.Column
23 import androidx.compose.foundation.layout.fillMaxSize
24 import androidx.compose.foundation.layout.requiredSize
25 import androidx.compose.foundation.layout.wrapContentSize
26 import androidx.compose.foundation.lazy.LazyColumn
27 import androidx.compose.material.Text
28 import androidx.compose.runtime.Composable
29 import androidx.compose.runtime.getValue
30 import androidx.compose.runtime.mutableIntStateOf
31 import androidx.compose.runtime.remember
32 import androidx.compose.runtime.setValue
33 import androidx.compose.ui.Alignment
34 import androidx.compose.ui.Modifier
35 import androidx.compose.ui.composed
36 import androidx.compose.ui.graphics.Color
37 import androidx.compose.ui.input.pointer.PointerEvent
38 import androidx.compose.ui.input.pointer.PointerEventPass
39 import androidx.compose.ui.input.pointer.PointerInputFilter
40 import androidx.compose.ui.input.pointer.PointerInputModifier
41 import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
42 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
43 import androidx.compose.ui.unit.IntSize
44 import androidx.compose.ui.unit.dp
45 import androidx.compose.ui.unit.em
46
47 /** Demonstration of how various press/tap gesture interact together in a nested fashion. */
48 @Composable
49 fun PointerInputDuringSubComp() {
50 Column {
51 Text(
52 "Demonstrates that PointerInputFilters that are currently receiving pointer input " +
53 "events can be removed from the hierarchy by sub composition with no difficulty"
54 )
55 Text(
56 "Below is an AdapterList with many touchable items. Each item keeps track of the " +
57 "number of pointers touching it. If you touch an item and then scroll so " +
58 "that it goes out of the viewport and then back into the viewport, you will" +
59 " see that it no longer knows that a finger is touching it. That is because " +
60 "it is actually a new item that has not been hit tested yet. If you keep " +
61 "your finger there and then add more fingers, it will track those new fingers."
62 )
63 LazyColumn(
64 Modifier.fillMaxSize()
65 .wrapContentSize(Alignment.Center)
66 .requiredSize(200.dp)
67 .background(color = Color.White)
68 ) {
69 items(100) {
70 var pointerCount by remember { mutableIntStateOf(0) }
71
72 Box(
73 Modifier.fillParentMaxSize()
74 .border(width = 1.dp, color = Color.Black)
75 .pointerCounterGestureFilter { newCount -> pointerCount = newCount },
76 contentAlignment = Alignment.Center
77 ) {
78 Text("$pointerCount", fontSize = 16.em, color = Color.Black)
79 }
80 }
81 }
82 }
83 }
84
pointerCounterGestureFilternull85 fun Modifier.pointerCounterGestureFilter(onPointerCountChanged: (Int) -> Unit): Modifier =
86 composed {
87 val filter = remember { PointerCounterGestureFilter() }
88 filter.onPointerCountChanged = onPointerCountChanged
89 PointerInputModifierImpl(filter)
90 }
91
92 internal class PointerInputModifierImpl(override val pointerInputFilter: PointerInputFilter) :
93 PointerInputModifier
94
95 internal class PointerCounterGestureFilter : PointerInputFilter() {
96
97 lateinit var onPointerCountChanged: (resultingPointerCount: Int) -> Unit
98
onPointerEventnull99 override fun onPointerEvent(
100 pointerEvent: PointerEvent,
101 pass: PointerEventPass,
102 bounds: IntSize
103 ) {
104 val changes = pointerEvent.changes
105
106 if (pass == PointerEventPass.Main) {
107 if (
108 changes.any { it.changedToDownIgnoreConsumed() || it.changedToUpIgnoreConsumed() }
109 ) {
110 onPointerCountChanged.invoke(changes.count { it.pressed })
111 }
112 }
113 }
114
onCancelnull115 override fun onCancel() {}
116 }
117