Android و ChromeOS انواع API را ارائه میکنند تا به شما در ساخت برنامههایی کمک کنند که تجربهای استثنایی از قلم را به کاربران ارائه دهند. کلاس MotionEvent
اطلاعاتی در مورد تعامل قلم با صفحه نمایش، از جمله فشار قلم، جهت گیری، شیب، شناور و تشخیص کف دست را در معرض دید قرار می دهد. کتابخانههای گرافیکی و پیشبینی حرکت با تأخیر کم، رندرینگ قلم روی صفحه را افزایش میدهند تا تجربهای طبیعی و شبیه به قلم و کاغذ ارائه دهند.
MotionEvent
کلاس MotionEvent
نشان دهنده تعاملات ورودی کاربر مانند موقعیت و حرکت اشاره گرهای لمسی روی صفحه است. برای ورودی قلم، MotionEvent
همچنین دادههای فشار، جهت، شیب و شناور را نشان میدهد.
داده های رویداد
برای دسترسی به دادههای MotionEvent
، یک اصلاحکننده pointerInput
به مؤلفهها اضافه کنید:
@Composable
fun Greeting() {
Text(
text = "Hello, Android!", textAlign = TextAlign.Center, style = TextStyle(fontSize = 5.em),
modifier = Modifier
.pointerInput(Unit) {
awaitEachGesture {
while (true) {
val event = awaitPointerEvent()
event.changes.forEach { println(it) }
}
}
},
)
}
یک شی MotionEvent
داده های مربوط به جنبه های زیر یک رویداد UI را ارائه می دهد:
- اقدامات: تعامل فیزیکی با دستگاه - لمس کردن صفحه نمایش، حرکت یک نشانگر روی سطح صفحه نمایش، نگه داشتن نشانگر روی سطح صفحه نمایش
- اشاره گرها: شناسه اشیایی که با صفحه در تعامل هستند - انگشت، قلم، ماوس
- محور: نوع داده - مختصات x و y، فشار، شیب، جهت، و شناور (فاصله)
اقدامات
برای اجرای پشتیبانی از قلم، باید بدانید که کاربر چه عملکردی را انجام می دهد.
MotionEvent
طیف گسترده ای از ثابت های ACTION
را ارائه می دهد که رویدادهای حرکت را تعریف می کنند. مهمترین اقدامات برای قلم شامل موارد زیر است:
اقدام | توضیحات |
---|---|
ACTION_DOWN ACTION_POINTER_DOWN | اشاره گر با صفحه تماس برقرار کرده است. |
ACTION_MOVE | اشاره گر روی صفحه در حال حرکت است. |
ACTION_UP ACTION_POINTER_UP | اشاره گر دیگر با صفحه در تماس نیست |
ACTION_CANCEL | وقتی مجموعه حرکت قبلی یا فعلی باید لغو شود. |
برنامه شما میتواند کارهایی مانند شروع یک سکته مغزی جدید در هنگام وقوع ACTION_DOWN
، کشیدن ضربه با ACTION_MOVE,
و پایان دادن به ضربه زمانی که ACTION_UP
فعال میشود، انجام دهد.
مجموعه اقدامات MotionEvent
از ACTION_DOWN
تا ACTION_UP
برای یک اشاره گر معین، مجموعه حرکت نامیده می شود.
اشاره گرها
بیشتر صفحهها چند لمسی هستند: سیستم برای هر انگشت، قلم، ماوس یا هر شی اشارهای که با صفحه در تعامل است یک اشارهگر اختصاص میدهد. یک نشانگر به شما امکان میدهد اطلاعات محور را برای یک نشانگر خاص، مانند موقعیت لمس انگشت اول صفحه یا انگشت دوم، به دست آورید.
ایندکس های اشاره گر از صفر تا تعداد نشانگرهای برگشتی توسط MotionEvent#pointerCount()
منهای 1 متغیر است.
با روش getAxisValue(axis, pointerIndex)
می توان به مقادیر محور اشاره گرها دسترسی داشت. هنگامی که شاخص اشاره گر حذف می شود، سیستم مقدار اولین اشاره گر، نشانگر صفر (0) را برمی گرداند.
اشیاء MotionEvent
حاوی اطلاعاتی در مورد نوع اشاره گر در حال استفاده هستند. شما می توانید نوع اشاره گر را با تکرار از طریق شاخص های اشاره گر و فراخوانی متد getToolType(pointerIndex)
بدست آورید.
برای کسب اطلاعات بیشتر در مورد اشاره گرها، به کنترل حرکات چند لمسی مراجعه کنید.
ورودی های قلم
میتوانید ورودیهای قلم را با TOOL_TYPE_STYLUS
فیلتر کنید:
val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)
قلم همچنین می تواند گزارش دهد که به عنوان یک پاک کن با TOOL_TYPE_ERASER
استفاده می شود:
val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)
داده های محور قلم
ACTION_DOWN
و ACTION_MOVE
داده های محور را در مورد قلم، یعنی مختصات x و y، فشار، جهت، شیب و شناور ارائه می دهند.
برای فعال کردن دسترسی به این دادهها، MotionEvent
API getAxisValue(int)
ارائه میکند، که در آن پارامتر هر یک از شناسههای محور زیر است:
محور | مقدار بازگشتی getAxisValue() |
---|---|
AXIS_X | مختصات X یک رویداد حرکتی. |
AXIS_Y | Y مختصات یک رویداد حرکتی. |
AXIS_PRESSURE | برای یک صفحه لمسی یا پد لمسی، فشار وارد شده توسط انگشت، قلم، یا اشاره گر دیگر. برای ماوس یا گوی توپ، اگر دکمه اصلی فشار داده شود، 1، در غیر این صورت 0. |
AXIS_ORIENTATION | برای صفحه لمسی یا پد لمسی، جهت انگشت، قلم، یا دیگر اشاره گر نسبت به صفحه عمودی دستگاه. |
AXIS_TILT | زاویه شیب قلم بر حسب رادیان. |
AXIS_DISTANCE | فاصله قلم از صفحه نمایش. |
برای مثال، MotionEvent.getAxisValue(AXIS_X)
مختصات x را برای اولین اشاره گر برمی گرداند.
همچنین به کنترل حرکات چند لمسی مراجعه کنید.
موقعیت
می توانید مختصات x و y یک اشاره گر را با فراخوانی های زیر بازیابی کنید:
-
MotionEvent#getAxisValue(AXIS_X)
یاMotionEvent#getX()
-
MotionEvent#getAxisValue(AXIS_Y)
یاMotionEvent#getY()
فشار
می توانید فشار اشاره گر را با MotionEvent#getAxisValue(AXIS_PRESSURE)
یا برای اولین اشاره گر، MotionEvent#getPressure()
بازیابی کنید.
مقدار فشار برای صفحات لمسی یا پد لمسی مقداری بین 0 (بدون فشار) و 1 است، اما مقادیر بالاتر بسته به کالیبراسیون صفحه نمایش قابل بازگشت است.
جهت گیری
جهت نشان می دهد که قلم به کدام جهت اشاره می کند.
جهت گیری اشاره گر را می توان با استفاده از getAxisValue(AXIS_ORIENTATION)
یا getOrientation()
(برای اولین اشاره گر) بازیابی کرد.
برای یک قلم، جهت به صورت یک مقدار رادیانی بین 0 تا pi (𝛑) در جهت عقربههای ساعت یا 0 تا -pi در خلاف جهت عقربههای ساعت برگردانده میشود.
جهتیابی شما را قادر میسازد تا یک قلم موی واقعی را پیادهسازی کنید. به عنوان مثال، اگر قلم نشان دهنده یک قلم مو صاف باشد، عرض قلم مو به جهت قلم بستگی دارد.
شیب
شیب میل قلم را نسبت به صفحه اندازه گیری می کند.
Tilt زاویه مثبت قلم را بر حسب رادیان برمیگرداند، جایی که صفر عمود بر صفحه است و 𝛑/2 روی سطح صاف است.
زاویه شیب را می توان با استفاده از getAxisValue(AXIS_TILT)
بازیابی کرد (بدون میانبری برای اولین اشاره گر).
از Tilt می توان برای بازتولید تا حد امکان ابزارهای واقعی مانند شبیه سازی سایه با مداد کج شده استفاده کرد.
شناور
فاصله قلم از صفحه نمایش را می توان با getAxisValue(AXIS_DISTANCE)
بدست آورد. این روش با دور شدن قلم از صفحه، مقداری را از 0.0 (تماس با صفحه) به مقادیر بالاتر برمی گرداند. فاصله شناور بین صفحه نمایش و نوک (نقطه) قلم بستگی به سازنده صفحه و قلم دارد. از آنجا که پیادهسازیها میتوانند متفاوت باشند، برای عملکردهای حیاتی برنامه به مقادیر دقیق تکیه نکنید.
از شناور قلم میتوان برای پیشنمایش اندازه قلم مو استفاده کرد یا نشان داد که یک دکمه قرار است انتخاب شود.
توجه: Compose اصلاحکنندههایی را ارائه میکند که بر وضعیت تعاملی عناصر UI تأثیر میگذارند:
-
hoverable
: مؤلفه را به گونه ای پیکربندی کنید که با استفاده از رویدادهای ورود و خروج اشاره گر قابل شناور باشد. -
indication
: جلوه های بصری را برای این مؤلفه در هنگام وقوع فعل و انفعالات ترسیم می کند.
رد کف دست، ناوبری، و ورودی های ناخواسته
گاهی اوقات صفحه نمایش های چند لمسی می توانند لمس های ناخواسته را ثبت کنند، به عنوان مثال، زمانی که کاربر به طور طبیعی دست خود را برای پشتیبانی در حین دست نویسی روی صفحه قرار می دهد. رد کف دست مکانیزمی است که این رفتار را تشخیص می دهد و به شما اطلاع می دهد که آخرین مجموعه MotionEvent
باید لغو شود.
در نتیجه، باید تاریخچه ای از ورودی های کاربر را نگه دارید تا لمس های ناخواسته از روی صفحه حذف شوند و ورودی های قانونی کاربر دوباره ارائه شوند.
ACTION_CANCEL و FLAG_CANCELED
ACTION_CANCEL
و FLAG_CANCELED
هر دو طراحی شدهاند تا به شما اطلاع دهند که مجموعه قبلی MotionEvent
باید از آخرین ACTION_DOWN
لغو شود، بنابراین میتوانید، برای مثال، آخرین ضربه را برای یک برنامه طراحی برای یک اشارهگر معین لغو کنید.
ACTION_CANCEL
اضافه شده در Android 1.0 (سطح API 1)
ACTION_CANCEL
نشان میدهد که مجموعه قبلی رویدادهای حرکتی باید لغو شوند.
ACTION_CANCEL
زمانی فعال میشود که یکی از موارد زیر شناسایی شود:
- حرکات ناوبری
- رد کف دست
هنگامی که ACTION_CANCEL
راه اندازی می شود، باید اشاره گر فعال را با getPointerId ( getActionIndex() )
شناسایی کنید. سپس stroke ایجاد شده با آن اشاره گر را از تاریخچه ورودی حذف کنید و صحنه را دوباره رندر کنید.
FLAG_CANCELED
اضافه شده در Android 13 (سطح API 33)
FLAG_CANCELED
نشان میدهد که اشارهگر به سمت بالا یک لمس ناخواسته کاربر بوده است. این پرچم معمولاً زمانی تنظیم میشود که کاربر به طور تصادفی صفحه را لمس کند، مثلاً با گرفتن دستگاه یا قرار دادن کف دست روی صفحه.
به صورت زیر به مقدار پرچم دسترسی پیدا می کنید:
val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED
اگر پرچم تنظیم شده است، باید آخرین مجموعه MotionEvent
را از آخرین ACTION_DOWN
از این نشانگر لغو کنید.
مانند ACTION_CANCEL
، نشانگر را می توان با getPointerId(actionIndex)
پیدا کرد.
تمام صفحه، لبه به لبه، و حرکات ناوبری
اگر برنامه ای تمام صفحه است و دارای عناصر قابل اجرا در نزدیکی لبه است، مانند بوم نقاشی یا برنامه یادداشت برداری، کشیدن انگشت از پایین صفحه برای نمایش پیمایش یا انتقال برنامه به پس زمینه ممکن است منجر به ایجاد ناخواسته شود. روی بوم لمس کنید
برای جلوگیری از ایجاد لمسهای ناخواسته در برنامهتان توسط اشارهها، میتوانید از مزایای insets و ACTION_CANCEL
استفاده کنید.
همچنین بخش رد کف دست، پیمایش و ورودیهای ناخواسته را ببینید.
از متد setSystemBarsBehavior()
و BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
WindowInsetsController
برای جلوگیری از حرکات ناوبری از ایجاد رویدادهای لمسی ناخواسته استفاده کنید:
// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
برای کسب اطلاعات بیشتر در مورد مدیریت ژستهای درونی و ژستها، رجوع کنید به:
- نوارهای سیستم را برای حالت همهجانبه پنهان کنید
- از سازگاری با ناوبری اشاره ای اطمینان حاصل کنید
- محتوا را لبه به لبه در برنامه خود نمایش دهید
تاخیر کم
تأخیر زمان مورد نیاز سخت افزار، سیستم و برنامه برای پردازش و ارائه ورودی کاربر است.
تأخیر = پردازش ورودی سخت افزار و سیستم عامل + پردازش برنامه + ترکیب سیستم
- رندر سخت افزاری
منبع تأخیر
- ثبت قلم با صفحه لمسی (سخت افزار): اتصال بی سیم اولیه هنگامی که قلم و سیستم عامل برای ثبت و همگام سازی با هم ارتباط برقرار می کنند.
- نرخ نمونهبرداری لمسی (سختافزار): تعداد دفعاتی که در هر ثانیه یک صفحه لمسی بررسی میکند که آیا نشانگر سطح را لمس میکند، از 60 تا 1000 هرتز.
- پردازش ورودی (برنامه): اعمال رنگ، جلوههای گرافیکی و تبدیل در ورودی کاربر.
- رندر گرافیکی (OS + سخت افزار): تعویض بافر، پردازش سخت افزار.
گرافیک کم تاخیر
کتابخانه گرافیکی با تأخیر کم Jetpack زمان پردازش بین ورودی کاربر و رندر روی صفحه را کاهش می دهد.
این کتابخانه با اجتناب از رندر چند بافری و استفاده از تکنیک رندر جلو بافر، که به معنای نوشتن مستقیم روی صفحه است، زمان پردازش را کاهش می دهد.
رندر جلو بافر
بافر جلو حافظه ای است که صفحه نمایش برای رندر استفاده می کند. این نزدیکترین اپلیکیشنی است که میتوانند مستقیماً روی صفحه طراحی کنند. کتابخانه با تأخیر کم به برنامهها امکان میدهد مستقیماً به بافر جلو ارائه شوند. این کار با جلوگیری از تعویض بافر، که می تواند برای رندر معمولی چند بافری یا رندر دو بافری (متداول ترین مورد) اتفاق بیفتد، عملکرد را بهبود می بخشد.
در حالی که رندر جلو بافر یک تکنیک عالی برای رندر کردن یک ناحیه کوچک از صفحه است، اما برای بهروزرسانی کل صفحه طراحی نشده است. با رندر بافر جلو، برنامه در حال رندر کردن محتوا به بافری است که نمایشگر در حال خواندن از آن است. در نتیجه، امکان رندر کردن مصنوعات یا پاره شدن وجود دارد (به زیر مراجعه کنید).
کتابخانه با تأخیر کم از Android 10 (سطح API 29) و بالاتر و در دستگاههای ChromeOS دارای Android 10 (سطح API 29) و بالاتر در دسترس است.
وابستگی ها
کتابخانه با تأخیر کم مؤلفه هایی را برای اجرای رندر جلو بافر فراهم می کند. کتابخانه به عنوان یک وابستگی در فایل build.gradle
ماژول برنامه اضافه شده است:
dependencies {
implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}
تماس های GLFrontBufferRenderer
کتابخانه با تأخیر کم شامل رابط GLFrontBufferRenderer.Callback
است که روش های زیر را تعریف می کند:
کتابخانه با تأخیر کم در مورد نوع داده ای که با GLFrontBufferRenderer
استفاده می کنید نظری ندارد.
با این حال، کتابخانه داده ها را به عنوان جریانی از صدها نقطه داده پردازش می کند. و بنابراین، داده های خود را برای بهینه سازی استفاده و تخصیص حافظه طراحی کنید.
تماس های تلفنی
برای فعال کردن بازخوانی رندر، GLFrontBufferedRenderer.Callback
را پیاده سازی کنید و onDrawFrontBufferedLayer()
و onDrawDoubleBufferedLayer()
را لغو کنید. GLFrontBufferedRenderer
از فراخوان ها برای ارائه داده های شما به بهینه ترین شکل ممکن استفاده می کند.
val callback = object: GLFrontBufferedRenderer.Callback<DATA_TYPE> {
override fun onDrawFrontBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
param: DATA_TYPE
) {
// OpenGL for front buffer, short, affecting small area of the screen.
}
override fun onDrawMultiDoubleBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
params: Collection<DATA_TYPE>
) {
// OpenGL full scene rendering.
}
}
یک نمونه از GLFrontBufferedRenderer را اعلام کنید
GLFrontBufferedRenderer
را با ارائه SurfaceView
و تماسهایی که قبلا ایجاد کردهاید، آماده کنید. GLFrontBufferedRenderer
رندر را در بافر جلویی و دوبل با استفاده از callback های شما بهینه می کند:
var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)
رندرینگ
با فراخوانی متد renderFrontBufferedLayer()
، که فراخوانی onDrawFrontBufferedLayer()
را فعال میکند، رندر بافر جلو شروع میشود.
هنگامی که commit()
را فراخوانی میکنید، رندر دو بافر از سر گرفته میشود، که پاسخ تماس onDrawMultiDoubleBufferedLayer()
را راهاندازی میکند.
در مثال زیر، هنگامی که کاربر شروع به کشیدن روی صفحه ( ACTION_DOWN
) میکند و نشانگر را به اطراف ( ACTION_MOVE
) حرکت میدهد، فرآیند به بافر جلو (رندر سریع) ارائه میشود. هنگامی که نشانگر از سطح صفحه خارج میشود، فرآیند به بافر دوگانه ارائه میشود ( ACTION_UP
).
میتوانید از requestUnbufferedDispatch()
استفاده کنید تا بخواهید که سیستم ورودی رویدادهای حرکتی را دستهبندی نکند، بلکه آنها را به محض در دسترس بودن تحویل دهد:
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
// Deliver input events as soon as they arrive.
view.requestUnbufferedDispatch(motionEvent)
// Pointer is in contact with the screen.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_MOVE -> {
// Pointer is moving.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_UP -> {
// Pointer is not in contact in the screen.
glFrontBufferRenderer.commit()
}
MotionEvent.CANCEL -> {
// Cancel front buffer; remove last motion set from the screen.
glFrontBufferRenderer.cancel()
}
}
رندر کردن بایدها و نبایدها
بخش های کوچک صفحه، دست خط، طراحی، طراحی.
به روز رسانی تمام صفحه، حرکت متحرک، بزرگنمایی. می تواند منجر به پارگی شود.
پاره شدن
پاره شدن زمانی اتفاق میافتد که صفحه نمایش بهروزرسانی میشود در حالی که بافر صفحه نمایش همزمان در حال تغییر است. بخشی از صفحه نمایش داده های جدید را نشان می دهد، در حالی که بخشی دیگر داده های قدیمی را نشان می دهد.
پیش بینی حرکت
کتابخانه پیشبینی حرکت Jetpack با تخمین مسیر ضربه کاربر و ارائه نقاط مصنوعی و موقت به رندر، تأخیر درک شده را کاهش میدهد.
کتابخانه پیشبینی حرکت ورودیهای واقعی کاربر را به عنوان اشیاء MotionEvent
دریافت میکند. اشیاء حاوی اطلاعاتی در مورد مختصات x و y، فشار و زمان هستند که توسط پیش بینی کننده حرکت برای پیش بینی اشیاء MotionEvent
در آینده استفاده می شود.
اشیاء پیش بینی شده MotionEvent
فقط تخمینی هستند. رویدادهای پیشبینیشده میتوانند تأخیر درک شده را کاهش دهند، اما دادههای پیشبینیشده باید پس از دریافت با دادههای واقعی MotionEvent
جایگزین شوند.
کتابخانه پیشبینی حرکت از Android نسخه 4.4 (سطح API 19) و بالاتر و در دستگاههای ChromeOS دارای Android 9 (سطح API 28) و بالاتر در دسترس است.
وابستگی ها
کتابخانه پیش بینی حرکت، اجرای پیش بینی را فراهم می کند. کتابخانه به عنوان یک وابستگی در فایل build.gradle
ماژول برنامه اضافه شده است:
dependencies {
implementation "androidx.input:input-motionprediction:1.0.0-beta01"
}
پیاده سازی
کتابخانه پیشبینی حرکت شامل رابط MotionEventPredictor
است که روشهای زیر را تعریف میکند:
-
record()
: اشیاءMotionEvent
را به عنوان رکوردی از اقدامات کاربر ذخیره می کند -
predict()
: یکMotionEvent
پیش بینی شده را برمی گرداند
یک نمونه از MotionEventPredictor
را اعلام کنید
var motionEventPredictor = MotionEventPredictor.newInstance(view)
پیش بینی را با داده تغذیه کنید
motionEventPredictor.record(motionEvent)
پیش بینی کنید
when (motionEvent.action) {
MotionEvent.ACTION_MOVE -> {
val predictedMotionEvent = motionEventPredictor?.predict()
if(predictedMotionEvent != null) {
// use predicted MotionEvent to inject a new artificial point
}
}
}
بایدها و نبایدهای پیش بینی حرکت
هنگامی که یک نقطه پیش بینی شده جدید اضافه می شود، نقاط پیش بینی را حذف کنید.
از نقاط پیش بینی برای رندر نهایی استفاده نکنید.
برنامه های یادداشت برداری
ChromeOS به برنامه شما امکان می دهد برخی از اقدامات یادداشت برداری را اعلام کند.
برای ثبت یک برنامه به عنوان یک برنامه یادداشت برداری در ChromeOS، به سازگاری ورودی مراجعه کنید.
برای ثبت یک برنامه به عنوان یادداشت برداری در Android، به ایجاد برنامه یادداشت برداری مراجعه کنید.
Android 14 (سطح API 34)، هدف ACTION_CREATE_NOTE
را معرفی کرد، که به برنامه شما امکان میدهد فعالیت یادداشتبرداری را در صفحه قفل شروع کند.
تشخیص جوهر دیجیتال با کیت ML
با تشخیص جوهر دیجیتال کیت ML ، برنامه شما میتواند متن دستنویس روی سطح دیجیتال را به صدها زبان تشخیص دهد. شما همچنین می توانید طرح ها را طبقه بندی کنید.
ML Kit کلاس Ink.Stroke.Builder
را برای ایجاد اشیاء Ink
ارائه میکند که میتوانند توسط مدلهای یادگیری ماشین پردازش شوند تا دستنویس به متن تبدیل شود.
این مدل علاوه بر تشخیص دست خط، میتواند حرکاتی مانند حذف و دایره را تشخیص دهد.
برای اطلاعات بیشتر به تشخیص جوهر دیجیتال مراجعه کنید.
منابع اضافی
راهنمای توسعه دهندگان
Codelabs
،Android و ChromeOS انواع API را ارائه میکنند تا به شما در ساخت برنامههایی کمک کنند که تجربهای استثنایی از قلم را به کاربران ارائه دهند. کلاس MotionEvent
اطلاعاتی در مورد تعامل قلم با صفحه نمایش، از جمله فشار قلم، جهت گیری، شیب، شناور و تشخیص کف دست را در معرض دید قرار می دهد. کتابخانههای گرافیکی و پیشبینی حرکت با تأخیر کم، رندرینگ قلم روی صفحه را افزایش میدهند تا تجربهای طبیعی و شبیه به قلم و کاغذ ارائه دهند.
MotionEvent
کلاس MotionEvent
نشان دهنده تعاملات ورودی کاربر مانند موقعیت و حرکت اشاره گرهای لمسی روی صفحه است. برای ورودی قلم، MotionEvent
همچنین دادههای فشار، جهت، شیب و شناور را نشان میدهد.
داده های رویداد
برای دسترسی به دادههای MotionEvent
، یک اصلاحکننده pointerInput
به مؤلفهها اضافه کنید:
@Composable
fun Greeting() {
Text(
text = "Hello, Android!", textAlign = TextAlign.Center, style = TextStyle(fontSize = 5.em),
modifier = Modifier
.pointerInput(Unit) {
awaitEachGesture {
while (true) {
val event = awaitPointerEvent()
event.changes.forEach { println(it) }
}
}
},
)
}
یک شی MotionEvent
داده های مربوط به جنبه های زیر یک رویداد UI را ارائه می دهد:
- اقدامات: تعامل فیزیکی با دستگاه - لمس کردن صفحه نمایش، حرکت یک نشانگر روی سطح صفحه نمایش، نگه داشتن نشانگر روی سطح صفحه نمایش
- اشاره گرها: شناسه اشیایی که با صفحه در تعامل هستند - انگشت، قلم، ماوس
- محور: نوع داده - مختصات x و y، فشار، شیب، جهت، و شناور (فاصله)
اقدامات
برای اجرای پشتیبانی از قلم، باید بدانید که کاربر چه عملکردی را انجام می دهد.
MotionEvent
طیف گسترده ای از ثابت های ACTION
را ارائه می دهد که رویدادهای حرکت را تعریف می کنند. مهمترین اقدامات برای قلم شامل موارد زیر است:
اقدام | توضیحات |
---|---|
ACTION_DOWN ACTION_POINTER_DOWN | اشاره گر با صفحه تماس برقرار کرده است. |
ACTION_MOVE | اشاره گر روی صفحه در حال حرکت است. |
ACTION_UP ACTION_POINTER_UP | اشاره گر دیگر با صفحه در تماس نیست |
ACTION_CANCEL | وقتی مجموعه حرکت قبلی یا فعلی باید لغو شود. |
برنامه شما میتواند کارهایی مانند شروع یک سکته مغزی جدید در هنگام وقوع ACTION_DOWN
، کشیدن ضربه با ACTION_MOVE,
و پایان دادن به ضربه زمانی که ACTION_UP
فعال میشود، انجام دهد.
مجموعه اقدامات MotionEvent
از ACTION_DOWN
تا ACTION_UP
برای یک اشاره گر معین، مجموعه حرکت نامیده می شود.
اشاره گرها
بیشتر صفحهها چند لمسی هستند: سیستم برای هر انگشت، قلم، ماوس یا هر شی اشارهای که با صفحه در تعامل است یک اشارهگر اختصاص میدهد. یک نشانگر به شما امکان میدهد اطلاعات محور را برای یک نشانگر خاص، مانند موقعیت لمس انگشت اول صفحه یا انگشت دوم، به دست آورید.
ایندکس های اشاره گر از صفر تا تعداد نشانگرهای برگشتی توسط MotionEvent#pointerCount()
منهای 1 متغیر است.
با روش getAxisValue(axis, pointerIndex)
می توان به مقادیر محور اشاره گرها دسترسی داشت. هنگامی که شاخص اشاره گر حذف می شود، سیستم مقدار اولین اشاره گر، نشانگر صفر (0) را برمی گرداند.
اشیاء MotionEvent
حاوی اطلاعاتی در مورد نوع اشاره گر در حال استفاده هستند. شما می توانید نوع اشاره گر را با تکرار از طریق شاخص های اشاره گر و فراخوانی متد getToolType(pointerIndex)
بدست آورید.
برای کسب اطلاعات بیشتر در مورد اشاره گرها، به کنترل حرکات چند لمسی مراجعه کنید.
ورودی های قلم
میتوانید ورودیهای قلم را با TOOL_TYPE_STYLUS
فیلتر کنید:
val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)
قلم همچنین می تواند گزارش دهد که به عنوان یک پاک کن با TOOL_TYPE_ERASER
استفاده می شود:
val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)
داده های محور قلم
ACTION_DOWN
و ACTION_MOVE
داده های محور را در مورد قلم، یعنی مختصات x و y، فشار، جهت، شیب و شناور ارائه می دهند.
برای فعال کردن دسترسی به این دادهها، MotionEvent
API getAxisValue(int)
ارائه میکند، که در آن پارامتر هر یک از شناسههای محور زیر است:
محور | مقدار بازگشتی getAxisValue() |
---|---|
AXIS_X | مختصات X یک رویداد حرکتی. |
AXIS_Y | Y مختصات یک رویداد حرکتی. |
AXIS_PRESSURE | برای یک صفحه لمسی یا پد لمسی، فشار وارد شده توسط انگشت، قلم، یا اشاره گر دیگر. برای ماوس یا گوی توپ، اگر دکمه اصلی فشار داده شود، 1، در غیر این صورت 0. |
AXIS_ORIENTATION | برای صفحه لمسی یا پد لمسی، جهت انگشت، قلم، یا دیگر اشاره گر نسبت به صفحه عمودی دستگاه. |
AXIS_TILT | زاویه شیب قلم بر حسب رادیان. |
AXIS_DISTANCE | فاصله قلم از صفحه نمایش. |
برای مثال، MotionEvent.getAxisValue(AXIS_X)
مختصات x را برای اولین اشاره گر برمی گرداند.
همچنین به کنترل حرکات چند لمسی مراجعه کنید.
موقعیت
می توانید مختصات x و y یک اشاره گر را با فراخوانی های زیر بازیابی کنید:
-
MotionEvent#getAxisValue(AXIS_X)
یاMotionEvent#getX()
-
MotionEvent#getAxisValue(AXIS_Y)
یاMotionEvent#getY()
فشار
می توانید فشار اشاره گر را با MotionEvent#getAxisValue(AXIS_PRESSURE)
یا برای اولین اشاره گر، MotionEvent#getPressure()
بازیابی کنید.
مقدار فشار برای صفحات لمسی یا پد لمسی مقداری بین 0 (بدون فشار) و 1 است، اما مقادیر بالاتر بسته به کالیبراسیون صفحه نمایش قابل بازگشت است.
جهت گیری
جهت نشان می دهد که قلم به کدام جهت اشاره می کند.
جهت گیری اشاره گر را می توان با استفاده از getAxisValue(AXIS_ORIENTATION)
یا getOrientation()
(برای اولین اشاره گر) بازیابی کرد.
برای یک قلم، جهت به صورت یک مقدار رادیانی بین 0 تا pi (𝛑) در جهت عقربههای ساعت یا 0 تا -pi در خلاف جهت عقربههای ساعت برگردانده میشود.
جهتیابی شما را قادر میسازد تا یک قلم موی واقعی را پیادهسازی کنید. به عنوان مثال، اگر قلم نشان دهنده یک قلم مو صاف باشد، عرض قلم مو به جهت قلم بستگی دارد.
شیب
شیب میل قلم را نسبت به صفحه اندازه گیری می کند.
Tilt زاویه مثبت قلم را بر حسب رادیان برمیگرداند، جایی که صفر عمود بر صفحه است و 𝛑/2 روی سطح صاف است.
زاویه شیب را می توان با استفاده از getAxisValue(AXIS_TILT)
بازیابی کرد (بدون میانبری برای اولین اشاره گر).
از Tilt می توان برای بازتولید تا حد امکان ابزارهای واقعی مانند شبیه سازی سایه با مداد کج شده استفاده کرد.
شناور
فاصله قلم از صفحه نمایش را می توان با getAxisValue(AXIS_DISTANCE)
بدست آورد. این روش با دور شدن قلم از صفحه، مقداری را از 0.0 (تماس با صفحه) به مقادیر بالاتر برمی گرداند. فاصله شناور بین صفحه نمایش و نوک (نقطه) قلم بستگی به سازنده صفحه و قلم دارد. از آنجا که پیادهسازیها میتوانند متفاوت باشند، برای عملکردهای حیاتی برنامه به مقادیر دقیق تکیه نکنید.
از شناور قلم میتوان برای پیشنمایش اندازه قلم مو استفاده کرد یا نشان داد که یک دکمه قرار است انتخاب شود.
توجه: Compose اصلاحکنندههایی را ارائه میکند که بر وضعیت تعاملی عناصر UI تأثیر میگذارند:
-
hoverable
: مؤلفه را به گونه ای پیکربندی کنید که با استفاده از رویدادهای ورود و خروج اشاره گر قابل شناور باشد. -
indication
: جلوه های بصری را برای این مؤلفه در هنگام وقوع فعل و انفعالات ترسیم می کند.
رد کف دست، ناوبری، و ورودی های ناخواسته
گاهی اوقات صفحه نمایش های چند لمسی می توانند لمس های ناخواسته را ثبت کنند، به عنوان مثال، زمانی که کاربر به طور طبیعی دست خود را برای پشتیبانی در حین دست نویسی روی صفحه قرار می دهد. رد کف دست مکانیزمی است که این رفتار را تشخیص می دهد و به شما اطلاع می دهد که آخرین مجموعه MotionEvent
باید لغو شود.
در نتیجه، باید تاریخچه ای از ورودی های کاربر را نگه دارید تا لمس های ناخواسته از روی صفحه حذف شوند و ورودی های قانونی کاربر دوباره ارائه شوند.
ACTION_CANCEL و FLAG_CANCELED
ACTION_CANCEL
و FLAG_CANCELED
هر دو طراحی شدهاند تا به شما اطلاع دهند که مجموعه قبلی MotionEvent
باید از آخرین ACTION_DOWN
لغو شود، بنابراین میتوانید، برای مثال، آخرین ضربه را برای یک برنامه طراحی برای یک اشارهگر معین لغو کنید.
ACTION_CANCEL
اضافه شده در Android 1.0 (سطح API 1)
ACTION_CANCEL
نشان میدهد که مجموعه قبلی رویدادهای حرکتی باید لغو شوند.
ACTION_CANCEL
زمانی فعال میشود که یکی از موارد زیر شناسایی شود:
- حرکات ناوبری
- رد کف دست
هنگامی که ACTION_CANCEL
راه اندازی می شود، باید اشاره گر فعال را با getPointerId ( getActionIndex() )
شناسایی کنید. سپس stroke ایجاد شده با آن اشاره گر را از تاریخچه ورودی حذف کنید و صحنه را دوباره رندر کنید.
FLAG_CANCELED
اضافه شده در Android 13 (سطح API 33)
FLAG_CANCELED
نشان میدهد که اشارهگر به سمت بالا یک لمس ناخواسته کاربر بوده است. این پرچم معمولاً زمانی تنظیم میشود که کاربر به طور تصادفی صفحه را لمس کند، مثلاً با گرفتن دستگاه یا قرار دادن کف دست روی صفحه.
به صورت زیر به مقدار پرچم دسترسی پیدا می کنید:
val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED
اگر پرچم تنظیم شده است، باید آخرین مجموعه MotionEvent
را از آخرین ACTION_DOWN
از این نشانگر لغو کنید.
مانند ACTION_CANCEL
، نشانگر را می توان با getPointerId(actionIndex)
پیدا کرد.
تمام صفحه، لبه به لبه، و حرکات ناوبری
اگر برنامه ای تمام صفحه است و دارای عناصر قابل اجرا در نزدیکی لبه است، مانند بوم نقاشی یا برنامه یادداشت برداری، کشیدن انگشت از پایین صفحه برای نمایش پیمایش یا انتقال برنامه به پس زمینه ممکن است منجر به ایجاد ناخواسته شود. روی بوم لمس کنید
برای جلوگیری از ایجاد لمسهای ناخواسته در برنامهتان توسط اشارهها، میتوانید از مزایای insets و ACTION_CANCEL
استفاده کنید.
همچنین بخش رد کف دست، پیمایش و ورودیهای ناخواسته را ببینید.
از متد setSystemBarsBehavior()
و BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
WindowInsetsController
برای جلوگیری از حرکات ناوبری از ایجاد رویدادهای لمسی ناخواسته استفاده کنید:
// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
برای کسب اطلاعات بیشتر در مورد مدیریت ژستهای درونی و ژستها، رجوع کنید به:
- نوارهای سیستم را برای حالت همهجانبه پنهان کنید
- از سازگاری با ناوبری اشاره ای اطمینان حاصل کنید
- محتوا را لبه به لبه در برنامه خود نمایش دهید
تاخیر کم
تأخیر زمان مورد نیاز سخت افزار، سیستم و برنامه برای پردازش و ارائه ورودی کاربر است.
تأخیر = پردازش ورودی سخت افزار و سیستم عامل + پردازش برنامه + ترکیب سیستم
- رندر سخت افزاری
منبع تأخیر
- ثبت قلم با صفحه لمسی (سخت افزار): اتصال بی سیم اولیه هنگامی که قلم و سیستم عامل برای ثبت و همگام سازی با هم ارتباط برقرار می کنند.
- نرخ نمونهبرداری لمسی (سختافزار): تعداد دفعاتی که در هر ثانیه یک صفحه لمسی بررسی میکند که آیا نشانگر سطح را لمس میکند، از 60 تا 1000 هرتز.
- پردازش ورودی (برنامه): اعمال رنگ، جلوههای گرافیکی و تبدیل در ورودی کاربر.
- رندر گرافیکی (OS + سخت افزار): تعویض بافر، پردازش سخت افزار.
گرافیک کم تاخیر
کتابخانه گرافیکی با تأخیر کم Jetpack زمان پردازش بین ورودی کاربر و رندر روی صفحه را کاهش می دهد.
این کتابخانه با اجتناب از رندر چند بافری و استفاده از تکنیک رندر جلو بافر، که به معنای نوشتن مستقیم روی صفحه است، زمان پردازش را کاهش می دهد.
رندر جلو بافر
بافر جلو حافظه ای است که صفحه نمایش برای رندر استفاده می کند. این نزدیکترین اپلیکیشنی است که میتوانند مستقیماً روی صفحه طراحی کنند. کتابخانه با تأخیر کم به برنامهها امکان میدهد مستقیماً به بافر جلو ارائه شوند. این کار با جلوگیری از تعویض بافر، که می تواند برای رندر معمولی چند بافری یا رندر دو بافری (متداول ترین مورد) اتفاق بیفتد، عملکرد را بهبود می بخشد.
در حالی که رندر جلو بافر یک تکنیک عالی برای رندر کردن یک ناحیه کوچک از صفحه است، اما برای بهروزرسانی کل صفحه طراحی نشده است. با رندر بافر جلو، برنامه در حال رندر کردن محتوا به بافری است که نمایشگر در حال خواندن از آن است. در نتیجه، امکان رندر کردن مصنوعات یا پاره شدن وجود دارد (به زیر مراجعه کنید).
کتابخانه با تأخیر کم از Android 10 (سطح API 29) و بالاتر و در دستگاههای ChromeOS دارای Android 10 (سطح API 29) و بالاتر در دسترس است.
وابستگی ها
کتابخانه با تأخیر کم مؤلفه هایی را برای اجرای رندر جلو بافر فراهم می کند. کتابخانه به عنوان یک وابستگی در فایل build.gradle
ماژول برنامه اضافه شده است:
dependencies {
implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}
تماس های GLFrontBufferRenderer
کتابخانه با تأخیر کم شامل رابط GLFrontBufferRenderer.Callback
است که روش های زیر را تعریف می کند:
کتابخانه با تأخیر کم در مورد نوع داده ای که با GLFrontBufferRenderer
استفاده می کنید نظری ندارد.
با این حال، کتابخانه داده ها را به عنوان جریانی از صدها نقطه داده پردازش می کند. و بنابراین، داده های خود را برای بهینه سازی استفاده و تخصیص حافظه طراحی کنید.
تماس های تلفنی
برای فعال کردن بازخوانی رندر، GLFrontBufferedRenderer.Callback
را پیاده سازی کنید و onDrawFrontBufferedLayer()
و onDrawDoubleBufferedLayer()
را لغو کنید. GLFrontBufferedRenderer
از فراخوان ها برای ارائه داده های شما به بهینه ترین شکل ممکن استفاده می کند.
val callback = object: GLFrontBufferedRenderer.Callback<DATA_TYPE> {
override fun onDrawFrontBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
param: DATA_TYPE
) {
// OpenGL for front buffer, short, affecting small area of the screen.
}
override fun onDrawMultiDoubleBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
params: Collection<DATA_TYPE>
) {
// OpenGL full scene rendering.
}
}
یک نمونه از GLFrontBufferedRenderer را اعلام کنید
GLFrontBufferedRenderer
را با ارائه SurfaceView
و تماسهایی که قبلا ایجاد کردهاید، آماده کنید. GLFrontBufferedRenderer
رندر را در بافر جلویی و دوبل با استفاده از callback های شما بهینه می کند:
var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)
رندرینگ
با فراخوانی متد renderFrontBufferedLayer()
، که فراخوانی onDrawFrontBufferedLayer()
را فعال میکند، رندر بافر جلو شروع میشود.
هنگامی که commit()
را فراخوانی میکنید، رندر دو بافر از سر گرفته میشود، که پاسخ تماس onDrawMultiDoubleBufferedLayer()
را راهاندازی میکند.
در مثال زیر، هنگامی که کاربر شروع به کشیدن روی صفحه ( ACTION_DOWN
) میکند و نشانگر را به اطراف ( ACTION_MOVE
) حرکت میدهد، فرآیند به بافر جلو (رندر سریع) ارائه میشود. هنگامی که نشانگر از سطح صفحه خارج میشود، فرآیند به بافر دوگانه ارائه میشود ( ACTION_UP
).
میتوانید از requestUnbufferedDispatch()
استفاده کنید تا بخواهید که سیستم ورودی رویدادهای حرکتی را دستهبندی نکند، بلکه آنها را به محض در دسترس بودن تحویل دهد:
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
// Deliver input events as soon as they arrive.
view.requestUnbufferedDispatch(motionEvent)
// Pointer is in contact with the screen.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_MOVE -> {
// Pointer is moving.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_UP -> {
// Pointer is not in contact in the screen.
glFrontBufferRenderer.commit()
}
MotionEvent.CANCEL -> {
// Cancel front buffer; remove last motion set from the screen.
glFrontBufferRenderer.cancel()
}
}
رندر کردن بایدها و نبایدها
بخش های کوچک صفحه، دست خط، طراحی، طراحی.
به روز رسانی تمام صفحه، حرکت متحرک، بزرگنمایی. می تواند منجر به پارگی شود.
پاره شدن
پاره شدن زمانی اتفاق میافتد که صفحه نمایش بهروزرسانی میشود در حالی که بافر صفحه نمایش همزمان در حال تغییر است. بخشی از صفحه نمایش داده های جدید را نشان می دهد، در حالی که بخشی دیگر داده های قدیمی را نشان می دهد.
پیش بینی حرکت
کتابخانه پیشبینی حرکت Jetpack با تخمین مسیر ضربه کاربر و ارائه نقاط مصنوعی و موقت به رندر، تأخیر درک شده را کاهش میدهد.
کتابخانه پیشبینی حرکت ورودیهای واقعی کاربر را به عنوان اشیاء MotionEvent
دریافت میکند. اشیاء حاوی اطلاعاتی در مورد مختصات x و y، فشار و زمان هستند که توسط پیش بینی کننده حرکت برای پیش بینی اشیاء MotionEvent
در آینده استفاده می شود.
اشیاء پیش بینی شده MotionEvent
فقط تخمینی هستند. رویدادهای پیشبینیشده میتوانند تأخیر درک شده را کاهش دهند، اما دادههای پیشبینیشده باید پس از دریافت با دادههای واقعی MotionEvent
جایگزین شوند.
کتابخانه پیشبینی حرکت از Android نسخه 4.4 (سطح API 19) و بالاتر و در دستگاههای ChromeOS دارای Android 9 (سطح API 28) و بالاتر در دسترس است.
وابستگی ها
کتابخانه پیش بینی حرکت، اجرای پیش بینی را فراهم می کند. کتابخانه به عنوان یک وابستگی در فایل build.gradle
ماژول برنامه اضافه شده است:
dependencies {
implementation "androidx.input:input-motionprediction:1.0.0-beta01"
}
پیاده سازی
کتابخانه پیشبینی حرکت شامل رابط MotionEventPredictor
است که روشهای زیر را تعریف میکند:
-
record()
: اشیاءMotionEvent
را به عنوان رکوردی از اقدامات کاربر ذخیره می کند -
predict()
: یکMotionEvent
پیش بینی شده را برمی گرداند
یک نمونه از MotionEventPredictor
را اعلام کنید
var motionEventPredictor = MotionEventPredictor.newInstance(view)
پیش بینی را با داده تغذیه کنید
motionEventPredictor.record(motionEvent)
پیش بینی کنید
when (motionEvent.action) {
MotionEvent.ACTION_MOVE -> {
val predictedMotionEvent = motionEventPredictor?.predict()
if(predictedMotionEvent != null) {
// use predicted MotionEvent to inject a new artificial point
}
}
}
بایدها و نبایدهای پیش بینی حرکت
هنگامی که یک نقطه پیش بینی شده جدید اضافه می شود، نقاط پیش بینی را حذف کنید.
از نقاط پیش بینی برای رندر نهایی استفاده نکنید.
برنامه های یادداشت برداری
ChromeOS به برنامه شما امکان می دهد برخی از اقدامات یادداشت برداری را اعلام کند.
برای ثبت یک برنامه به عنوان یک برنامه یادداشت برداری در ChromeOS، به سازگاری ورودی مراجعه کنید.
برای ثبت یک برنامه به عنوان یادداشت برداری در Android، به ایجاد برنامه یادداشت برداری مراجعه کنید.
Android 14 (سطح API 34)، هدف ACTION_CREATE_NOTE
را معرفی کرد، که به برنامه شما امکان میدهد فعالیت یادداشتبرداری را در صفحه قفل شروع کند.
تشخیص جوهر دیجیتال با کیت ML
با تشخیص جوهر دیجیتال کیت ML ، برنامه شما میتواند متن دستنویس روی سطح دیجیتال را به صدها زبان تشخیص دهد. شما همچنین می توانید طرح ها را طبقه بندی کنید.
ML Kit کلاس Ink.Stroke.Builder
را برای ایجاد اشیاء Ink
ارائه میکند که میتوانند توسط مدلهای یادگیری ماشین پردازش شوند تا دستنویس به متن تبدیل شود.
این مدل علاوه بر تشخیص دست خط، میتواند حرکاتی مانند حذف و دایره را تشخیص دهد.
برای اطلاعات بیشتر به تشخیص جوهر دیجیتال مراجعه کنید.