השרטוט של ממשק המשתמש הוא רק חלק אחד מיצירת תצוגה מותאמת אישית. צריך גם לגרום לתצוגה שלכם להגיב לקלט של משתמשים באופן שדומה מאוד הפעולה בעולם האמיתי שאתם מחקים.
לגרום לאובייקטים באפליקציה לפעול כמו אובייקטים אמיתיים. לדוגמה, אל תיתנו התמונות באפליקציה בולטות מחוץ לקיום ומופיעות שוב במקומות אחרים, כי אובייקטים בעולם האמיתי, לא עושים זאת. במקום זאת, כדאי להעביר את התמונות ממקום אחד אל אחר.
המשתמשים מרגישים אפילו התנהגות עדינה או מרגישים בממשק, ומגיבים בצורה הטובה ביותר דקויות שמחקות את העולם האמיתי. לדוגמה, כשמשתמשים מעבירים את האובייקט בממשק המשתמש, לתת להם תחושה של אינרציה בהתחלה שמעכבת את התנועה. בסוף של התנועה, לתת להם תחושה של מומנטום שנושא את האובייקט אל מעבר להחליק.
בדף הזה אנחנו מסבירים איך להשתמש בתכונות של Android Framework כדי להוסיף בהתנהגויות האלה בעולם האמיתי לתצוגה המותאמת אישית.
מידע קשור נוסף זמין בכתובת סקירה כללית של אירועי קלט וגם אנימציה של נכס סקירה כללית.
טיפול בתנועות קלט
בדומה להרבה מסגרות אחרות של ממשק משתמש, Android תומך במודל של אירועי קלט. משתמשים
הפעולות הופכות לאירועים שמפעילים קריאות חוזרות, ואפשר לשנות את
קריאה חוזרת (callback) כדי להתאים אישית את האופן שבו האפליקציה מגיבה למשתמש. הקלט הנפוץ ביותר
במערכת Android האירוע touch מפעיל,
onTouchEvent(android.view.MotionEvent)
.
כדי לטפל באירוע, צריך לשנות את השיטה הזו באופן הבא:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
אירועי מגע כשלעצמם לא שימושיים במיוחד. ממשקי מגע מודרניים
להגדיר אינטראקציות במונחים של תנועות, כמו הקשה, משיכה, דחיפה
ריצוף ושינוי מרחק התצוגה. כדי להמיר אירועי מגע גולמיים לתנועות, מערכת Android
מספקת
GestureDetector
בונים GestureDetector
על ידי העברת מופע במחלקה
שמיישם
GestureDetector.OnGestureListener
.
אם רוצים לעבד רק מספר קטן של תנועות, אפשר להאריך
GestureDetector.SimpleOnGestureListener
במקום ליישם את המדיניות GestureDetector.OnGestureListener
גרפי. לדוגמה, הקוד הזה יוצר מחלקה שמתרחבת
GestureDetector.SimpleOnGestureListener
ושינויים מברירת המחדל
onDown(MotionEvent)
.
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
Java
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
גם אם לא משתמשים ב-GestureDetector.SimpleOnGestureListener
,
ליישם תמיד
onDown()
שמחזירה את הערך true
. פעולה זו נחוצה מכיוון שכל התנועות
להתחיל בהודעת onDown()
. אם תוחזר בתאריך false
החל מ-onDown()
, כמו
GestureDetector.SimpleOnGestureListener
, המערכת מניחה
שרוצים להתעלם משאר התנועה, ומהשיטות האחרות
לא ניתן להתקשר אל GestureDetector.OnGestureListener
. החזרה בלבד
false
מ-onDown()
אם ברצונך להתעלם מקטע שלם
תנועה.
אחרי ההטמעה של GestureDetector.OnGestureListener
ויצירת
מופע של GestureDetector
, אפשר להשתמש
GestureDetector
כדי לפרש את אירועי המגע שמתקבלים
onTouchEvent()
.
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
Java
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
כשמעבירים ל-onTouchEvent()
אירוע מגע שלא עובר
מזהה כחלק מתנועה, הוא מחזיר false
. לאחר מכן תוכלו להריץ
קוד משלכם לזיהוי תנועה.
יצירת תנועה שמתאימה פיזית
תנועות מספקות שליטה טובה במכשירים עם מסך מגע, אבל בלתי הגיוניים וקשה לזכור אותם, אלא אם הם מייצרים הכנסות בתוצאות הגיוניות.
לדוגמה, נניח שאתם רוצים להטמיע תנועת הנפה אופקית מגדיר את הפריט שצייר בתצוגה מסתובבת על הציר האנכי שלו. התנועה הזו הגיוני אם ממשק המשתמש מגיב באמצעות תנועה מהירה בכיוון של הניטה. ואז הוא מאט, כאילו המשתמש דוחף גלגל תנופה וגורם לו להסתובב.
המסמכים הדרושים
הנפשה של גלילה
תנועה מספקת הסבר מפורט על האופן שבו ניתן להטמיע גולגולת משלכם
או התנהגות המשתמשים. אבל הדמיה של התחושה של גלגל תנופה אינה טריוויאלית. הרבה פיזיקה
ומתמטיקה נדרשת כדי שמודל של גלגל תנופה יפעל כראוי. למזלנו,
ב-Android יש כיתות מסייעים כדי לדמות התנהגויות כאלה והתנהגויות אחרות.
Scroller
class הוא הבסיס לטיפול בתנועות טיסה בסגנון גלגל תנופה.
כדי להתחיל בקלי קלות, אפשר להתקשר
fling()
עם מהירות ההתחלה ועם x ו-y מינימליים
את הערכים של השלכה. את ערך המהירות אפשר להשתמש בערך שמחושב על ידי
GestureDetector
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
Java
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
הקריאה ל-fling()
מגדירה את מודל הפיזיקה בשביל הנעצים
תנועה. לאחר מכן, אפשר לעדכן את Scroller
באמצעות התקשרות
Scroller.computeScrollOffset()
במרווחי זמן קבועים. computeScrollOffset()
מעדכנת את
המצב הפנימי של האובייקט Scroller
באמצעות קריאת השעה הנוכחית ו
באמצעות מודל הפיזיקה כדי לחשב את המיקום x ו-y
בזמן האימון. שיחת טלפון
getCurrX()
וגם
getCurrY()
כדי לאחזר את הערכים האלה.
רוב התצוגות מעבירות את מאפייני x ו-y של האובייקט Scroller
ממקם ישירות אל
scrollTo()
הדוגמה הזו שונה במקצת: היא משתמשת במיקום ה-x הנוכחי של הגלילה
כדי להגדיר את הזווית הסיבובית של התצוגה.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
הכיתה Scroller
מחשבת מיקומי גלילה בשבילכם, אבל
לא מחיל את המיקומים האלה באופן אוטומטי על התצוגה שלכם. החלת קואורדינטות חדשות
לעיתים קרובות מספיק כדי שהאנימציה תיראה חלקה. יש שתי דרכים
עושים זאת:
- אילוץ שרטוט מחדש באמצעות קריאה
postInvalidate()
אחרי הקריאה אלfling()
. בשיטה הזו קיזוזי גלילה במחשוב ב:onDraw()
וקוראים לפונקציהpostInvalidate()
בכל פעם שהיסט הגלילה שינויים. - הגדרת
ValueAnimator
כדי ליצור אנימציה במהלך ההחלקה ולהוסיף מאזינים לעיבוד עדכוני אנימציה באמצעות התקשרותaddUpdateListener()
. הטכניקה הזו מאפשרת להנפיש מאפיינים שלView
מעבר חלק
המשתמשים מצפים שממשק משתמש מודרני יעבור בצורה חלקה בין מצבים: רכיבים בממשק המשתמש להיעלם ולהיעלם, במקום להופיע ולהיעלם, ותנועות מתחילות ולהסתיים בצורה חלקה במקום להתחיל ולהפסיק בפתאומיות. מכשיר Android אנימציה של נכס framework מאפשרת מעברים חלקים יותר בקלות.
כדי להשתמש במערכת האנימציה, בכל פעם שנכס משנה את מה שמשפיע
המראה של התצוגה המפורטת, אל תשנו את הנכס ישירות. במקום זאת, השתמשו
ValueAnimator
כדי לבצע את השינוי. בדוגמה הבאה,
שינוי של רכיב הצאצא שנבחר בתצוגה יגרום לרינדור כולו
התצוגה תסובב כך שמצביע הבחירה ימוקם במרכז.
ValueAnimator
משנה את הסיבוב על פני תקופה של כמה מאות
במקום להגדיר באופן מיידי את ערך הסבב החדש.
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
Java
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
אם הערך שרוצים לשנות הוא אחד מהערכים הבסיסיים View
של האנימציה, ביצוע האנימציה אפילו קל יותר, מפני שהתצוגות
ViewPropertyAnimator
שמותאם לאנימציה סימולטנית של נכסים מרובים, כמו
בדוגמה הבאה:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();