Compose 手勢處理,增進交互體驗

Compose 手勢處理,增進交互體驗

  • 概述
  • 常用手勢處理Modifier
    • clickable()
    • combinedClickable()
    • draggable()
    • swipeable()
    • transformable()
    • scrollable()
    • nestedScroll
      • NestedScrollConnection
      • NestedScrollDispatcher
  • 定制手勢處理
    • 使用 PointerInput Modifier
      • PointerInputScope
    • awaitPointerEventScope

概述

在處理手勢時,應將手勢處理修飾符盡可能放到 Modifier 末尾,從而可以避免產生不可預期的行為。

常用手勢處理Modifier

clickable()

監聽點擊事件,在絕大多數情況下,只需要出入 onClick 回調即可。當然也可以將 enable 參數設置為一個可變狀態,通過狀態來動態控制啟用點擊監聽。

@Composable
private fun GestureOfClick(){var colorState by remember { mutableStateOf(false) }Box(modifier = Modifier.size(60.dp).background(color = if (colorState) Color.LightGray else Color.Gray).clickable { colorState = !colorState },contentAlignment = Alignment.Center){Text(text = "點擊")}
}

combinedClickable()

和 clickable() 類似,但支持長按/雙擊/單擊:

// Clickable.kt
fun Modifier.combinedClickable(enabled: Boolean = true,onClickLabel: String? = null,role: Role? = null,onLongClickLabel: String? = null,onLongClick: (() -> Unit)? = null,onDoubleClick: (() -> Unit)? = null,onClick: () -> Unit
)

draggable()

只支持檢測單一方向的拖動(水平方向或垂直方向),不支持同時監聽兩個方向上的拖動偏移,要實現這種效果,需要使用更底層的 PointerInputModifier。

fun Modifier.draggable(state: DraggableState,//用于獲取拖動手勢偏移量,并且允許動態控制發生偏移行為。orientation: Orientation,//拖動手勢方向enabled: Boolean = true,//是否啟用拖動手勢監聽。 interactionSource: MutableInteractionSource? = null,//監聽組件的拖動、按壓、懸停、焦點等狀態startDragImmediately: Boolean = false,//是否立即開始拖動。onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},//拖動開始的回調。onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {},//拖動結束的回調。reverseDirection: Boolean = false//是否反轉方向。
): Modifier

swipeable()

可以拖動元素,釋放后,這些元素通常朝一個方向定義的兩個或多個錨點繼續滑動以呈現動畫效果。其常見用途是實現“滑動關閉”模式。

必傳的參數有:

  • state:是一個SwipeableState,可以記錄當前的偏移數據
  • anchors:錨點,用來記錄不同滑動數據對應的狀態
  • orientation:滑動方向
  • thresholds:不同錨點之間的臨界值

transformable()

多點觸控,在日常生活當中,多點觸控這樣的操作多數是在瀏覽圖片,網頁或者地圖之類的場景下被用到
transformable有三個參數:

  • state:TransformableState,用來獲取多點觸控時候目標組件大小,位移,旋轉角度變化情況的
  • lockRotationOnZoomPan:Boolean,這個參數的意思是如果設置為false,那么多點觸控的時候將會同時監聽雙指拖動,縮放以及旋轉,但是如果設置為true的時候,除非旋轉動作比其余兩個動作先執行,這樣會被監聽到,不然的話,只會監聽雙指拖動和縮放動作,旋轉事件將不會被監聽
  • enabled:Boolean,是否可用
@Composable
private fun TransformableSample() {// set up all transformation statesvar scale by remember { mutableStateOf(1f) }var rotation by remember { mutableStateOf(0f) }var offset by remember { mutableStateOf(Offset.Zero) }val state = rememberTransformableState { zoomChange, offsetChange, rotationChange ->scale *= zoomChangerotation += rotationChangeoffset += offsetChange}Box(Modifier// apply other transformations like rotation and zoom// on the pizza slice emoji.graphicsLayer(scaleX = scale,scaleY = scale,rotationZ = rotation,translationX = offset.x,translationY = offset.y)// add transformable to listen to multitouch transformation events// after offset.transformable(state = state).background(Color.Blue).fillMaxSize())
}

scrollable()

雖然 verticalScroll() / horizontalScroll() 和 scrollable() 的名字很像,但它們并不是相同的東西,scrollable() 修飾符僅負責檢測滾動手勢,并不會幫我們自動偏移元素內容,滾動行為由開發者定義,用法類似 draggable() 修飾符

var offsetX by remember { mutableFloatStateOf(0f) }
Column {Text(text = "OffsetX: $offsetX")Box(Modifier.size(200.dp).background(Pink).scrollable(// 檢測水平方向的滾動手勢orientation = Orientation.Horizontal,// 使用 rememberScrollableState 創建并傳遞一個 ScrollableState 對象。// 通過 ScrollableState 可以獲取到滾動手勢的偏移量,進一步定義滾動行為。state = rememberScrollableState { delta ->offsetX += deltadelta // 為了支持嵌套滾動,必須返回消費的滾動距離量}))
}

nestedScroll

嵌套滑動,需要傳遞兩個參數,connection: NestedScrollConnection 和 dispatcher: NestedScrollDispatcher,源碼如下:

fun Modifier.nestedScroll(connection: NestedScrollConnection,dispatcher: NestedScrollDispatcher? = null
){...}
  1. connection:包含了嵌套滑動的和姓邏輯,通過回調可以在子布局獲得滑動事件前,預先消費掉部分或全部手勢偏移量,當然也可以獲取子布局消費后剩下的手勢偏移量。
  2. dispatcher:包含用于父布局的NestedScrollConnection,可以使用包含的 dispatch**系列方法動態控制組件完成滑動。

NestedScrollConnection

interface NestedScrollConnection {fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = Offset.Zerofun onPostScroll(consumed: Offset,available: Offset,source: NestedScrollSource): Offset = Offset.Zerosuspend fun onPreFling(available: Velocity): Velocity = Velocity.Zerosuspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {return Velocity.Zero}
}
  • onPreScroll:在子控件滑動之前,會先使用NestedScrollDispatcher詢問父控件是否需要消費available的偏移量,父控件可以在該方法內計算自身需要消費的量,然后返回自身消費了的偏移量。
  • onPostScroll:在子控件滑動之后,會使用NestedScrollDispatcher通知父控件,告知其consumed的偏移量以及剩余available的偏移量,而父控件則可以根據情況判斷是否還要再偏移,以及使用和子控件同等的偏移還是剩余的偏移。完成之后返回自身消費了的偏移量
  • onPreFling:在子控件進行慣性滑行之前,會先使用NestedScrollDispatcher詢問父控件是否需要消費available的速度值,父控件可以在該方法內計算自身需要消費的量,然后返回自身消費了的速度值
  • onPostFling:在子控件進行慣性滑行之后,會使用NestedScrollDispatcher通知父控件,告知其consumed的速度值以及剩余available的速度值,而父控件則可以根據情況判斷是否還要再偏移,以及使用和子控件同等的速度還是剩余的速度。完成之后返回自身消費了的速度值。

一句話概括:在滑動前父控件可以通過onPreScroll回調先消費部分或全部偏移量;待子控件消費完后,父控件依舊可以通過onPostScroll方法進行消費,區別在于在onPreScroll回調中,父控件是優先消費的,而onPostScroll則是子控件優先消費,fling的兩個方法同理。

NestedScrollDispatcher

class NestedScrollDispatcher {// ....// 在滑動之前調用,將可用的偏移量傳遞給父控件fun dispatchPreScroll(available: Offset, source: NestedScrollSource): Offset {return parent?.onPreScroll(available, source) ?: Offset.Zero}//在滑動之后調用,將已經消費的偏移量以及剩余可用的偏移量傳遞給父控件fun dispatchPostScroll(consumed: Offset,available: Offset,source: NestedScrollSource): Offset {return parent?.onPostScroll(consumed, available, source) ?: Offset.Zero}//在滑動之后,如果產生了慣性滑動,那么需要將相應的速度值傳遞給父控件suspend fun dispatchPreFling(available: Velocity): Velocity {return parent?.onPreFling(available) ?: Velocity.Zero}//在慣性滑動之后,需要將已經消費了的速度值以及剩余可用的速度值傳遞給父控件suspend fun dispatchPostFling(consumed: Velocity, available: Velocity): Velocity {return parent?.onPostFling(consumed, available) ?: Velocity.Zero}

一句話概括:通過dispatchPreScroll方法詢問父控件是否需要消費,方法返回的就是父控件消費了的量,然后就可以做自己的滑動操作了,在滑動完成之后,再通過dispatchPostScroll方法通知父控件。

定制手勢處理

前面提到的手勢處理修飾符都是基于低級別的 pointerInput 修飾符進行封裝實現的。

使用 PointerInput Modifier

// SuspendingPointerInputFilter.kt
fun Modifier.pointerInput(key1: Any?, block: suspend PointerInputScope.() -> Unit
): Modifier
  • keys:當 Composable 發生重組時,如果傳入的 keys 發生了變化,則手勢事件處理過程會被中斷
  • block:在這個 PointerInputScope 作用域代碼塊中,變可以聲明手勢事件的處理邏輯了,發生在協程中。

PointerInputScope

  • detectTapGestures():設置更細粒度的點擊監聽回調
  • detectDragGestures():設置更細粒度的拖動手勢監聽回調
  • detectTransformGestures():雙指拖動、縮放與旋轉手勢操作中更具體的手勢信息
  • detectDragGesturesAfterLongPress():監聽長按后的拖動手勢
  • detectHorizontalDragGestures():監聽水平拖動手勢
  • detectVerticalDragGestures():監聽垂直拖動手勢
  • forEachGesture:允許用戶可以對每一個手勢事件序列進行相同的定制處理

awaitPointerEventScope

AwaitPointerEventScope 作用域中,可以使用 Compose 中所有低級別的手勢處理掛起方法。

suspend fun <R> awaitPointerEventScope(block: suspend AwaitPointerEventScope.() -> R
): R
API名稱作用
awaitPointerEvent手勢事件
awaitFirstDown第一根手指的按下事件
drag拖動事件
horizontalDrag水平拖動事件
verticalDrag垂直拖動事件
awaitDragOrCancellation單次拖動事件
awaitHorizontalDragOrCancellation單次水平拖動事件
awaitVerticalDragOrCancellation單次垂直拖動事件
awaitTouchSlopOrCancellation有效拖動事件
awaitHorizontalTouchSlopOrCancellation有效水平拖動事件
awaitVerticalTouchSlopOrCancellation有效垂直拖動事件

參考資料:巧用Compose來實現手勢拖拽效果

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70840.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70840.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70840.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ue5 3dcesium中從本地配置文件讀取路3dtilles的路徑

關卡藍圖中獲得3dtiles的引用 拉出設置url 設置路徑 至于設置的路徑從哪里來 可以使用varest讀取文件里的接送字符串 path中配置地址 path變量的值為: Data/VillageStartMapConfig.json此地址代表content的地下的data文件夾里的config.json文件 {"FilePath": &quo…

音視頻入門基礎:RTP專題(12)——RTP中的NAL Unit Type簡介

一、引言 RTP封裝H.264時&#xff0c;RTP對NALU Header的nal_unit_type附加了擴展含義。 由《音視頻入門基礎&#xff1a;H.264專題&#xff08;4&#xff09;——NALU Header&#xff1a;forbidden_zero_bit、nal_ref_idc、nal_unit_type簡介》可以知道&#xff0c;nal_unit…

搜索賦能:大型語言模型的知識增強與智能提升

引言 近年來&#xff0c;大型語言模型&#xff08;LLM&#xff09;取得了顯著的進展&#xff0c;并在各個領域展現出強大的能力。然而&#xff0c;LLM也存在一些局限性&#xff0c;尤其是在知識庫方面。由于訓練數據的局限性&#xff0c;LLM無法獲取最新的知識&#xff0c;也無…

EX_25/2/24

寫一個三角形類&#xff0c;擁有私有成員 a,b,c 三條邊 寫好構造函數初始化 abc 以及 abc 的set get 接口 再寫一個等腰三角形類&#xff0c;繼承自三角形類 1&#xff1a;寫好構造函數&#xff0c;初始化三條邊 2&#xff1a;要求無論如何&#xff0c;等腰三角形類對象&#x…

nv docker image 下載與使用命令備忘

1&#xff0c;系統需求 Requirements for GPU Simulation GPU Architectures Volta, Turing, Ampere, Ada, Hopper NVIDIA GPU with Compute Capability 7.0 CUDA 11.x (Driver 470.57.02), 12.x (Driver 525.60.13) Supported Systems CPU architectures x86_64, ARM…

學習記錄:初次學習使用transformers進行大模型微調

初次使用transformers進行大模型微調 環境&#xff1a; 電腦配置&#xff1a; 筆記本電腦&#xff1a;I5&#xff08;6核12線程&#xff09; 16G RTX3070&#xff08;8G顯存&#xff09; 需要自行解決科學上網 Python環境&#xff1a; python版本:3.8.8 大模型&#xff1a…

【Java學習】Object類與接口

面向對象系列五 一、引用 1.自調傳自與this類型 2.類變量引用 3.重寫時的發生 二、Object類 1.toString 2.equals 3.hashCode 4.clone 三、排序規則接口 1.Comparable 2.Comparator 一、引用 1.自調傳自與this類型 似復刻變量調用里面的非靜態方法時&#xff0c;都…

OpenEuler學習筆記(三十五):搭建代碼托管服務器

以下是主流的代碼托管軟件分類及推薦&#xff0c;涵蓋自托管和云端方案&#xff0c;您可根據團隊規模、功能需求及資源情況選擇&#xff1a; 一、自托管代碼托管平臺&#xff08;可私有部署&#xff09; 1. GitLab 簡介: 功能全面的 DevOps 平臺&#xff0c;支持代碼托管、C…

Vscode無法加載文件,因為在此系統上禁止運行腳本

1.在 vscode 終端執行 get-ExecutionPolicy 如果返回是Restricted&#xff0c;說明是禁止狀態。 2.在 vscode 終端執行set-ExecutionPolicy RemoteSigned 爆紅說明沒有設置成功 3.在 vscode 終端執行Set-ExecutionPolicy -Scope CurrentUser RemoteSigned 然后成功后你再在終…

Transformer 架構 理解

大家讀完覺得有幫助記得關注和點贊&#xff01;&#xff01;&#xff01; Transformer 架構&#xff1a;encoder/decoder 內部細節。 的介紹&#xff0c;說明 Transformer 架構相比當時主流的 RNN/CNN 架構的創新之處&#xff1a; 在 transformer 之前&#xff0c;最先進的架構…

事務的4個特性和4個隔離級別

事務的4個特性和4個隔離級別 1. 什么是事務2. 事務的ACID特性2.1 原子性2.2 一致性2.3 持久性2.4 隔離性 3. 事務的創建4. 事務并發時出現的問題4.1 DIRTY READ 臟讀4.2 NON - REPEATABLR READ 不可重復讀4.3 PHANTOM READ 幻讀 5. 事務的隔離級別5.1 READ UNCOMMITTED 讀未提交…

LeetCode熱題100- 字符串解碼【JavaScript講解】

古語有云&#xff1a;“事以密成&#xff0c;語以泄敗”&#xff01; 關于字符串解碼&#xff1a; 題目&#xff1a;題解&#xff1a;js代碼&#xff1a;代碼中遇到的方法&#xff1a;repeat方法&#xff1a;為什么這里不用this.strstack.push(result)&#xff1f; 題目&#x…

水利工程安全包括哪幾個方面

水利工程安全培訓的內容主要包括以下幾個方面&#xff1a; 基礎知識和技能培訓 &#xff1a; 法律法規 &#xff1a;學習水利工程相關的安全生產法律法規&#xff0c;了解安全生產標準及規范。 事故案例 &#xff1a;通過分析事故案例&#xff0c;了解事故原因和教訓&#x…

淺談新能源汽車充電樁建設問題分析及解決方案

摘要&#xff1a; 在全球倡導低碳減排的大背景下&#xff0c;新能源成為熱門行業在全球范圍內得以開展。汽車尾氣排放會在一定程度上加重溫室效應&#xff0c;并且化石能源的日漸緊缺也迫切對新能源汽車發展提出新要求。現階段的新能源汽車以電力汽車為主&#xff0c;與燃油汽…

05-1基于vs2022的c語言筆記——運算符

目錄 前言 5.運算符和表達式 5-1-1 加減乘除運算符 1.把變量進行加減乘除運算 2.把常量進行加減乘除運算 3.對于比較大的數&#xff08;往數軸正方向或者負方向&#xff09;&#xff0c;要注意占位符的選取 4.浮點數的加減乘除 5-1-2取余/取模運算符 1.基本規則 2.c語…

ubuntu:換源安裝docker-ce和docker-compose

更新apt源 apt換源&#xff1a;ubuntu&#xff1a;更新阿里云apt源-CSDN博客 安裝docker-ce 1、更新軟件源 sudo apt update2、安裝基本軟件 sudo apt-get install apt-transport-https ca-certificates curl software-properties-common lrzsz -y3、指定使用阿里云鏡像 su…

0—QT ui界面一覽

2025.2.26&#xff0c;感謝gpt4 1.控件盒子 1. Layouts&#xff08;布局&#xff09; 布局控件用于組織界面上的控件&#xff0c;確保它們的位置和排列方式合理。 Vertical Layout&#xff08;垂直布局&#xff09; &#xff1a;將控件按垂直方向排列。 建議&#xff1a;適…

Apache Doris 索引的全面剖析與使用指南

搞大數據開發的都知道&#xff0c;想要在海量數據里快速查數據&#xff0c;就像在星圖里找一顆特定的星星&#xff0c;賊費勁。不過別慌&#xff0c;數據庫索引就是咱們的 “定位神器”&#xff0c;能讓查詢效率直接起飛&#xff01;就拿 Apache Doris 這個超火的分析型數據庫來…

docker file中ADD命令的介紹

在 Docker 的世界里&#xff0c;Dockerfile 是一個用于定義鏡像內容和行為的腳本文件。其中&#xff0c;ADD 指令是 Dockerfile 中一個非常重要的命令&#xff0c;用于將文件或目錄從主機文件系統復制到容器的文件系統中。本文將詳細介紹 ADD 指令的作用、使用方式以及一些最佳…

從零到一:如何用阿里云百煉和火山引擎搭建專屬 AI 助手(DeepSeek)?

本文首發&#xff1a;從零到一&#xff1a;如何用阿里云百煉和火山引擎搭建專屬 AI 助手&#xff08;DeepSeek&#xff09;&#xff1f; 阿里云百煉和火山引擎都推出了免費的 DeepSeek 模型體驗額度&#xff0c;今天我和大家一起搭建一個本地的專屬 AI 助手。  阿里云百煉為 …