Di động cho mọi người: Lập trình xử lý cử chỉ trong Android

Cho phép cử chỉ lướt tay trong ứng dụng Android của bạn

Những người dùng di động thường dễ bị phân tâm, hay bận rộn và có những hạn chế, do đó nên xây dựng giao diện cho ứng dụng di động của bạn một cách hợp lý. Andrew Glover thảo luận về các vấn đề quan trọng phân biệt ứng dụng di động với ứng dụng web, sau đó hướng dẫn bạn thông qua việc tạo một giao diện người dùng cho ứng dụng di động sử dụng cử chỉ lướt tay để chuyển hướng thay vì dùng các nút bấm.

Andrew Glover, Giám đốc công nghệ, App47

Andrew GloverAndrew Glover là nhà phát triển, tác giả, diễn giả và nhà doanh nghiệp có niềm đam mê về phát triển theo hành vi, Tích hợp liên tục và phát triển phần mềm Agile. Ông là người sáng lập khung công tác BDD (Behavior-Driven Development - Phát triển theo hành vi) easyb và là đồng tác giả của ba cuốn sách: Continuous Integration (Tích hợp liên tục), Groovy in Action (Thực hành Groovy) và Java Testing Patterns (Các mẫu thử nghiệm Java). Bạn có thể xem blog và theo dõi ông trên Twitter.



30 08 2013

Giới thiệu

About this series

Phát triển ứng dụng di động đang bùng nổ, và đó là thị trường cho các nhà phát triển ứng dụng di động. Loạt bài này trên developerWorks giới thiệu tổng quan về di động cho các nhà lập trình có kinh nghiệm nhưng vẫn còn mới với lĩnh vực di động này.

Bắt đầu viết các ứng dụng bằng mã Java™, sau đó mở rộng bộ công cụ của bạn để thêm vào các ngôn ngữ JVM, các Framework ngôn ngữ kịch bản, HTML5/CSS/JavaScript, các công cụ của bên thứ ba, và hơn thế nữa. Từng bước một, bạn sẽ rành về các kỹ năng cần thiết để phát triển bất kỳ ứng dụng di động nào.

Khi bạn xây dựng một ứng dụng di động, cũng như xây dựng một ứng dụng web, hãy ghi nhớ điều này (kể cả với người dùng). Luôn luôn hiểu ứng dụng của bạn mục đích là gì và làm thế nào để mọi người hưởng lợi từ nó. Những loại thông tin nào sẽ hiển thị trên ứng dụng của bạn, những chức năng nào sẽ có, và làm cách nào để người dùng có thể truy cập vào cả hai? Cung cấp các hướng dẫn đầy đủ để người dùng trải nghiệm ứng dụng di động của bạn giúp đảm bảo tính thành công của nó.

Không giống như phát triển giao diện truyền thống cho máy tính để bàn hay các ứng dụng web, các quy tắc của ứng dụng di động ít hơn nhiều. Khi bạn thiết kế một giao diện ứng dụng mobile, hãy suy nghĩ thật đơn giản. Hầu hết các thiết bị di động đều nhỏ gọn (trừ khi bạn đang dùng Samsung Note 4 — điện thoại di động lớn nhất hay máy tính bảng mà tôi từng thấy). Kích thước nhỏ có mặt khắp mọi nơi như một phần thiết yếu và là lý do tại sao mọi người mang chúng khắp nơi và sử dụng bất cứ lúc nào. Điều này dẫn đến một phát hiện quan trọng về ứng dụng di động, đó là hầu hết người dùng không chú trọng vào ứng dụng của bạn khi sử dụng nó!

Một vài ứng dụng di động được xây dựng dành riêng cho máy tính bảng và chỉ sử dụng cho lĩnh vực kinh doanh (như bác sĩ truy cập vào hồ sơ bệnh nhân). Hầu hết ứng dụng di động đều truy cập trên các thiết bị nhỏ bởi người dùng và cũng để làm nhiều việc khác nữa. Như khi tôi đang xếp hàng để mua một vài thứ trong cửa hàng tạp hóa, tôi có thể chơi một vài ván Angry Birds. Nếu tôi xuống máy bay sau một chuyến bay dài, tôi có thể sử dụng thời gian đó để kiểm tra email. Nhưng nếu nó khiến tôi mất nhiều thời gian thì tôi thích bước tới trước hơn.

Điều khác biệt giữa ứng dụng di động và các ứng dụng trên web, trên máy tính cá nhân chính là số lượng: Đối với ứng dụng web, dễ dàng có trên 100 thiết bị di động. Cung cấp nhiều hơn các dịch vụ xứng đáng với ứng dụng của bạn. Tạo sự dễ dàng và hấp dẫn khi sử dụng. Nếu bạn mong đợi người dùng đọc hướng dẫn sử dụng (RTM - read the manual), thì điều đó có thể là vấn đề đối họ, vấn đề đó trở thành vấn đề của chính bạn. Việc người dùng của bạn là bác sĩ truy cập vào hồ sơ bệnh án hay chỉ là người chơi Cut-the-Rope trong khi chờ đợi đều không quan trọng. Nếu ứng dụng di động của bạn làm mất nhiều thời gian để cài đặt, thì người dùng có thể tìm kiếm một ứng dụng khác trong kho ứng dụng.


(Hello) Overheard Word

Nếu bạn đã đọc bài đầu tiên trong loạt bài này, bạn đã biết được cách cài đặt môi trường phát triển Android trên Eclipse, và cấu hình Android SDK cho phiên bản Android 4.2. Bạn cũng đã viết ứng dụng Android cơ bản đầu tiên, Hello World. Lần này, cũng theo cách đó, bạn sẽ thiết kế một ứng dụng độc đáo hơn.

Ứng dụng mẫu của tôi, Overheard Word, được thiết kế nhằm tạo sự vui vẻ và dễ dàng để học từ vựng mới và thực hành chúng trong ngữ cảnh văn bản — thật tình cờ vì đây cũng là sở thích của tôi. Trong ứng dụng này, người dùng sẽ học một vài từ và sau đó làm bài kiểm tra. Giao diện bao gồm màn hình hiển thị và hai nút bấm. Màn hình dùng để hiển thị từ và nghĩa của chúng, trong khi đó các nút bấm để cho người dùng điều hướng ứng dụng.

Overheard Word là ứng dụng di động đơn giản và thú vị cho những người yêu thích học từ vựng (như tôi ngụ ý từ aficionados (người hâm mộ) và từ connoisseurs (người sành sỏi)). Quan trọng hơn, ví dụ này như một ứng dụng Android hợp lệ và bạn có thể triển khai nó trên thiết bị thật chạy Android.

Mục tiêu của ứng dụng

Trước khi bạn thiết kế ứng dụng, tôi muốn khảo sát mục tiêu của thị trường. Ứng dụng mà tôi đã xây dựng ở Phần 1 là dành cho Android 4.2, hay thư viện API phiên bản 17. Hãy nhìn vào bản báo cáo của Google phân phối Android hiện tại (Hình 1):

Hình 1. Thị phần các phiên bản Android
Ảnh chụp danh sách xếp hạng các bản phân phối của Android

Máy tính bảng so với điện thoại

Trong Hình 1, máy tính bảng được bán nhiều nhất nhờ sự gia tăng của người dùng Android phiên bản 15. Hầu hết máy tính bảng hiện nay chạy Android 4.x trở lên. Nếu bạn không viết ứng dụng dành riêng cho máy tính bảng, vậy hãy tập trung vào phiên bản Android 9 và 10, bởi vì nó được hiện diện trên hầu hết các thiết bị di động.

Hình 1 cho thấy rằng hiện nay phiên bản Android 2.3.x (API 9 và 10) chiếm khoảng một nửa thị phần thiết bị Android! Chẳng phải nếu một nửa số thiết bị đang chạy Android 2.3.x thì chúng ta nên tập trung phát triển cho thị phần đó hay sao?


Khởi động một dự án mới

Trong bài trước, tôi đã cài đặt thư viện API 4.2. Bây giờ tôi muốn cài bản Android 2.3.3 (API 10). Khi kiểm tra Android SDK Manager, như Hình 2, tôi cũng thấy có một vài bản cập nhật:

Hình 2. Một Android SDK cài đặt với chỉ duy nhất một phiên bản đã cài đặt
Hình ảnh Android SDK Manager chỉ ra rằng đã cài đặt phiên bản 4.2

Nếu bạn muốn làm theo tôi, hãy nhấp vào tùy chọn cài đặt Android 2.3.3 (API 10) và làm theo những chỉ thị của trình quản lý.

Hình 3. Cài đặt Android 2.3.3 (API 10)
Hình ảnh Android SDK Manager đang cài Android 2.3.3

Sau khi tải về một API mới cho Android, bạn có thể tạo một trình giả lập tương thích hay thiết bị máy ảo Android (AVD - Android Virtual Device). Từ trình quản lý Android SDK, chọn Tools, sau đó đến tùy chọn Manage AVDs. Hãy chắc chằn rằng bạn tạo một AVD với Target là API Level 10.

Hình 4. Tạo một AVD
Hình ảnh tạo một trình giả lập hay AVD trong SDK Manager

Tiếp đến, tạo một dự án New Android Application và đặt tên cho nó. Tôi đặt tên là ứng dụng Overheard Word. Thiết lập target cho ứng dụng là API 10 để nó được hỗ trợ trên nhiều thiết bị nhất. Hãy xem xét kỹ Hình 5 và lưu ý rằng tôi cũng chọn thư viện biên dịch (compile) là API 10. Bạn có thể chọn biên dịch với phiên bản Android mới hơn nhưng tôi thích lập trình và biên dịch cùng một API. (Nếu tôi biên dịch với một API mới hơn, tôi phải cẩn thận để không sử dụng bất kỳ tính năng nào của nó trong ứng dụng của tôi, vì nó có thể tạo ra kết quả không tương thích.)

Hình 5. Tạo một dự án Android mới trong Eclipse
Hình ảnh tạo một dự án Android trong Eclipse

Khi bạn nhấn Next, hãy chắc chắn rằng bạn không tạo một dự án Library. Hãy giữ các thiết lập mặc định như bạn đã thiết lập cho ứng dụng Hello World. Sau này, bạn cũng có thể tiến hành những thay đổi nhỏ (như thay đổi biểu tượng cho ứng dụng).

Bước cài đặt cuối cùng là đặt tên cho Activity đầu tiên. Tôi thích dùng OverheardWord hơn thay vì Main, và tương tự, đặt tên cho layout. Nếu bạn cũng sử dụng Target là API 10, bạn sẽ có một vài lựa chọn Navigation Type trong hộp thoại cuối. Hiện tại đừng lo lắng về điều đó; tôi sẽ hướng dẫn bạn cách tạo một menu điều hướng trong bài sau. Còn đối với ứng dụng này, hãy để nguyên Navigation Typenone.

Hình 6. Tạo một Activity mặc định
Hình ảnh tạo các hoạt động mặc định trên Eclipse

Nhấn Finish để tạo dự án như dự án HelloWorld mặc định mà bạn đã làm trước đây.


Xây dựng giao diện người dùng Android

Bây giờ bạn đã sẵn sàng để xây dựng một giao diện người dùng đơn giản. Hãy nhớ lại, một giao diện người dùng Android hoàn toàn được định nghĩa dưới dạng XML mà bạn có thể đã làm quen. Nếu bạn phát triển giao diện người dùng Java™ theo cách truyền thống, bạn có thể thấy Android cũng sử dụng layouts. Với những layout (bố cục) của Android, bạn có thể định nghĩa (hay gợi ý) để thiết bị biết cách hiển thị các thành phần trong ứng dụng của bạn. Các kiểu layout bao gồm:

  • Linear, các thành phần được tổ chức theo kiểu ngang hoặc dọc (giống như các cột của tờ báo)
  • Relative, các thành phần cách nhau một khoảng tương đối (ví dụ một widget 2 ở bên phải widget 1)
  • Table, các thành phần được hiển thị theo cột và dòng

Còn nhiều kiểu layout nữa, nhưng bạn sẽ bắt đầu làm quen trước với ba kiểu này!

Trong một layout, bạn có thể định nghĩa widgets, là những thành phần thông thường mà bạn có thể tìm thấy ở bất cứ giao diện người dùng nào. Nền tảng Android cung cấp những widget cơ bản như những buttons, text-boxes, và drop-down lists, và nhiều widget phức tạp như image viewers và spin-wheels.

Tôi đã xác định được những yêu cầu giao diện của Overheard Word:

  • 3 trường văn bản (để lưu một từ, một phần lưu phát âm, và định nghĩa)
  • 2 nút bấm (1 nút để chọn từ và 1 nút để làm bài kiểm tra)

Định nghĩa layout

Trước tiên ta đặt giao diện người dùng Android lại với nhau để định nghĩa layout, cùng với các widgets mà bạn muốn kèm theo. Trong Liệt kê 1, đầu tiên tôi định nghĩa một LinearLayout với 3 TextViews. Tiếp theo, tôi tạo một sub-layout (một LinearLayout khác) có 2 Buttons.

Liệt kê 1. Định nghĩa một LinearLayout trong Android
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".OverheardWord" >

    <TextView
        android:id="@+id/word_study_word"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="60dp"
        android:textColor="@color/black"
        android:textSize="30sp" />

    <TextView
        android:id="@+id/word_study_part_of_speech"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:textColor="@color/black"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/word_study_definition"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="40dp"
        android:layout_marginRight="40dp"
        android:textColor="@color/black"
        android:textSize="18sp" />

    <LinearLayout
        android:id="@+id/widget62"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp" >

        <Button
            android:id="@+id/next_word"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/next_word" />

        <Button
            android:id="@+id/take_quiz"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="50dp"
            android:layout_marginTop="20dp"
            android:text="@string/take_quiz" />
    </LinearLayout>

</LinearLayout>

Như bạn thấy XML trong Liệt kê 1, mỗi widget có một số thuộc tính ảnh hưởng trực tiếp lên nó. Ví dụ, một button (nút bấm) trong 2 button có một phần tử text mà chuỗi của nó trỏ tới resource element (phần tử tài nguyên). Resource element được định nghĩa trong một tệp resource có tên là strings.xml, được chứa trong thư mục res/values. Liệt kê 2 hiển thị tệp strings.xml.

Liệt kê 2. tệp resource định nghĩa màu và nhãn của button
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Overheard Word</string>

    <color name="white">#ffffff</color>
    <color name="black">#000000</color>

    <string name="next_word">Next word</string>
    <string name="take_quiz">Take quiz</string>

</resources>

Nếu bạn muốn biết giao diện ứng dụng của bạn trông như thế nào, bạn có thể bắt đầu với trình mô phỏng. Tôi nhấn vào thẻ Graphical Layout trong Eclipse, như Hình 7.

Hình 7. Ứng dụng Overheard Word chạy trên Eclipse
Ảnh chụp ứng dụng Overheard Word chạy trong Eclipse

Placeholder text

Khi xem giao diện người dùng, tôi hay nhằm lẫn khoảng trắng đối với thuộc tính word. Nhưng không sao: tôi sẽ thêm một vài đoạn văn bản mẫu cho nhiều ý tưởng thực tế rằng đoạn văn bản sẽ hiển thị như thế nào trước khi tôi viết mã lệnh..

Đầu tiên, tôi thêm một thuộc tính android:text cho mỗi widget TextView, giống như tôi đã làm với các nhãn của button. Bây giờ tôi thêm các placeholder text như sau:

Liệt kê 3. Tạo các placeholders với android:text
<TextView
        android:id="@+id/word_study_part_of_speech"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:textColor="@color/black"
        android:textSize="18sp" 
        android:text="Part of Speech"/>

Như bạn có thể thấy trong Liệt kê 3, tôi đã thêm một đoạn văn bản mẫu ("Part of Speech") vào TextViewword_study_part_of_speech. Tôi đã cập nhật một số kiểu phần tử bằng cách tăng khích thước của văn bản, định nghĩa màu văn bản và canh giữa widget trong layout. Các thuộc tính trong Liệt kê 3 là phổ biến để định nghĩa các widget trong một ứng dụng Android; khi bạn xây dựng nhiều ứng dụng, bạn sẽ dần yêu thích chúng. Tôi cũng định nghĩa trực tiếp đoạn văn bản mẫu trong tệp layout hơn là trong tệp tài nguyên (resource) bởi vì giá trị của văn bản chỉ là tạm thời trong suốt quá trình thiết kế.

Khi chạy ứng dụng từ Eclipse, tôi hình dung xem người dùng sẽ thấy gì khi ứng dụng bắt đầu:

Hình 8. Overheard Word chạy trong AVD
Ảnh chụp ứng dụng Overheard Word chạy trong AVD

Có một số điều chưa đúng về thiết kế giao diện người dùng. Bạn có thể đoán đó là gì không? Vâng, đó chính là các nút bấm! Liệu chúng có thật sự cần thiết không? Người dùng có thường nhấn vào các nút bấm trên ứng dụng di động không? Có lẽ người dùng có thể quét tay sang trái hay phải để xem từ mới và quét xuống để làm bài kiểm tra.

Việc bỏ đi những nút bấm giúp giao diện trông sạch sẽ và ứng dụng Overhead Word đáp ứng được mong đợi của người dùng hơn. Tôi có thể dễ dàng thực hiện các hành vi cử chỉ, bạn sẽ thấy điều đó.


Viết mã xử lý cử chỉ trong Android

Tôi đã định nghĩa màn hình khởi tạo cho ứng dụng Overheard Word và sẵn sàng thêm một vài hành vi. Với kinh nghiệm làm việc với giao diện người dùng Java truyền thống, một lần nữa tôi có thể ứng dụng nó trực tiếp lên Android, trong đó, đầu tiên tôi thực hiện các bộ lắng nghe phản hồi của sự kiện. Nếu giữ lại 2 nút bấm, tôi có thể sử dụng bộ lắng nghe OnClickListener để đính kèm hành vi vào mỗi hành động nhấn nút (button), sau đó gán phương thức setOnClickListener cho mỗi button widget.

Các lớp ẩn danh

Các lớp ẩn danh là những lớp cục bộ nhưng không có tên: bạn có thể vừa định nghĩa vừa khởi tạo các lớp ẩn danh trong một biểu thức duy nhất. Tôi thấy rằng các lớp ẩn danh rất tiện dụng, do đó tôi dùng nó rất nhiều, đặc biệt là khi lập trình hành vi giao diện người dùng.

Trong khi tôi chắc rằng có thể sử dụng OnClickListener, tôi có thể kết thúc với nhiều tệp lớp cho nhiều widget. Tôi không thích những mớ hỗn độn đó, do đó tôi thực hiện với các lớp ẩn danh.

Mặc dù cử chỉ lướt tay đó không phải là một khái niệm trong lập trình giao diện truyền thống, nó cũng thực hiện tương tự như khi viết mã xử lý việc nhấn button. Tôi bắt đầu với một touch listener (trình lắng nghe động tác chạm), là một phần của nền tảng Android, và sau đó cung cấp một hành vi tương ứng. Để phát hiện một chuyển động khi lướt tay, tôi cũng tính toán sự khác biệt giữa các cử chỉ bắt đầukết thúc, thêm vào đó là tốc độ của hành động lướt tay. (Tôi thấy rằng tọa độ Catesian giúp ích được trong việc này: hành động lướt tay sang trái và phải sẽ dựa trên trục X, còn hướng trên và dưới sẽ dựa trên trục Y.)

Một cử chỉ có thể xảy ra ở bất cứ đâu trên giao diện. Khi bạn kết hợp loại lắng nghe này vào bất kỳ thành phần nào của một giao diện ứng dụng, hãy bắt đầu với layout (trong Android, layout đại diện như là một View) và bao gồm cả widget button đơn giản nhất. Tôi sử dụng bộ lắng nghe SimpleOnGestureListener của Android để khởi tạo thực thi cử chỉ, sau đó gắn nó lên View thông qua phương thức onTouchListener. Bằng cách đính kèm một thể hiện của view, tôi có thể nhận dạng được hành động lướt tay ngang qua toàn bộ giao diện chứ không chỉ trong một thành phần, như một trình xem ảnh nhỏ gọn.

Trước khi làm được điều đó, hãy nghĩ xem, tôi phải viết một lớp làm nhiệm vụ phát hiện ra các chuyển động của ngón tay trên thiết bị di động.

SwipeDetector

Ta rất dễ nhận dạng hành động nhấn vào button. Nền tảng của hệ thống hoạt động dựa trên các chi tiết tọa độ của viền button, vì thế bạn có thể bỏ qua việc định nghĩa chúng một cách thủ công. Lập trình ứng dụng của bạn để nhận biết và phản hồi lại chuyển động lướt tay là một việc phức tạp. Android hỗ trợ rất nhiều cho lập trình viên, do đó công việc chẳng những nhẹ nhàng hơn mà còn cung cấp cho bạn sự linh hoạt cao. Bạn có thể chọn nhận dạng một cái lướt tay đơn như tôi làm ở đây. Bạn cũng có thể nhận dạng chuyển động của ngón tay khác, chẳng hạn đôi khi có chuyển động cọ xát trong game.

Nhìn lại thiết bị di động

Bạn không thắc mắc rằng bộ lắng nghe sự kiện của ứng dụng di động vẫn có thể được nhận dạng bằng cách click (nhấp)? Lần cuối cùng tôi kiểm nghiệm điều đó, người dùng nhấn (press) button trên thiết bị di động của họ hơn là nhấp (click) như khi dùng chuột trên máy tính. Một phần của việc trở thành một nhà phát triển di động có liên quan đến việc hiểu rõ các thuật ngữ, có nghĩa là bạn suy nghĩ lại cơ chế web và máy tính truyền thống trong việc nhấp button, khởi động menu, và thậm chí là giao diện người dùng.

Lập trình nhận dạng lướt tay có liên quan đến các tính toán Cartesian. Cụ thể, nếu tọa độ bắt đầu của điểm chạm trừ đi tọa độ kết thúc trên trục đó lớn hơn khoảng cách đã xác định trước, và nếu vận tốc của chuyển động đó lớn hơn so với giá trị xác định khác, thì bạn có thể giả định một cách hợp lý là người dùng đang lướt tay từ bên phải ứng dụng bạn để hiển thị bên trái. Các động tác lượt tay lên và xuống cũng tính toán tương tự như vậy đối với trục Y.

Tôi có thể trừu tượng hóa quá trình logic này vào một lớp đơn giản như khi làm toán tiểu học. Bộ lắng nghe SimpleOnGestureListener có một phương thức được gọi là onFling. Phương thức onFling có hai loại MotionEvent (cho điểm bắt đầu (start)điểm kết thúc (end)) để tượng trưng cho vận tốc X và 2 float tượng trưng cho vận tốc Y. Lớp SwipeDetector trong Liệt kê 4 phác họa 4 phương thức đầy đủ phạm vi của chuyển động: trái , phải, trên, và dưới.

Liệt kê 4. SwipeDetector
import android.view.MotionEvent;

public class SwipeDetector {

    private int swipe_distance;
    private int swipe_velocity;
    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;

    public SwipeDetector(int distance, int velocity) {
        super();
        this.swipe_distance = distance;
        this.swipe_velocity = velocity;
    }

    public SwipeDetector() {
        super();
        this.swipe_distance = SWIPE_MIN_DISTANCE;
        this.swipe_velocity = SWIPE_THRESHOLD_VELOCITY;
    }

    public boolean isSwipeDown(MotionEvent e1, MotionEvent e2, float velocityY) {
        return isSwipe(e2.getY(), e1.getY(), velocityY);
    }

    public boolean isSwipeUp(MotionEvent e1, MotionEvent e2, float velocityY) {
        return isSwipe(e1.getY(), e2.getY(), velocityY);
    }

    public boolean isSwipeLeft(MotionEvent e1, MotionEvent e2, float velocityX) {
        return isSwipe(e1.getX(), e2.getX(), velocityX);
    }

    public boolean isSwipeRight(MotionEvent e1, MotionEvent e2, float velocityX) {
        return isSwipe(e2.getX(), e1.getX(), velocityX);
    }

    private boolean isSwipeDistance(float coordinateA, float coordinateB) {
        return (coordinateA - coordinateB) > this.swipe_distance;
    }

    private boolean isSwipeSpeed(float velocity) {
        return Math.abs(velocity) > this.swipe_velocity;
    }

    private boolean isSwipe(float coordinateA, float coordinateB, float velocity) {
        return isSwipeDistance(coordinateA, coordinateB)
                && isSwipeSpeed(velocity);
    }
}

Bây giờ tôi có một lớp tiện dụng cho tôi biết được liệu cử chỉ có xảy ra hay không, và tôi có thể đưa nó vào giao diện. Nhớ lại khi lần đầu tôi tạo một Activity mặc định, tên là OverheardWord. Tôi bỏ tất cả mã mặc định mà Eclipse sinh ra, và thêm vào một số thứ. Để lắng nghe và phản hồi lại hành động lướt tay, tôi định nghĩa một GestureDetector cho Android, nó nhằm thực hiện sự kiện OnGestureListener. Rất may, Android đã tạo ra một lớp tiện lợi mà tôi có thể thực hiện thông qua một lớp ẩn danh tên là SimpleOnGestureListener. Sau đó tôi ghi đè phương thức onFling.

Nhận dạng hành động lướt tay trong giao diện người dùng

Trong phương thức onCreate của Activity, tôi bắt đầu tạo một GestureDetector với một SimpleOnGestureListener, được dùng trong lớp SwipeDetector.

Tiếp theo, tôi viết một hộp thoại nhỏ để chỉ ra rằng sự kiện được gióng lên khi có ai đó lướt tay qua. Đây là loại hộp thoại nhỏ, vòng đời ngắn, trên Android gọi là Toast. Trong một vài trường hợp, một Toast có thể bao gồm chỉ một dòng mã.

Liệt kê 5. GestureDetector
private GestureDetector initGestureDetector() {
    return new GestureDetector(new SimpleOnGestureListener() {
        
        private SwipeDetector detector = new SwipeDetector();
        
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
               float velocityY) {
            try {
                if (detector.isSwipeDown(e1, e2, velocityY)) {
                    return false;
                } else if (detector.isSwipeUp(e1, e2, velocityY)) {
                    showToast("Up Swipe");
                }else if (detector.isSwipeLeft(e1, e2, velocityX)) {
                    showToast("Left Swipe");
                } else if (detector.isSwipeRight(e1, e2, velocityX)) {
                    showToast("Right Swipe");
                }
            } catch (Exception e) {} //for now, ignore
            return false;
        }
        
        private void showToast(String phrase){
            Toast.makeText(getApplicationContext(), phrase, Toast.LENGTH_SHORT).show();
        }
    });
}

Bây giờ tôi tạo một phương thức initGestureDetector để tính toán phạm vi và vận tốc lướt tay của người dùng. Đầu tiên, tạo ra một thể hiện ẩn của SimpleOnGestureListener và cung cấp thực hiện cho phương thức onFling. Lưu ý cách mà phương thức onFling sử dụng SwipeDetector. Ví dụ, nếu hành động lướt tay diễn ra, một Toast hiển thị để dò tìm chuyển động.

Tiếp theo, ta gắn các bộ nhận diện lướt tay vào giao diện người dùng. Tôi bắt đầu đăng ký một vài bộ lắng nghe với thể hiện của View.

Liệt kê 6. Đăng ký nhận diện lướt tay trên View
private GestureDetector gestureDetector;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_overheard_word);

    gestureDetector = initGestureDetector();

    View view = findViewById(R.id.LinearLayout1);

    view.setOnTouchListener(new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
        }
    });

    view.setOnClickListener(new OnClickListener() {
        public void onClick(View arg0) {
        }
    });
}

Trong phương thức onCreate, tôi đã quản lý thể hiện của View thông qua một Id. Hầu hết các nhà phát triển Java đánh giá cao sự tiện dụng Android-ism này.

Tìm view theo phương pháp ID

Hãy nhớ khi bạn định nghĩa XML cho giao diện, cùng với các giá trị tương ứng trong tập tin tài nguyên khác nhau (như strings.xml)? Khi tôi biên dịch ứng dụng Android, mã XML đó chuyển thành mã tự sinh và gửi Id tương ứng vào một lớp gọi là R. Hãy kiểm tra lớp R trong thư mục gen thuộc dự án của bạn trước khi tiếp tục.

Xem xét tệp layout XML của bạn (bạn có thể nhớ rằng tôi đã đặt tên là activity_overheard_word.xml). Trong tệp XML đó, mỗi widget đều có thuộc tính id. Ví dụ, id của layout trong ứng dụng của tôi là android:id="@+id/LinearLayout1", hay ngắn hơn là LinearLayout1. Tên Id được Eclipse sinh ra tự động nhưng tôi có thể thay đổi bất kỳ lúc nào. Điều quan trọng là id, LinearLayout1, có thuộc tính tương ứng với lớp R, như trong Liệt kê 7.

Liệt kê 7. Widget IDs trong lớp R
public static final class id {
    public static final int LinearLayout1=0x7f090000;
    public static final int action_settings=0x7f090004;
    public static final int word_study_definition=0x7f090003;
    public static final int word_study_part_of_speech=0x7f090002;
    public static final int word_study_word=0x7f090001;
}

Việc liên kết nội dung của tệp XML với tệp R tương ứng, tôi có thể tham khảo đến các widget mà không cần phân tích XML. Vì vậy để trả về phương thức onCreate của Activity, tôi có thể nhận một tham chiếu đến widget của View thông qua id của nó: R.id.LinearLayout1. Phương thức findViewById được cung cấp bởi nền tảng Android khi bạn mở rộng Activity.

Finger swiping good!

Sau khi có một thể hiện View, tôi có thể đính kèm thể hiện gestureDetector thông qua setOnTouchListener. Trong Liệt kê 6, lớp ẩn danh khác bây giờ xử lý tất cả hành vi chạm của tôi. Khi người dùng chạm vào màn hình thiết bị, sự kiện này được gióng lên và chỉ đến gestureDetector. Tôi cũng thực hiện một OnClickListener và đặt nó vào phương thức setOnClickListener; nhưng trong trường hợp này, lớp ẩn danh không có hành vi nào.

Với tất cả những điều đó, tôi có một giao diện khác đơn giản có thể phản hồi cử chỉ lướt tay. Trong trường hợp này là ba trong số chúng: trái, phải, và hướng lên. Hãy chạy thử đoạn mã trong AVD của bạn. Để mô phỏng cử chỉ lướt tay, hãy dùng chuột và kéo qua trái, phải, hay lên.

Bây giờ, làm sao để chạy ứng dụng này lên thiết bị thật?


Triển khai

Để phản hồi lại sự phổ biến của virus, trojan và các phần mềm độc hại gây nguy hại cho người dùng, cả Apple và Google đều giới thiệu mẫu mã xác thực (code signing) cho ứng dụng của bên thứ ba chạy trên các thiết bị tương ứng. Trên lý thuyết, mã xác thực đóng vai trò như là bảo hiểm mà một người có uy tín xây dựng ứng dụng mà không ai can thiệp vào tệp mã nhị phân trước khi cài đặt. Thực tế, Google và Apple cung cấp các mã xác thực bảo mật khác nhau. Khi tôi xác thực ứng dụng Android, ví dụ, tôi có thể khẳng định tôi là một ngân hàng quốc gia nổi tiếng. Nhưng tôi không thể làm như vậy khi đăng ký ứng dụng của tôi trên iOS hay Windows Phone 8.

Trong môi trường phát triển, mã xác thực không đáng lo ngại. Trong thực thế, Android và Eclipse xây dựng mã xác thực nhị phân với một khóa phát triển mà bạn không biết. Vì vậy, việc đưa ứng dụng đến một thiết bị thì không khó trong môi trường phát triển. Cách đơn giản nhất để đưa ứng dụng của bạn lên thiết bị di động là gắn thiết bị Android của bạn vào máy tính.

Tiếp theo, vào Eclipse, nhấp chuột phải vào dự án (project) của bạn, và chọn tùy chọn Run As menu, sau đó chọn Run Configurations. Eclipse sẽ hiển thị hộp thoại cấu hình nơi bạn có thể chọn dự án của mình và nhấp vào thẻ Target, như Hình 9.

Hình 9. Chọn thiết bị để chạy ứng dụng của bạn
Ảnh chụp chọn một thiết bị trong Eclipse

Bạn có thể chọn Always prompt to pick device (nếu bạn muốn) hoặc Launch on all compatible devices/AVD's. Nếu bạn chọn cái sau giống tôi, Active Devices, sau đó nhấp Run. Nếu bạn viết mã giống như tôi thì một lúc sau, tiện ích sẽ hiển thị trên thiết bị của bạn. Lướt tay qua trái, phải, lên, và thấy rằng các hộp thoại đơn giản xác nhận từng hành động.

Các thiết lập bảo mật

Nếu bạn không thể tải ứng dụng từ email hay Dropbox, hãy vào mục thiết lập (settings) và kích hoạt Unknown Sources nằm trong mục Security Settings. Sau đó, bạn sẽ có thể cài đặt ứng dụng không có trên Google Play, bao gồm cả ứng dụng đang phát triển của bạn.

Có một các khác để triển khai ứng dụng của bạn là gửi tệp .apk của bạn qua email, sau đó mở nó trên thiết bị Android và làm theo hộp thoại chỉ dẫn cài đặt. Hoặc bạn có thể tải tệp .apk lên một dịch vụ lưu trữ như Dropbox, sau đó mở ứng dụng Dropbox trên thiết bị của bạn và cài đặt nó. Tệp .apk cho ứng dụng của bạn là thư mục bin trong dự án, nơi Eclipse đặt các mã nhị phân tương ứng.

Hãy nhớ rằng cơ chế triển khai là để thử nghiệm chứ chưa phải phân phối. Để phân phối và kiếm tiền từ ứng dụng của bạn cần thông qua nhiều bước hơn, tôi sẽ thảo luận điều này trong một bài viết sau.


Kết luận

Thiết kế một ứng dụng di động có nghĩa là làm cho nó đơn giản, dễ dàng. Lần này, bạn đã học cách viết một ứng dụng di động phản hồi lại cử chỉ lướt tay thay vì dùng các button (nút bấm) như trên web và máy tính. Trong nhiều trường hợp, một ứng dụng di động có thể có lợi với một hay hai button. Hãy chắc chắn rằng chúng phù hợp với thị hiếu của người dùng. Với ứng dụng Overhead Word quá đơn giản của tôi, tôi không cần thiết phải có các button, và tôi thấy người dùng thường sử dụng động tác lướt lên, lướt xuống rất nhiều.

Tôi có nói rằng ứng dụng di động sẽ được triển khai nhanh chóng ở đây nhưng chỉ đi sâu vào việc thử nghiệm — bạn vẫn chưa triển khai sản phẩm được. Hầu hết người dùng sẽ không cài đặt các ứng dụng không đáng tin cậy, đó là lý do ứng dụng thành công nhất được phân phối bởi một kho trung gian như Google Play, iTunes App Store, hay Amazon Appstore cho Android. Trong bài tiếp theo, tôi sẽ nói qua về các tiêu chuẩn an ninh của một kho ứng dụng. Quá trình đó đi qua một vài bước xác thực ứng dụng của bạn. Điều quan trọng là sau đó ứng dụng của bạn được lưu trữ bởi một nhà phân phối lớn, sẵn sàng cho cả thế giới.

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

  • Tải về Android: Android SDK cung cấp cho bạn các thư viện API và công cụ phát triển cần thiết để xây dựng, kiểm thử và debug ứng dụng Android của bạn.
  • Tải về IBM developer kits: Cập nhật hệ thống của bạn và lấy về các công cụng công nghệ mới nhất tại đây.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, Nguồn mở
ArticleID=943069
ArticleTitle=Di động cho mọi người: Lập trình xử lý cử chỉ trong Android
publish-date=08302013