您好,登錄后才能下訂單哦!
這篇文章主要講解了“Compose for Desktop鼠標事件源碼分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Compose for Desktop鼠標事件源碼分析”吧!
點擊監聽器在安卓Compose 和Compose for Desktop中都是可用的, 所以像這樣的代碼在兩個平臺上都可以使用:
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication { var count by remember { mutableStateOf(0) } Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) { var text by remember { mutableStateOf("Click magenta box!") } Column { @OptIn(ExperimentalFoundationApi::class) Box( modifier = Modifier .background(Color.Magenta) .fillMaxWidth(0.7f) .fillMaxHeight(0.2f) .combinedClickable( onClick = { text = "Click! ${count++}" }, onDoubleClick = { text = "Double click! ${count++}" }, onLongClick = { text = "Long click! ${count++}" } ) ) Text(text = text, fontSize = 40.sp) } } }
combinedClickable只支持主按鈕(鼠標左鍵)和觸摸事件. 如果需要以不同方式處理其他按鈕, 請看下面的Modifier.onClick(注意: Modifier.onClick目前只適用于Desktop-JVM平臺).
讓我們創建一個窗口, 并在其上安裝一個指針移動監聽器, 根據鼠標指針的位置來改變背景顏色:
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication { var color by remember { mutableStateOf(Color(0, 0, 0)) } Box( modifier = Modifier .wrapContentSize(Alignment.Center) .fillMaxSize() .background(color = color) .onPointerEvent(PointerEventType.Move) { val position = it.changes.first().position color = Color(position.x.toInt() % 256, position.y.toInt() % 256, 0) } ) }
注意, onPointerEvent是實驗性的, 將來可能會被改變. 對于更多穩定的API請看Modifier.pointerInput.
Compose for Desktop 也支持指針的進入和退出處理, 像這樣:
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication { Column( Modifier.background(Color.White), verticalArrangement = Arrangement.spacedBy(10.dp) ) { repeat(10) { index -> var active by remember { mutableStateOf(false) } Text( modifier = Modifier .fillMaxWidth() .background(color = if (active) Color.Green else Color.White) .onPointerEvent(PointerEventType.Enter) { active = true } .onPointerEvent(PointerEventType.Exit) { active = false }, fontSize = 30.sp, fontStyle = if (active) FontStyle.Italic else FontStyle.Normal, text = "Item $index" ) } } }
注意, onPointerEvent是實驗性的, 將來可能會被改變. 對于更多穩定的API請看Modifier.pointerInput.
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.unit.sp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication { var number by remember { mutableStateOf(0f) } Box( Modifier .fillMaxSize() .onPointerEvent(PointerEventType.Scroll) { number += it.changes.first().scrollDelta.y }, contentAlignment = Alignment.Center ) { Text("Scroll to change the number: $number", fontSize = 30.sp) } }
注意, onPointerEvent是實驗性的, 將來可能會被改變. 對于更多穩定的API請看Modifier.pointerInput.
Compose for Desktop內部使用Swing, 并允許訪問原始AWT事件:
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.awt.awtEventOrNull import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalComposeUiApi::class) fun main() = singleWindowApplication { var text by remember { mutableStateOf("") } Box( Modifier .fillMaxSize() .onPointerEvent(PointerEventType.Press) { text = it.awtEventOrNull?.locationOnScreen?.toString().orEmpty() }, contentAlignment = Alignment.Center ) { Text(text) } }
注意, onPointerEvent是實驗性的, 將來可能會被改變. 對于更多穩定的API請看Modifier.pointerInput.
在上面的片段中, 我們使用了Modifier.onPointerEvent, 它是一個輔助函數, 用于訂閱某種類型的指針事件. 它是Modifier.pointerInput的一個簡短變體. 目前, 它是實驗性的, 并且只在桌面上使用(你不能在普通主代碼中使用它). 如果你需要在commonMain中訂閱事件或者你需要穩定的API, 你可以使用Modifier.pointerInput:
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Text import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication { val list = remember { mutableStateListOf<String>() } Column( Modifier .fillMaxSize() .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() val position = event.changes.first().position // on every relayout Compose will send synthetic Move event, // so we skip it to avoid event spam if (event.type != PointerEventType.Move) { list.add(0, "${event.type} $position") } } } }, ) { for (item in list.take(20)) { Text(item) } } }
Modifier.onClick為點擊, 雙擊, 長按提供獨立的回調. 它只處理源于指針事件的點擊, 并且可訪問性點擊事件不被處理.
每個onClick可以被配置為針對特定的指針事件(使用matcher: PointerMatcher和keyboardModifiers: PointerKeyboardModifiers.() -> Boolean). matcher可以被指定來選擇什么鼠標按鈕應該觸發點擊. keyboardModifiers允許過濾有指定keyboardModifiers按下的指針事件.
多個onClick修改器可以被連鎖, 以處理不同條件的點擊(匹配器和鍵盤修改器). 與clickable不同, onClick沒有默認的Modifier.indication, Modifier.semantics, 當Enter按下時, 它不會觸發一個點擊事件. 如果有必要, 這些修改器需要單獨添加. 最通用的(條件最少的)onClick處理程序應該在其他之前聲明, 以確保事件傳播的正確性.
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.PointerMatcher import androidx.compose.foundation.background import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.size import androidx.compose.foundation.onClick import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerButton import androidx.compose.ui.input.pointer.isAltPressed import androidx.compose.ui.input.pointer.isShiftPressed import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class) fun main() = singleWindowApplication { Column { var topBoxText by remember { mutableStateOf("Click me\nusing LMB or LMB + Shift") } var topBoxCount by remember { mutableStateOf(0) } // No indication on interaction Box(modifier = Modifier.size(200.dp, 100.dp).background(Color.Blue) // the most generic click handler (without extra conditions) should be the first one .onClick { // it will receive all LMB clicks except when Shift is pressed println("Click with primary button") topBoxText = "LMB ${topBoxCount++}" }.onClick( keyboardModifiers = { isShiftPressed } // accept clicks only when Shift pressed ) { // it will receive all LMB clicks when Shift is pressed println("Click with primary button and shift pressed") topBoxCount++ topBoxText = "LMB + Shift ${topBoxCount++}" } ) { AnimatedContent( targetState = topBoxText, modifier = Modifier.align(Alignment.Center) ) { Text(text = it, textAlign = TextAlign.Center) } } var bottomBoxText by remember { mutableStateOf("Click me\nusing LMB or\nRMB + Alt") } var bottomBoxCount by remember { mutableStateOf(0) } val interactionSource = remember { MutableInteractionSource() } // With indication on interaction Box(modifier = Modifier.size(200.dp, 100.dp).background(Color.Yellow) .onClick( enabled = true, interactionSource = interactionSource, matcher = PointerMatcher.mouse(PointerButton.Secondary), // Right Mouse Button keyboardModifiers = { isAltPressed }, // accept clicks only when Alt pressed onLongClick = { // optional bottomBoxText = "RMB Long Click + Alt ${bottomBoxCount++}" println("Long Click with secondary button and Alt pressed") }, onDoubleClick = { // optional bottomBoxText = "RMB Double Click + Alt ${bottomBoxCount++}" println("Double Click with secondary button and Alt pressed") }, onClick = { bottomBoxText = "RMB Click + Alt ${bottomBoxCount++}" println("Click with secondary button and Alt pressed") } ) .onClick(interactionSource = interactionSource) { // use default parameters bottomBoxText = "LMB Click ${bottomBoxCount++}" println("Click with primary button (mouse left button)") } .indication(interactionSource, LocalIndication.current) ) { AnimatedContent( targetState = bottomBoxText, modifier = Modifier.align(Alignment.Center) ) { Text(text = it, textAlign = TextAlign.Center) } } } }
Modifier.onDrag allows for configuration of the pointer that should trigger the drag (see matcher: PointerMatcher).
Many onDrag modifiers can be chained together.The example below also shows how to access the state of keyboard modifiers (via LocalWindowInfo.current.keyboardModifier) for cases when keyboard modifiers can alter the behaviour of the drag (for example: move an item if we perform a simple drag; or copy/paste an item if dragged with Ctrl pressed)
Modifier.onDrag允許配置應觸發拖動的指針(見matcher: PointerMatcher). 許多onDrag修改器可以被連鎖起來.
下面的例子還顯示了如何訪問鍵盤修改器的狀態(通過LocalWindowInfo.current.keyboardModifier), 以應對鍵盤修改器可以改變拖動行為的情況(例如: 如果我們執行簡單的拖動, 則移動一個項目; 如果按住Ctrl拖動, 則復制/粘貼一個項目)
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.PointerMatcher import androidx.compose.foundation.background import androidx.compose.foundation.gestures.onDrag import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerButton import androidx.compose.ui.input.pointer.isCtrlPressed import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalFoundationApi::class) fun main() = singleWindowApplication { val windowInfo = LocalWindowInfo.current Column { var topBoxOffset by remember { mutableStateOf(Offset(0f, 0f)) } Box(modifier = Modifier.offset { IntOffset(topBoxOffset.x.toInt(), topBoxOffset.y.toInt()) }.size(100.dp) .background(Color.Green) .onDrag { // all default: enabled = true, matcher = PointerMatcher.Primary (left mouse button) topBoxOffset += it } ) { Text(text = "Drag with LMB", modifier = Modifier.align(Alignment.Center)) } var bottomBoxOffset by remember { mutableStateOf(Offset(0f, 0f)) } Box(modifier = Modifier.offset { IntOffset(bottomBoxOffset.x.toInt(), bottomBoxOffset.y.toInt()) }.size(100.dp) .background(Color.LightGray) .onDrag( enabled = true, matcher = PointerMatcher.mouse(PointerButton.Secondary), // right mouse button onDragStart = { println("Gray Box: drag start") }, onDragEnd = { println("Gray Box: drag end") } ) { val keyboardModifiers = windowInfo.keyboardModifiers bottomBoxOffset += if (keyboardModifiers.isCtrlPressed) it * 2f else it } ) { Text(text = "Drag with RMB,\ntry with CTRL", modifier = Modifier.align(Alignment.Center)) } } }
也有非修改器方式來處理拖拽, 就是用suspend fun PointerInputScope.detectDragGestures:
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.PointerMatcher import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication @OptIn(ExperimentalFoundationApi::class) fun main() = singleWindowApplication { var topBoxOffset by remember { mutableStateOf(Offset(0f, 0f)) } Box(modifier = Modifier.offset { IntOffset(topBoxOffset.x.toInt(), topBoxOffset.y.toInt()) }.size(100.dp) .background(Color.Green) .pointerInput(Unit) { detectDragGestures( matcher = PointerMatcher.Primary ) { topBoxOffset += it } } ) { Text(text = "Drag with LMB", modifier = Modifier.align(Alignment.Center)) } }
感謝各位的閱讀,以上就是“Compose for Desktop鼠標事件源碼分析”的內容了,經過本文的學習后,相信大家對Compose for Desktop鼠標事件源碼分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。