Vẽ giao diện người dùng chỉ là một phần trong quá trình tạo khung hiển thị tuỳ chỉnh. Bạn cũng cần làm cho khung hiển thị của bạn phản hồi hoạt động đầu vào của người dùng theo cách gần giống với hành động trong thế giới thực mà bạn đang bắt chước.
Làm cho các đối tượng trong ứng dụng hoạt động giống như đối tượng thực. Ví dụ: đừng để hình ảnh trong ứng dụng của bạn hiện ra và xuất hiện lại ở nơi khác, do đối tượng trong thế giới thực, đừng làm điều đó. Thay vào đó, hãy di chuyển hình ảnh từ một nơi sang khác.
Người dùng cảm nhận được hành vi hoặc cảm giác tinh tế trong một giao diện và phản ứng tốt nhất với những chi tiết tinh tế bắt chước thế giới thực. Ví dụ: khi người dùng hất một đối tượng giao diện người dùng, tạo cho chúng cảm giác quán tính tại thời điểm ban đầu làm trì hoãn chuyển động. Ở cuối của chuyển động, cho chúng cảm giác về động lượng đưa vật thể vượt ra ngoài hất.
Trang này minh hoạ cách sử dụng các tính năng của khung Android để thêm các hành vi trong thế giới thực vào khung hiển thị tuỳ chỉnh.
Bạn có thể tìm thêm thông tin liên quan trong Tổng quan về sự kiện đầu vào và Ảnh động thuộc tính tổng quan.
Xử lý cử chỉ nhập
Giống như nhiều khung giao diện người dùng khác, Android hỗ trợ mô hình sự kiện đầu vào. Người dùng
sẽ chuyển thành các sự kiện kích hoạt lệnh gọi lại, đồng thời bạn có thể ghi đè
lệnh gọi lại để tuỳ chỉnh cách ứng dụng phản hồi người dùng. Thông tin đầu vào phổ biến nhất
sự kiện trong hệ thống Android là Chạm, sự kiện này sẽ kích hoạt
onTouchEvent(android.view.MotionEvent)
.
Ghi đè phương thức này để xử lý sự kiện như sau:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Bản thân các sự kiện chạm không đặc biệt hữu ích. Giao diện người dùng cảm ứng hiện đại
xác định các tương tác bằng cử chỉ như nhấn, kéo, đẩy,
hất và thu phóng. Để chuyển đổi các sự kiện chạm thô thành cử chỉ, Android
cung cấp
GestureDetector
.
Tạo GestureDetector
bằng cách truyền vào một thực thể của lớp
triển khai
GestureDetector.OnGestureListener
Nếu chỉ muốn xử lý một vài cử chỉ, bạn có thể mở rộng
GestureDetector.SimpleOnGestureListener
thay vì triển khai GestureDetector.OnGestureListener
. Ví dụ: mã này tạo một lớp mở rộng
GestureDetector.SimpleOnGestureListener
và ghi đè
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());
Dù bạn có sử dụng GestureDetector.SimpleOnGestureListener
hay không,
luôn triển khai một
onDown()
trả về true
. Điều này là cần thiết vì tất cả cử chỉ
bắt đầu bằng thông báo onDown()
. Nếu bạn trả về false
từ onDown()
, dưới dạng
GestureDetector.SimpleOnGestureListener
thì có, hệ thống giả định
bạn muốn bỏ qua phần còn lại của cử chỉ và các phương pháp khác
GestureDetector.OnGestureListener
không được gọi. Chỉ trả lại hàng
false
từ onDown()
nếu bạn muốn bỏ qua toàn bộ
cử chỉ.
Sau khi bạn triển khai GestureDetector.OnGestureListener
và tạo
một thực thể của GestureDetector
, bạn có thể sử dụng
GestureDetector
để diễn giải các sự kiện chạm mà bạn nhận được
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; }
Khi bạn truyền onTouchEvent()
một sự kiện chạm mà nó không
nhận dạng dưới dạng một phần của một cử chỉ, nó sẽ trả về false
. Sau đó, bạn có thể chạy
mã phát hiện cử chỉ tuỳ chỉnh của riêng bạn.
Tạo chuyển động vật lý hợp lý
Cử chỉ là một cách hiệu quả để điều khiển thiết bị màn hình cảm ứng, nhưng chúng có thể phản trực giác và khó nhớ trừ phi chúng tạo ra kết quả chính đáng.
Ví dụ: giả sử bạn muốn triển khai cử chỉ hất theo chiều ngang đặt mục được vẽ trong chế độ xem quay quanh trục tung của nó. Cử chỉ này sẽ hợp lý nếu giao diện người dùng phản hồi bằng cách di chuyển nhanh theo hướng hất, sau đó giảm tốc độ, như thể người dùng đẩy bánh đà và làm cho nó quay tròn.
Tài liệu hướng dẫn cách
tạo hiệu ứng chuyển động cho một cuộn
cử chỉ giải thích chi tiết về cách triển khai scoll của riêng bạn
hành vi. Tuy nhiên, việc mô phỏng cảm giác của bánh đà không phải là một việc đơn giản. Rất nhiều vật lý
và toán học là cần thiết để làm cho mô hình bánh đà hoạt động chính xác. Rất may là
Android cung cấp các lớp trợ giúp để mô phỏng hành vi này và các hành vi khác. Chiến lược phát hành đĩa đơn
Scroller
là cơ sở để xử lý các cử chỉ hất kiểu bánh đà.
Để bắt đầu hất, hãy gọi
fling()
với vận tốc ban đầu cùng với x và y tối thiểu và cực đại
giá trị của cử chỉ hất. Đối với giá trị vận tốc, bạn có thể sử dụng giá trị được tính bằng
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; }
Lệnh gọi đến fling()
thiết lập mô hình vật lý cho cử chỉ hất
cử chỉ. Sau đó, hãy cập nhật Scroller
bằng cách gọi
Scroller.computeScrollOffset()
theo định kỳ. computeScrollOffset()
cập nhật
Trạng thái bên trong của đối tượng Scroller
bằng cách đọc thời gian hiện tại và
bằng cách sử dụng mô hình vật lý để tính vị trí x và y tại đó
bất cứ lúc nào. Gọi điện
getCurrX()
và
getCurrY()
để truy xuất các giá trị này.
Hầu hết thành phần hiển thị đều truyền x và y của đối tượng Scroller
trực tiếp đến
scrollTo()
.
Ví dụ này hơi khác một chút: nó sử dụng vị trí cuộn x hiện tại
để đặt góc quay của khung hiển thị.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
Lớp Scroller
tính toán vị trí cuộn cho bạn, nhưng
không tự động áp dụng các vị trí đó vào chế độ xem của bạn. Áp dụng toạ độ mới
đủ thường xuyên để làm cho ảnh động cuộn trông mượt mà. Có 2 cách
thực hiện việc này:
- Buộc vẽ lại bằng cách gọi
postInvalidate()
sau khi gọifling()
. Kỹ thuật này yêu cầu bạn tính toán độ lệch cuộn trongonDraw()
và gọipostInvalidate()
mỗi khi độ lệch cuộn thay đổi. - Thiết lập một
ValueAnimator
để tạo hiệu ứng động trong thời gian hất và thêm trình nghe để xử lý cập nhật ảnh động bằng cách gọiaddUpdateListener()
Kỹ thuật này cho phép bạn tạo ảnh động cho các thuộc tính củaView
.
Giúp chuyển cảnh mượt mà
Người dùng mong muốn có một giao diện người dùng hiện đại để chuyển đổi suôn sẻ giữa các trạng thái: các thành phần trên giao diện người dùng hiện dần trong và biến mất, thay vì xuất hiện rồi biến mất, và chuyển động bắt đầu và kết thúc suôn sẻ thay vì bắt đầu rồi dừng đột ngột. Hệ điều hành Android ảnh động thuộc tính khung giúp chuyển đổi mượt mà dễ dàng hơn.
Để sử dụng hệ thống ảnh động, bất cứ khi nào một thuộc tính thay đổi, điều gì ảnh hưởng đến
giao diện của chế độ xem, đừng trực tiếp thay đổi thuộc tính. Thay vào đó, hãy sử dụng
ValueAnimator
để thực hiện thay đổi. Trong ví dụ sau đây:
Việc sửa đổi thành phần con đã chọn trong chế độ xem sẽ khiến toàn bộ thành phần được kết xuất
xoay khung nhìn để con trỏ lựa chọn nằm chính giữa.
ValueAnimator
thay đổi chế độ xoay trong khoảng thời gian vài trăm
mili giây thay vì đặt ngay giá trị xoay vòng mới.
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();
Nếu giá trị bạn muốn thay đổi là một trong các giá trị cơ sở View
các thuộc tính, việc tạo ảnh động thậm chí còn dễ dàng hơn vì các chế độ xem đã được tích hợp sẵn
ViewPropertyAnimator
được tối ưu hoá cho ảnh động đồng thời của nhiều thuộc tính, như trong
ví dụ sau:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();