Thao tác và ngăn xếp lui

1. Trước khi bắt đầu

Trong lớp học lập trình này, bạn sẽ hoàn tất việc triển khai phần còn lại của ứng dụng Cupcake (ứng dụng mà bạn đã bắt đầu trong một lớp học lập trình trước đó). Ứng dụng Cupcake có nhiều màn hình và hiển thị luồng đơn đặt hàng cupcake. Ứng dụng hoàn chỉnh phải cho phép người dùng di chuyển xuyên suốt ứng dụng để:

  • Tạo đơn đặt hàng cupcake
  • Sử dụng nút Up (Lên) hoặc Back (Quay lại) để quay về bước trước của luồng đơn đặt hàng
  • Huỷ đơn đặt hàng
  • Gửi đơn đặt hàng đến ứng dụng khác, chẳng hạn như ứng dụng email

Đồng thời, bạn sẽ tìm hiểu về cách Android xử lý nhiệm vụ và ngăn xếp lui cho một ứng dụng. Điều này cho phép bạn thao tác ngăn xếp lui trong các tình huống như huỷ đơn đặt hàng, đưa người dùng quay lại màn hình đầu tiên của ứng dụng (thay vì màn hình trước của luồng đơn đặt hàng).

Điều kiện tiên quyết

  • Có thể tạo và sử dụng mô hình hiển thị dùng chung trên các mảnh trong một hoạt động
  • Làm quen với việc sử dụng thành phần điều hướng Jetpack
  • Đã sử dụng tính năng liên kết dữ liệu với LiveData để đồng bộ hoá giao diện người dùng với mô hình hiển thị
  • Có thể tạo ý định để bắt đầu hoạt động mới

Kiến thức bạn sẽ học được

  • Cách điều hướng ảnh hưởng đến ngăn xếp lui của ứng dụng
  • Cách triển khai hành vi ngăn xếp lui tuỳ chỉnh

Sản phẩm bạn sẽ tạo ra

  • Một ứng dụng đặt cupcake cho phép người dùng gửi đơn đặt hàng đến một ứng dụng khác và cho phép huỷ đơn đặt hàng

Bạn cần có

  • Một máy tính đã cài đặt Android Studio.
  • Mã cho ứng dụng Cupcake sau khi kết thúc lớp học lập trình trước

2. Tổng quan về ứng dụng khởi đầu

Lớp học lập trình này sử dụng ứng dụng Cupcake từ lớp học lập trình trước. Bạn có thể sử dụng mã của mình sau khi kết thúc lớp học lập trình trước đó hoặc tải mã khởi động xuống qua GitHub.

Tải mã khởi động cho lớp học lập trình này

Nếu tải mã khởi động xuống qua GitHub, hãy lưu ý tên thư mục của dự án là android-basics-kotlin-cupcake-app-viewmodel. Chọn thư mục này khi mở dự án trong Android Studio.

Để lấy mã cho lớp học lập trình này và mở trong Android Studio, hãy thực hiện các bước sau.

Lấy mã

  1. Nhấp vào URL được cung cấp. Thao tác này sẽ mở trang GitHub của dự án trong một trình duyệt.
  2. Trên trang GitHub của dự án, hãy nhấp vào nút Code (Mã), một hộp thoại sẽ xuất hiện.

5b0a76c50478a73f.png

  1. Trong hộp thoại này, hãy nhấp vào nút Download ZIP (Tải tệp ZIP xuống) để lưu dự án vào máy tính. Chờ quá trình tải xuống hoàn tất.
  2. Xác định vị trí của tệp trên máy tính (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  3. Nhấp đúp vào tệp ZIP để giải nén. Thao tác này sẽ tạo một thư mục mới chứa các tệp dự án.

Mở dự án trong Android Studio

  1. Khởi động Android Studio.
  2. Trong cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy nhấp vào Open an existing Android Studio project (Mở một dự án hiện có trong Android Studio).

36cc44fcf0f89a1d.png

Lưu ý: Nếu Android Studio đã mở sẵn thì thay vào đó, hãy chọn tuỳ chọn sau đây trong trình đơn File > New > Import Project (Tệp > Mới > Nhập dự án).

21f3eec988dcfbe9.png

  1. Trong hộp thoại Import Project (Nhập dự án), hãy chuyển đến nơi chứa thư mục dự án đã giải nén (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  2. Nhấp đúp vào thư mục dự án đó.
  3. Chờ Android Studio mở dự án.
  4. Nhấp vào nút Run (Chạy) 11c34fc5e516fb1c.png để xây dựng và chạy ứng dụng. Hãy đảm bảo ứng dụng được dựng như mong đợi.
  5. Duyệt qua các tệp dự án trong cửa sổ công cụ Project (Dự án) để xem cách ứng dụng được thiết lập.

Chạy ứng dụng mới và ứng dụng sẽ có dạng như sau.

45844688c0dc69a2.png

Trong lớp học lập trình này, trước tiên bạn sẽ hoàn tất việc triển khai nút Up (Lên) trong ứng dụng. Nhờ đó, thao tác nhấn vào nút này sẽ đưa người dùng quay lại bước trước đó trong luồng đơn đặt hàng.

fbdc1793f9fea6da.png

Sau đó, bạn sẽ thêm nút Cancel (Huỷ) để người dùng có thể huỷ đơn đặt hàng nếu họ đổi ý trong quá trình đặt hàng.

d66fdafeac1b0dcf.gif

Rồi bạn sẽ mở rộng ứng dụng để khi nhấn vào Send Order to Another App (Gửi đơn đặt hàng tới ứng dụng khác) sẽ chia sẻ đơn đặt hàng với ứng dụng khác. Sau đó, đơn đặt hàng có thể được gửi đến cửa hàng cupcake qua email chẳng hạn.

170d76b64ce78f56.png

Cùng tìm hiểu kỹ hơn và hoàn thành ứng dụng Cupcake nhé!

3. Triển khai hành vi của nút Up (Lên)

Trong ứng dụng Cupcake, thanh ứng dụng sẽ hiển thị một mũi tên để quay lại màn hình trước. Đây được gọi là nút Up (Lên) mà bạn đã tìm hiểu trong các lớp học lập trình trước. Nút Up (Lên) hiện không có tác dụng gì, do vậy, trước hết hãy sửa lỗi di chuyển này trong ứng dụng.

fbdc1793f9fea6da.png

  1. Trong MainActivity, bạn phải có mã để thiết lập thanh ứng dụng (còn gọi là thanh thao tác) bằng trình điều khiển điều hướng. Đặt navController làm biến lớp để bạn có thể sử dụng mã này trong phương thức khác.
class MainActivity : AppCompatActivity(R.layout.activity_main) {

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val navHostFragment = supportFragmentManager
                .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController

        setupActionBarWithNavController(navController)
    }
}
  1. Trong cùng một lớp, thêm mã để ghi đè hàm onSupportNavigateUp(). Mã này sẽ yêu cầu navController xử lý việc di chuyển lên trong ứng dụng. Nếu không, quay lại triển khai siêu lớp (trong AppCompatActivity) để xử lý nút Up (Lên).
override fun onSupportNavigateUp(): Boolean {
   return navController.navigateUp() || super.onSupportNavigateUp()
}
  1. Chạy ứng dụng. Giờ đây nút Up (Lên) sẽ hoạt động từ FlavorFragment, PickupFragmentSummaryFragment. Khi bạn di chuyển đến các bước trước trong luồng đơn đặt hàng, các mảnh phải hiển thị đúng hương vị và ngày đến lấy hàng từ mô hình hiển thị.

4. Tìm hiểu về nhiệm vụ và ngăn xếp lui

Bây giờ, bạn sẽ đưa nút Cancel (Huỷ) vào bên trong luồng đơn đặt hàng của ứng dụng. Việc huỷ đơn vào bất kỳ thời điểm nào trong quá trình đặt hàng sẽ đưa người dùng quay lại StartFragment. Để xử lý hành vi này, bạn sẽ tìm hiểu về các nhiệm vụ và ngăn xếp lui trong Android.

Nhiệm vụ

Hoạt động trên Android tồn tại bên trong các nhiệm vụ. Khi bạn mở một ứng dụng lần đầu tiên từ biểu tượng trình chạy, Android sẽ tạo một nhiệm vụ mới dựa trên hoạt động chính của bạn. Nhiệm vụ là tập hợp các hoạt động mà người dùng tương tác khi thực hiện một công việc nhất định (ví dụ kiểm tra email, tạo đơn đặt hàng cupcake, chụp ảnh).

Hoạt động được sắp xếp trong một ngăn xếp, gọi là ngăn xếp lui, nơi từng hoạt động mới, được người dùng truy cập, sẽ được đẩy vào ngăn xếp lui cho nhiệm vụ. Bạn có thể xem nhiệm vụ là một nhóm bánh kếp, trong đó, mỗi một chiếc bánh mới được thêm vào đầu ngăn xếp. Hoạt động ở đầu ngăn xếp là hoạt động hiện tại mà người dùng đang tương tác. Các hoạt động bên dưới hoạt động này trong ngăn xếp đã được đưa vào nền và bị dừng.

517054e483795b46.png

Ngăn xếp lui rất hữu ích khi người dùng muốn quay lui. Android có thể xoá hoạt động hiện tại khỏi đầu ngăn xếp, huỷ và bắt đầu lại hoạt động nằm bên dưới. Thao tác này được gọi là kéo hoạt động ra khỏi ngăn xếp và đưa hoạt động trước lên nền trước để người dùng tương tác cùng. Nếu người dùng muốn quay lại nhiều lần, Android sẽ tiếp tục kéo các hoạt động ra khỏi đầu ngăn xếp cho đến khi bạn tiến gần hơn đến cuối ngăn xếp. Khi không còn hoạt động nào trong ngăn xếp lui, người dùng được đưa trở lại màn hình trình chạy của thiết bị (hoặc đến ứng dụng đã khởi chạy màn hình này).

Cùng xem phiên bản của ứng dụng Words (Từ) mà bạn đã triển khai với 2 hoạt động: MainActivityDetailActivity.

Khi bạn chạy ứng dụng lần đầu tiên, MainActivity sẽ mở và được thêm vào ngăn xếp lui của nhiệm vụ.

4bc8f5aff4d5ee7f.png

Khi bạn nhấp vào một chữ cái, DetailActivity sẽ được khởi chạy và đẩy vào ngăn xếp lui. Điều này có nghĩa là DetailActivity đã được tạo, khởi động và tiếp tục để người dùng có thể tương tác cùng. MainActivity được đưa vào chế độ nền và hiển thị với màu nền xám trong sơ đồ.

80f7c594ae844b84.png

Nếu bạn nhấn vào nút Back (Quay lại), DetailActivity sẽ được kéo ra khỏi ngăn xếp lui còn thực thể DetailActivity bị huỷ và kết thúc.

80f532af817191a4.png

Sau đó, mục tiếp theo bên trên ngăn xếp lui (MainActivity) được đưa lên nền trước.

85004712d2fbcdc1.png

Giống như cách ngăn xếp phía sau có thể theo dõi hoạt động mà người dùng đã mở, ngăn xếp lui cũng có thể theo dõi các điểm đến của mảnh mà người dùng đã truy cập với sự hỗ trợ của thành phần Điều hướng Jetpack.

fe417ac5cbca4ce7.png

Thư viện Điều hướng cho phép bạn kéo điểm đến của mảnh ra khỏi ngăn xếp lui mỗi khi người dùng nhấn nút Back (Quay lại). Hành vi mặc định này được cung cấp miễn phí và bạn không cần phải triển khai hành vi nào cả. Bạn chỉ cần viết mã nếu cần đến hành vi ngăn xếp lui tuỳ chỉnh và sẽ được bạn thực hiện cho ứng dụng Cupcake.

Hành vi mặc định của ứng dụng Cupcake

Cùng xem cách ngăn xếp lui hoạt động trong ứng dụng Cupcake. Chỉ có một hoạt động trong ứng dụng, nhưng có nhiều điểm đến của mảnh mà người dùng di chuyển qua. Do đó, nút Back (Quay lại) cần phải quay trở lại điểm đến của mảnh trước đó mỗi khi được nhấn vào.

Khi bạn mở ứng dụng lần đầu tiên, điểm đến StartFragment sẽ được hiển thị. Điểm đến đó được đẩy lên bên trên ngăn xếp.

cf0e80b4907d80dd.png

Sau khi chọn số lượng cupcake để đặt hàng, bạn di chuyển đến FlavorFragment. Mã này sẽ được đẩy vào ngăn xếp lui.

39081dcc3e537e1e.png

Khi chọn một hương vị và nhấn vào Next (Tiếp theo), bạn sẽ di chuyển đến PickupFragment. Mã này sẽ được đẩy vào ngăn xếp lui.

37dca487200f8f73.png

Cuối cùng, sau khi chọn ngày đến lấy hàng, rồi nhấn vào Next (Tiếp theo), bạn sẽ di chuyển đến SummaryFragment. Mã này sẽ được thêm vào bên trên ngăn xếp lui.

d67689affdfae0dd.png

Từ SummaryFragment, giả sử bạn nhấn vào nút Back (Quay lại) hoặc Up (Lên). SummaryFragment được kéo ra khỏi ngăn xếp và bị huỷ.

215b93fd65754017.png

Bây giờ PickupFragment nằm ở trên cùng ngăn xếp lui và được hiển thị đến người dùng.

37dca487200f8f73.png

Nhấn nút Back (Quay lại) hoặc Up (Lên) lần nữa. PickupFragment được kéo ra khỏi ngăn xếp và rồi bạn sẽ nhìn thấy FlavorFragment.

Nhấn nút Back (Quay lại) hoặc Up (Lên) lần nữa. FlavorFragment được kéo ra khỏi ngăn xếp và rồi bạn sẽ nhìn thấy StartFragment.

Khi bạn di chuyển ngược về các bước trước đó trong luồng đơn đặt hàng, mỗi lần chỉ một điểm đến được kéo ra. Nhưng trong nhiệm vụ tiếp theo, bạn sẽ thêm tính năng huỷ đơn đặt hàng vào ứng dụng. Điều này đòi hỏi bạn phải kéo nhiều điểm đến ra khỏi ngăn xếp lui cùng lúc để đưa người dùng quay về StartFragment nhằm bắt đầu một đơn đặt hàng mới.

e3dae0f492450207.png

Sửa đổi ngăn xếp lui trong ứng dụng Cupcake

Sửa đổi các tệp lớp và bố cục FlavorFragment, PickupFragmentSummaryFragment nhằm cung cấp nút Huỷ đơn đặt hàng cho người dùng.

Thêm thao tác di chuyển

Trước tiên, thêm các thao tác di chuyển vào sơ đồ điều hướng trong ứng dụng để người dùng có thể quay lại StartFragment từ các điểm đến tiếp theo.

  1. Mở Navigation Editor (Trình chỉnh sửa điều hướng) bằng cách chuyển đến tệp res > navigation > nav_map.xml rồi chọn chế độ xem Design.
  2. Vào lúc này, có một thao tác từ startFragment đến flavorFragment, một thao tác từ flavorFragment đến pickupFragment và một thao tác từ pickupFragment đến summaryFragment.
  3. Nhấp và kéo để tạo một thao tác điều hướng mới từ summaryFragment đến startFragment. Bạn có thể xem các hướng dẫn này nếu muốn ôn lại cách kết nối các điểm đến trong sơ đồ điều hướng.
  4. Từ pickupFragment, nhấp và kéo để tạo một thao tác mới cho startFragment.
  5. Từ flavorFragment, nhấp và kéo để tạo một thao tác mới cho startFragment.
  6. Khi bạn hoàn tất, sơ đồ điều hướng sẽ có dạng như sau.

dcbd27a08d24cfa0.png

Với những thay đổi này, người dùng có thể di chuyển từ một trong các mảnh ở cuối luồng đơn đặt hàng trở lại đầu luồng đơn đặt hàng. Bây giờ, bạn cần mã thực sự di chuyển với các thao tác đó. Bạn cần nhấn vào nút Cancel (Huỷ).

Thêm nút Cancel (Huỷ) vào bố cục

Trước tiên, thêm nút Cancel (Huỷ) vào các tệp bố cục cho tất cả các mảnh, trừ StartFragment. Không cần huỷ đơn đặt hàng nếu bạn đã ở màn hình đầu tiên của luồng đơn đặt hàng.

  1. Mở tệp bố cục fragment_flavor.xml.
  2. Sử dụng chế độ xem Split (phân tách) để trực tiếp chỉnh sửa XML và xem màn hình xem trước đặt cạnh nhau.
  3. Thêm nút Cancel (Huỷ) ở giữa chế độ xem văn bản tổng phụ và nút Next (Tiếp theo). Gán cho nút một mã định dạng (ID) tài nguyên @+id/cancel_button với văn bản để hiển thị dưới dạng @string/cancel.

Nút này nên được đặt theo chiều ngang bên cạnh nút Next (Tiếp theo) để có được một hàng nút. Đối với điều kiện ràng buộc theo chiều dọc, ràng buộc phần trên cùng của nút Cancel (Huỷ) nằm ngang với phần trên cùng của nút Next (Tiếp theo). Đối với điều kiện ràng buộc theo chiều ngang, ràng buộc đầu nút Huỷ vào vùng chứa mẹ và ràng buộc đuôi của nút đó nằm với đầu nút Tiếp theo.

Ngoài ra, cho nút Cancel (Huỷ) có chiều cao là wrap_content và chiều rộng là 0dp để nút này có thể chia đều chiều rộng của màn hình với nút khác. Lưu ý rằng nút này không hiển thị trên ngăn Preview (Xem trước) cho đến bước tiếp theo.

...

<TextView
    android:id="@+id/subtotal" ... />

<Button
    android:id="@+id/cancel_button"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="@string/cancel"
    app:layout_constraintEnd_toStartOf="@id/next_button"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/next_button" />

<Button
    android:id="@+id/next_button" ... />

...
  1. Trong fragment_flavor.xml, bạn cũng cần thay đổi giới hạn bắt đầu của nút Next (Tiếp theo) từ app:layout_constraintStart_toStartOf="parent" đến app:layout_constraintStart_toEndOf="@id/cancel_button". Ngoài ra, thêm lề cuối vào nút Cancel (Huỷ) để có một ít khoảng trắng giữa hai nút. Giờ đây, nút Cancel (Huỷ) sẽ xuất hiện trong ngăn Preview (Xem trước) trong Android Studio.
...

<Button
    android:id="@+id/cancel_button"
    android:layout_marginEnd="@dimen/side_margin" ... />

<Button
    android:id="@+id/next_button"
    app:layout_constraintStart_toEndOf="@id/cancel_button"... />

...
  1. Về phong cách hình ảnh, áp dụng phong cách Material Outlined Button (Nút với đường viền Material) (có thuộc tính style="?attr/materialButtonOutlinedStyle") để nút Cancel (Huỷ) trông không nổi bật hơn nút Next (Tiếp theo) là thao tác chính mà bạn muốn người dùng tập trung.
<Button
    android:id="@+id/cancel_button"
    style="?attr/materialButtonOutlinedStyle" ... />

Vào lúc này, nút và vị trí đặt nút trông rất đẹp!

1fb41763cc255c05.png

  1. Tương tự như vậy, thêm nút Cancel (Huỷ) vào tệp bố cục fragment_pickup.xml.
...

<TextView
    android:id="@+id/subtotal" ... />

<Button
    android:id="@+id/cancel_button"
    style="?attr/materialButtonOutlinedStyle"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="@dimen/side_margin"
    android:text="@string/cancel"
    app:layout_constraintEnd_toStartOf="@id/next_button"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/next_button" />

<Button
    android:id="@+id/next_button" ... />

...
  1. Đồng thời, cập nhật giới hạn bắt đầu trên nút Next (Tiếp theo). Sau đó, nút Cancel (Huỷ) sẽ xuất hiện trong chế độ xem trước.
<Button
    android:id="@+id/next_button"
    app:layout_constraintStart_toEndOf="@id/cancel_button" ... />
  1. Thực hiện thay đổi tương tự với tệp fragment_summary.xml, mặc dù bố cục cho mảnh này hơi khác. Bạn sẽ thêm nút Cancel (Huỷ) ở bên dưới nút Send (Gửi) trong chiều dài mẹ LinearLayout với một chút lề ở giữa.

741c0f034397795c.png

...

    <Button
        android:id="@+id/send_button" ... />

    <Button
        android:id="@+id/cancel_button"
        style="?attr/materialButtonOutlinedStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin_between_elements"
        android:text="@string/cancel" />

</LinearLayout>
  1. Chạy và kiểm thử ứng dụng. Bây giờ, bạn sẽ thấy nút Cancel (Huỷ) xuất hiện trong bố cục cho FlavorFragment, PickupFragmentSummaryFragment. Tuy nhiên, việc nhấn vào nút này vẫn chưa có tác dụng nào cả. Thiết lập trình nghe lượt nhấp cho các nút này trong bước tiếp theo.

Thêm trình nghe lượt nhấp vào nút Cancel (Huỷ)

Trong mỗi lớp mảnh (trừ StartFragment) thêm một phương thức trợ giúp để xử lý khi người dùng nhấp vào nút Cancel (Huỷ).

  1. Thêm phương thức cancelOrder() này vào FlavorFragment. Khi xem tuỳ chọn hương vị, nếu người dùng quyết định huỷ đơn đặt hàng, hãy xoá mô hình chế độ xem bằng cách gọi sharedViewModel.resetOrder(). Sau đó, quay lại StartFragment bằng thao tác di chuyển với mã nhận dạng R.id.action_flavorFragment_to_startFragment.
fun cancelOrder() {
    sharedViewModel.resetOrder()
    findNavController().navigate(R.id.action_flavorFragment_to_startFragment)
}

Nếu thấy lỗi liên quan đến mã nhận diện (ID) tài nguyên thao tác, có lẽ bạn cần quay lại tệp nav_graph.xml để xác minh thao tác di chuyển đó có cùng tên gọi là (action_flavorFragment_to_startFragment).

  1. Dùng liên kết trình nghe để thiết lập trình nghe lượt nhấp trên nút Cancel (Huỷ) trong bố cục fragment_flavor.xml. Nhấp vào nút này sẽ gọi phương thức cancelOrder() vừa được bạn tạo trong lớp FragmentFlavor.
<Button
    android:id="@+id/cancel_button"
    android:onClick="@{() -> flavorFragment.cancelOrder()}" ... />
  1. Lặp lại quy trình tương tự cho PickupFragment. Thêm phương thức cancelOrder() vào lớp mảnh để đặt lại đơn đặt hàng và chuyển từ PickupFragment đến StartFragment.
fun cancelOrder() {
    sharedViewModel.resetOrder()
    findNavController().navigate(R.id.action_pickupFragment_to_startFragment)
}
  1. Trong fragment_pickup.xml, đặt trình nghe lượt nhấp vào nút Cancel (Huỷ) để gọi phương thức cancelOrder() khi người dùng nhấp vào.
<Button
    android:id="@+id/cancel_button"
    android:onClick="@{() -> pickupFragment.cancelOrder()}" ... />
  1. Thêm mã tương tự cho nút Huỷ trong SummaryFragment để đưa người dùng quay lại StartFragment. Bạn cần nhập (import) androidx.navigation.fragment.findNavController nếu mã này không được nhập tự động cho bạn.
fun cancelOrder() {
    sharedViewModel.resetOrder()
    findNavController().navigate(R.id.action_summaryFragment_to_startFragment)
}
  1. Trong fragment_summary.xml, gọi phương thức cancelOrder() của SummaryFragment khi nút Cancel (Huỷ) được nhấp vào.
<Button
    android:id="@+id/cancel_button"
    android:onClick="@{() -> summaryFragment.cancelOrder()}" ... />
  1. Chạy và kiểm thử ứng dụng để xác minh logic vừa được bạn thêm vào cho từng mảnh. Khi bạn tạo đơn đặt hàng cupcake, nhấn vào nút Huỷ trên FlavorFragment, PickupFragment hoặc SummaryFragment sẽ đưa bạn trở lại StartFragment. Khi tạo đơn đặt hàng mới, bạn sẽ thấy thông tin từ đơn đặt hàng trước đã bị xoá.

Có vẻ như mã này hoạt động, nhưng thực ra có lỗi khi di chuyển ngược lại, một khi bạn quay lại StartFragment. Thực hiện một vài bước tiếp theo để tái tạo lỗi này.

  1. Di chuyển qua luồng đơn đặt hàng để tạo đơn đặt hàng cupcake mới cho đến khi bạn đến được màn hình tóm tắt. Ví dụ: bạn có thể đặt hàng 12 chiếc cupcake, hương sô cô la và chọn một ngày trong tương lai để đến lấy hàng.
  2. Sau đó, hãy nhấn vào Cancel (Huỷ). Bạn nên quay lại StartFragment.
  3. Điều này trông có vẻ đúng, nhưng nếu nhấn vào nút Back (Quay lại) của hệ thống thì bạn sẽ trở lại màn hình tóm tắt đơn đặt hàng với tóm tắt đơn đặt hàng là 0 cupcake và không có hương vị. Điều này là không đúng và không được hiển thị cho người dùng.

1a9024cd58a0e643.png

Người dùng có lẽ không muốn xem lại luồng đơn đặt hàng. Ngoài ra, tất cả dữ liệu đơn đặt hàng trong mô hình hiển thị đã bị xoá nên thông tin này không còn hữu ích. Thay vào đó, việc nhấn vào nút Back (Quay lại) từ StartFragment sẽ khiến người dùng thoát khỏi ứng dụng Cupcake.

Cùng xem ngăn xếp lui hiện tại trông ra sao và cách khắc phục lỗi. Khi bạn tạo đơn thông qua màn hình tóm tắt đơn đặt hàng, từng điểm đến được đẩy vào ngăn xếp lui.

fc88100cdf1bdd1.png

Bạn đã huỷ đơn đặt hàng trên SummaryFragment. Khi bạn di chuyển qua thao tác từ SummaryFragment đến StartFragment, Android đã thêm một thực thể khác của StartFragment làm điểm đến mới trên ngăn xếp lui.

5616cb0028b63602.png

Đó là lý do tại sao khi bạn nhấn vào nút Back (Quay lại) từ StartFragment, ứng dụng lại hiển thị SummaryFragment (với thông tin đơn đặt hàng để trống).

Để khắc phục lỗi điều hướng này, tìm hiểu cách thành phần Điều hướng cho phép bạn kéo các điểm đến bổ sung ra khỏi ngăn xếp lui khi di chuyển bằng một thao tác.

Kéo các điểm đến khác ra khỏi ngăn xếp lui

Nếu bạn thêm thuộc tính app:popUpTo vào thao tác di chuyển trong sơ đồ điều hướng, nhiều hơn một điểm đến có thể được kéo ra khỏi ngăn xếp lui cho đến khi đạt tới điểm đến đã chỉ định. Nếu bạn chỉ định app:popUpTo="@id/startFragment", thì các điểm đến trong ngăn xếp lui sẽ được kéo ra cho đến khi bạn đạt đến StartFragment, điểm đến này vẫn nằm trong ngăn xếp.

Khi thêm thay đổi này vào mã và chạy ứng dụng, bạn sẽ thấy rằng khi huỷ một đơn đặt hàng, bạn sẽ quay lại StartFragment. Nhưng lần này, khi nhấn vào nút Back (Quay lại) từ StartFragment, bạn sẽ thấy lại StartFragment (thay vì thoát khỏi ứng dụng). Đây cũng không phải là hành vi được mong muốn. Như đề cập trước đó, vì bạn đang di chuyển đến StartFragment nên chính Android đã thêm StartFragment làm điểm đến mới ở ngăn xếp lui. Vì vậy, lúc này bạn có 2 thực thể của StartFragment trong ngăn xếp lui. Do đó, bạn cần nhấn vào nút Back (Quay lại) hai lần để thoát khỏi ứng dụng.

dd0fedc6e231e595.png

Để khắc phục lỗi mới này, đòi hỏi tất cả các điểm đến đều được kéo ra khỏi ngăn xếp lui cho đến khi gặp và bao gồm StartFragment. Thực hiện việc này bằng cách chỉ định app:popUpTo="@id/startFragment"

app:popUpToInclusive="true" trên các thao tác di chuyển thích hợp. Như vậy, bạn chỉ có một thực thể mới của StartFragment trong ngăn xếp lui. Tiếp đó, nhấn vào nút Back (Quay lại) một lần từ StartFragment để thoát khỏi ứng dụng. Hãy cùng thực hiện thay đổi này ngay bây giờ.

cf0e80b4907d80dd.png

Sửa đổi thao tác di chuyển

  1. Chuyển đến Navigation Editor (Trình chỉnh sửa điều hướng) bằng cách mở tệp res > navigation > nav_map.xml.
  2. Chọn thao tác đi từ summaryFragment đến startFragment, để thao tác này được đánh dấu bằng màu xanh dương.
  3. Mở rộng Thuộc tính ở bên phải (nếu còn chưa mở). Tìm Hành vi kéo ra trong danh sách các thuộc tính bạn có thể sửa đổi.

8c87589f9cc4d176.png

  1. Trong số các tuỳ chọn thả xuống, đặt popUpTo thành startFragment. Điều này có nghĩa tất cả điểm đến trong ngăn xếp lui sẽ được kéo ra cho đến startFragment (bắt đầu từ đầu ngăn xếp rồi di chuyển xuống dưới).

a9a17493ed6bc27f.png

  1. Sau đó, nhấp vào hộp đánh dấu popUpToInclusive cho đến khi hộp này hiển thị dấu kiểm và nhãn là true. Việc này có nghĩa là bạn muốn kéo ra các điểm đến cho đến khi và bao gồm cả thực thể của startFragment đã có trong ngăn xếp lui. Tiếp đó, sẽ không còn có hai thực thể của startFragment trong ngăn xếp lui.

4a403838a62ff487.png

  1. Lặp lại các thay đổi này cho thao tác kết nối pickupFragment với startFragment.

4a403838a62ff487.png

  1. Lặp lại cho thao tác kết nối flavorFragment với startFragment.
  2. Khi hoàn tất, xác nhận rằng ứng dụng của bạn đã được sửa đổi chính xác bằng cách xem xét chế độ xem của tệp sơ đồ điều hướng.
<navigation
    android:id="@+id/nav_graph" ...>
    <fragment
        android:id="@+id/startFragment" ...>
        ...
    </fragment>
    <fragment
        android:id="@+id/flavorFragment" ...>
        ...
        <action
            android:id="@+id/action_flavorFragment_to_startFragment"
            app:destination="@id/startFragment"
            app:popUpTo="@id/startFragment"
            app:popUpToInclusive="true" />
    </fragment>
    <fragment
        android:id="@+id/pickupFragment" ...>
        ...
        <action
            android:id="@+id/action_pickupFragment_to_startFragment"
            app:destination="@id/startFragment"
            app:popUpTo="@id/startFragment"
            app:popUpToInclusive="true" />
    </fragment>
    <fragment
        android:id="@+id/summaryFragment" ...>
        <action
            android:id="@+id/action_summaryFragment_to_startFragment"
            app:destination="@id/startFragment"
            app:popUpTo="@id/startFragment"
            app:popUpToInclusive="true" />
    </fragment>
</navigation>

Lưu ý rằng đối với từng thao tác trong 3 thao tác (action_flavorFragment_to_startFragment, action_pickupFragment_to_startFragmentaction_summaryFragment_to_startFragment), các thuộc tính mới app:popUpTo="@id/startFragment"app:popUpToInclusive="true" cần được thêm vào.

  1. Bây giờ, hãy chạy ứng dụng. Duyệt qua luồng đơn đặt hàng và nhấn vào Cancel (Huỷ). Khi quay lại StartFragment, nhấn vào nút Back (Quay lại) (chỉ một lần!) và bạn sẽ thoát khỏi ứng dụng.

Tóm lại, khi bạn huỷ đơn đặt hàng và quay lại màn hình đầu tiên của ứng dụng, tất cả điểm đến mảnh trong ngăn xếp lui đều được kéo ra, bao gồm cả phiên bản đầu tiên của StartFragment. Sau khi hoàn tất thao tác điều hướng, StartFragment đã được thêm vào làm điểm đến mới trên ngăn xếp lui. Việc nhấn vào Back (Quay lại) từ đây sẽ kéo StartFragment ra khỏi ngăn xếp, khiến cho không còn mảnh nào trong ngăn xếp lui. Do đó, Android sẽ hoàn tất hoạt động và người dùng rời khỏi ứng dụng.

Ứng dụng phải trông như thế này: 2e0599d9b55401f1.png

5. Gửi đơn đặt hàng

Ứng dụng của bạn trông rất tuyệt! Tuy nhiên, vẫn còn thiếu một phần. Khi bạn nhấn vào nút gửi đơn đặt hàng trên SummaryFragment, thông báo Toast vẫn còn bật lên.

90ed727c7b812fd6.png

Nếu có thể gửi đơn đặt hàng ra ngoài thì ứng dụng càng có ích cho người dùng. Hãy tận dụng những điều bạn đã học trong các lớp học lập trình trước đây về việc sử dụng ý định ngầm ẩn để chia sẻ thông tin với một ứng dụng khác, nhằm giúp người dùng có thể chia sẻ thông tin với ứng dụng email trên thiết bị và gửi đơn đặt hàng cho cửa hàng cupcake.

170d76b64ce78f56.png

Để triển khai tính năng này, hãy xem chủ đề và nội dung email có cấu trúc như thế nào trong ảnh chụp màn hình bên trên.

Bạn sẽ dùng những chuỗi này đã có sẵn trong tệp strings.xml.

<string name="new_cupcake_order">New Cupcake Order</string>
<string name="order_details">Quantity: %1$s cupcakes \n Flavor: %2$s \nPickup date: %3$s \n Total: %4$s \n\n Thank you!</string>

order_details là một tài nguyên chuỗi chứa 4 đối số định dạng khác nhau. Đây là phần giữ chỗ cho số lượng cupcake thực tế, hương vị mong muốn, ngày đến lấy hàng mong muốn và tổng giá. Các đối số được đánh số từ 1 đến 4 theo cú pháp %1 đến %4. Loại đối số cũng được chỉ định ($s có nghĩa là một chuỗi được mong đợi tại đây).

Trong mã Kotlin, bạn có thể gọi getString() trên R.string.order_details, theo sau là 4 đối số (đặt hàng rất quan trọng!). Ví dụ: gọi getString(R.string.order_details, "12", "Chocolate", "Sat Dec 12", "$24.00") sẽ tạo chuỗi sau đây, chính xác là nội dung email bạn muốn.

Quantity: 12 cupcakes
Flavor: Chocolate
Pickup date: Sat Dec 12
Total: $24.00

Thank you!
  1. Trong SummaryFragment.kt, sửa đổi phương thức sendOrder(). Xoá thông báo Toast hiện có.
fun sendOrder() {

}
  1. Trong phương thức sendOrder(), tạo văn bản tóm tắt đơn đặt hàng. Tạo chuỗi order_details được định dạng bằng cách lấy số lượng đặt hàng, hương vị, ngày và giá từ mô hình hiển thị được chia sẻ.
val orderSummary = getString(
    R.string.order_details,
    sharedViewModel.quantity.value.toString(),
    sharedViewModel.flavor.value.toString(),
    sharedViewModel.date.value.toString(),
    sharedViewModel.price.value.toString()
)
  1. Vẫn trong phương thức sendOrder(), tạo một ý định ngầm ẩn để chia sẻ đơn đặt hàng với một ứng dụng khác. Xem tài liệu để biết cách tạo ý định gửi email. Chỉ định Intent.ACTION_SEND cho thao tác theo ý định, đặt loại cho"text/plain" và bao gồm phần bổ sung ý định cho chủ đề email (Intent.EXTRA_SUBJECT) và nội dung email (Intent.EXTRA_TEXT). Nhập android.content.Intent nếu cần.
val intent = Intent(Intent.ACTION_SEND)
    .setType("text/plain")
    .putExtra(Intent.EXTRA_SUBJECT, getString(R.string.new_cupcake_order))
    .putExtra(Intent.EXTRA_TEXT, orderSummary)

Mẹo hay, nếu áp dụng ứng dụng này vào trường hợp sử dụng của riêng mình, bạn có thể điền sẵn người nhận email là địa chỉ email của cửa hàng cupcake. Trong ý định, bạn chỉ định người nhận email bằng ý định bổ sung Intent.EXTRA_EMAIL.

  1. Do đây là ý định ngầm ẩn, nên bạn không cần phải biết trước thành phần hoặc ứng dụng cụ thể nào sẽ xử lý ý định này. Người dùng sẽ quyết định họ muốn sử dụng ứng dụng nào để thực hiện ý định. Tuy nhiên, trước khi khởi chạy một hoạt động với ý định này, kiểm tra xem liệu có ứng dụng nào có thể xử lý ý định hay không. Bước kiểm tra này ngăn không cho ứng dụng Cupcake gặp sự cố nếu không có ứng dụng nào để xử lý ý định, nhờ đó khiến mã của bạn an toàn hơn.
if (activity?.packageManager?.resolveActivity(intent, 0) != null) {
    startActivity(intent)
}

Thực hiện kiểm tra bằng cách truy cập vào PackageManager. Trình này chứa thông tin về gói ứng dụng nào được cài đặt trên thiết bị. PackageManager có thể được truy cập qua activity của mảnh, chừng nào activitypackageManager còn không rỗng. Gọi phương thức resolveActivity() của PackageManager với ý định mà bạn đã tạo. Nếu kết quả không rỗng, thì có thể an toàn gọi startActivity() cùng ý định của bạn.

  1. Chạy ứng dụng để kiểm thử mã của bạn. Tạo một đơn đặt hàng cupcake và nhấn vào Send Order to Another App (Gửi đơn đặt hàng đến một ứng dụng khác). Khi hộp thoại chia sẻ bật lên, bạn có thể chọn ứng dụng Gmail hoặc một ứng dụng khác nếu muốn. Nếu chọn ứng dụng Gmail, bạn có thể phải thiết lập một tài khoản trên thiết bị nếu vẫn chưa có (ví dụ: nếu bạn đang sử dụng trình mô phỏng). Nếu đơn đặt hàng cupcake mới nhất không xuất hiện trong nội dung email, trước tiên, bạn cần huỷ bản nháp email hiện tại.

170d76b64ce78f56.png

Khi kiểm thử các tình huống, bạn có thể thấy lỗi nếu chỉ có 1 chiếc cupcake. Tóm tắt đơn đặt hàng cho thấy 1 cupcakes(nhiều cupcake), nhưng trong tiếng Anh, đây là lỗi ngữ pháp.

ef046a100381bb07.png

Đáng ra, bạn phải thấy 1 cupcake (một cupcake) (không phải số nhiều). Nếu muốn dùng từ cupcake hoặc nhiều cupcake dựa trên giá trị số lượng thì bạn có thể sử dụng chuỗi có tên là chuỗi số lượng trong Android. Bằng cách khai báo tài nguyên plurals, bạn có thể chỉ định các tài nguyên chuỗi để sử dụng dựa trên số lượng, ví dụ như trong trường hợp số ít hoặc số nhiều.

  1. Thêm tài nguyên số nhiều cupcakes trong tệp strings.xml.
<plurals name="cupcakes">
    <item quantity="one">%d cupcake</item>
    <item quantity="other">%d cupcakes</item>
</plurals>

Trong trường hợp số ít (quantity="one"), chuỗi số ít sẽ được sử dụng. Trong mọi các trường hợp khác (quantity="other"), chuỗi số nhiều sẽ được sử dụng. Lưu ý rằng thay vì %s, đòi hỏi một đối số chuỗi, thì %d đòi hỏi một đối số nguyên, được chuyển vào khi bạn định dạng chuỗi.

Trong mã Kotlin của bạn, gọi:

getQuantityString(R.plurals.cupcakes, 1, 1) sẽ trả về chuỗi 1 cupcake

getQuantityString(R.plurals.cupcakes, 6, 6) sẽ trả về chuỗi 6 cupcakes

getQuantityString(R.plurals.cupcakes, 0, 0) sẽ trả về chuỗi 0 cupcakes

  1. Trước khi chuyển đến mã Kotlin, cập nhật tài nguyên chuỗi order_details trong strings.xml để phiên bản số nhiều của cupcake không còn được mã cứng vào đó nữa.
<string name="order_details">Quantity: %1$s \n Flavor: %2$s \nPickup date: %3$s \n
        Total: %4$s \n\n Thank you!</string>
  1. Trong lớp SummaryFragment, hãy cập nhật phương thức sendOrder() để sử dụng chuỗi số lượng mới. Cách dễ nhất là trước hết hình dung ra số lượng từ mô hình hiển thị và lưu trữ số lượng đó trong một biến số. Do quantity trong mô hình hiển thị thuộc loại LiveData<Int> nên có thể sharedViewModel.quantity.value là rỗng. Nếu mã này là rỗng, dùng 0 làm giá trị mặc định cho numberOfCupcakes.

Thêm mã này làm dòng mã đầu tiên trong phương thức sendOrder().

val numberOfCupcakes = sharedViewModel.quantity.value ?: 0

Toán tử elvis (?:) có nghĩa là nếu biểu thức bên trái không rỗng, thì hãy sử dụng biểu thức đó. Ngược lại, nếu biểu thức ở bên trái là rỗng, sử dụng biểu thức bên phải toán tử elvis (là 0 trong trường hợp này).

  1. Sau đó, định dạng chuỗi order_details như đã thực hiện trước đó. Thay vì truyền trực tiếp numberOfCupcakes làm đối số số lượng, hãy tạo chuỗi cupcake được định dạng bằng resources.getQuantityString(R.plurals.cupcakes, numberOfCupcakes, numberOfCupcakes).

Phương thức sendOrder() đầy đủ sẽ có dạng như sau:

fun sendOrder() {
    val numberOfCupcakes = sharedViewModel.quantity.value ?: 0
    val orderSummary = getString(
        R.string.order_details,
        resources.getQuantityString(R.plurals.cupcakes, numberOfCupcakes, numberOfCupcakes),
        sharedViewModel.flavor.value.toString(),
        sharedViewModel.date.value.toString(),
        sharedViewModel.price.value.toString()
    )

    val intent = Intent(Intent.ACTION_SEND)
        .setType("text/plain")
        .putExtra(Intent.EXTRA_SUBJECT, getString(R.string.new_cupcake_order))
        .putExtra(Intent.EXTRA_TEXT, orderSummary)

    if (activity?.packageManager?.resolveActivity(intent, 0) != null) {
        startActivity(intent)
    }
}
  1. Chạy và kiểm thử mã của bạn. Kiểm tra xem tóm tắt đơn đặt hàng trong nội dung email có hiển thị 1 cupcake so với 6 cupcake hoặc 12 cupcake.

Đến đây bạn đã hoàn thành tất cả chức năng của ứng dụng Cupcake! Xin chúc mừng!! Đây chắc chắn là một ứng dụng đầy thách thức và bạn đã có tiến bộ vượt bậc trong hành trình trở thành nhà phát triển của Android! Bạn có thể kết hợp thành công tất cả các khái niệm đã học được từ trước đến nay, đồng thời chọn một số mẹo giải quyết vấn đề mới trong hành trình này.

Các bước cuối cùng

Bây giờ, dành chút thời gian để dọn dẹp mã của bạn. Đây là một phương pháp lập trình tốt bạn đã học được từ các lớp học lập trình trước đó.

  • Tối ưu hoá lệnh nhập
  • Định dạng lại tệp
  • Xoá mã không dùng đến hoặc bị nhận xét
  • Thêm nhận xét vào mã khi cần

Để ứng dụng của bạn dễ tiếp cận hơn, kiểm thử ứng dụng của bạn sau khi bật Talkback để đảm bảo trải nghiệm người dùng mượt mà. Phản hồi bằng giọng nói phải giúp truyền tải mục đích của từng thành phần trên màn hình, khi thích hợp. Ngoài ra, đảm bảo rằng bạn có thể thao tác trên tất cả các thành phần của ứng dụng bằng các cử chỉ vuốt.

Kiểm tra kỹ xem những trường hợp sử dụng đã được triển khai có hoạt động đúng như dự kiến trong ứng dụng cuối cùng của bạn hay không. Ví dụ:

  • Dữ liệu phải được bảo tồn khi xoay thiết bị (nhờ mô hình hiển thị).
  • Nếu bạn nhấn vào nút Up (Lên) hoặc Back (Quay lại), thì thông tin đơn đặt hàng vẫn hiển thị chính xác trên FlavorFragmentPickupFragment.
  • Việc gửi đơn đặt hàng đến ứng dụng khác phải chia sẻ thông tin chi tiết chính xác về đơn đặt hàng.
  • Việc huỷ đơn đặt hàng phải xoá tất cả thông tin trong đơn đặt hàng đó.

Nếu bạn phát hiện lỗi, hãy tiếp tục và sửa lỗi.

Rất tốt vì đã kiểm tra kỹ!

6. Mã giải pháp

Mã giải pháp cho lớp học lập trình này nằm trong dự án dưới đây.

Để lấy mã cho lớp học lập trình này và mở trong Android Studio, hãy thực hiện các bước sau.

Lấy mã

  1. Nhấp vào URL được cung cấp. Thao tác này sẽ mở trang GitHub của dự án trong một trình duyệt.
  2. Trên trang GitHub của dự án, hãy nhấp vào nút Code (Mã), một hộp thoại sẽ xuất hiện.

5b0a76c50478a73f.png

  1. Trong hộp thoại này, hãy nhấp vào nút Download ZIP (Tải tệp ZIP xuống) để lưu dự án vào máy tính. Chờ quá trình tải xuống hoàn tất.
  2. Xác định vị trí của tệp trên máy tính (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  3. Nhấp đúp vào tệp ZIP để giải nén. Thao tác này sẽ tạo một thư mục mới chứa các tệp dự án.

Mở dự án trong Android Studio

  1. Khởi động Android Studio.
  2. Trong cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy nhấp vào Open an existing Android Studio project (Mở một dự án hiện có trong Android Studio).

36cc44fcf0f89a1d.png

Lưu ý: Nếu Android Studio đã mở sẵn thì thay vào đó, hãy chọn tuỳ chọn sau đây trong trình đơn File > New > Import Project (Tệp > Mới > Nhập dự án).

21f3eec988dcfbe9.png

  1. Trong hộp thoại Import Project (Nhập dự án), hãy chuyển đến nơi chứa thư mục dự án đã giải nén (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  2. Nhấp đúp vào thư mục dự án đó.
  3. Chờ Android Studio mở dự án.
  4. Nhấp vào nút Run (Chạy) 11c34fc5e516fb1c.png để xây dựng và chạy ứng dụng. Hãy đảm bảo ứng dụng được dựng như mong đợi.
  5. Duyệt qua các tệp dự án trong cửa sổ công cụ Project (Dự án) để xem cách ứng dụng được thiết lập.

7. Tóm tắt

  • Android lưu giữ một ngăn xếp lui chứa mọi đích đến đã được bạn truy cập, trong đó mỗi đích đến mới sẽ được đẩy lên đầu ngăn xếp.
  • Khi nhấn vào nút Up (Lên) hoặc Back (Quay lại), bạn có thể kéo các điểm đến ra khỏi ngăn xếp lui.
  • Sử dụng thành phần Điều hướng Jetpack sẽ giúp bạn đẩy và kéo các điểm đến phân mảnh ra khỏi ngăn xếp lui để hành vi mặc định của nút Back (Quay lại) trở thành miễn phí.
  • Chỉ định thuộc tính app:popUpTo cho một thao tác trong sơ đồ điều hướng để kéo điểm đến ra khỏi ngăn xếp lui cho đến khi nhìn thấy điểm đến được chỉ định trong giá trị thuộc tính.
  • Chỉ định app:popUpToInclusive="true" cho một thao tác khi điểm đến được chỉ định trong app:popUpTo cũng phải được kéo ra khỏi ngăn xếp lui.
  • Bạn có thể tạo ý định ngầm ẩn để chia sẻ nội dung với một ứng dụng email, bằng cách sử dụng Intent.ACTION_SEND và điền sẵn các ý định khác như Intent.EXTRA_EMAIL, Intent.EXTRA_SUBJECTIntent.EXTRA_TEXT, v.v.
  • Sử dụng tài nguyên plurals nếu bạn muốn sử dụng các tài nguyên chuỗi dựa trên số lượng, chẳng hạn như trường hợp số ít hoặc số nhiều.

8. Tìm hiểu thêm

9. Tự thực hành

Mở rộng ứng dụng Cupcake với các biến thể của riêng bạn trong luồng đơn đặt hàng cupcake. Ví dụ:

  • Bổ sung một hương vị đặc biệt với một số điều kiện đặc biệt khác, chẳng hạn như không có bánh để đến lấy ngay trong ngày.
  • Yêu cầu người dùng điền tên của họ vào đơn đặt hàng cupcake.
  • Cho phép người dùng chọn nhiều hương vị cupcake cho đơn đặt hàng của họ nếu số lượng nhiều hơn 1 bánh.

Bạn cần cập nhật phần nào trong ứng dụng để phù hợp với chức năng mới này?

Kiểm tra thành phẩm của bạn:

Sau khi hoàn thiện, ứng dụng của bạn phải không gặp lỗi nào khi chạy.

10. Nhiệm vụ thử thách

Áp dụng những điều bạn đã học từ việc xây dựng ứng dụng Cupcake để tạo một ứng dụng cho trường hợp sử dụng của riêng mình. Đó có thể là một ứng dụng để đặt pizza, bánh sandwich hoặc bất cứ thứ gì khác mà bạn có thể nghĩ đến! Bạn nên phác thảo các đích đến khác nhau trong ứng dụng trước khi bắt đầu triển khai.

Để tìm cảm hứng từ các ý tưởng thiết kế khác, bạn cũng có thể tham khảo ứng dụng Shrine. Ứng dụng này là một ví dụ về cách áp dụng các chủ đề và thành phần của Material cho thương hiệu. Ứng dụng Shrine phức tạp hơn nhiều so với ứng dụng Cupcake đã được bạn xây dựng, do vậy thay vì nhắm đến xây dựng một ứng dụng rất khó, trước hết hãy nghĩ về các tính năng nhỏ mà bạn có thể giải quyết. Xây dựng niềm tin của bạn theo thời gian bằng những chiến thắng nho nhỏ.

Sau khi bạn hoàn tất ứng dụng riêng của mình, hãy chia sẻ những gì bạn đã xây dựng trên mạng xã hội. Dùng hashtag #LearningKotlin để chúng ta xem ứng dụng đó!