Thêm danh sách có thể cuộn

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

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách tạo danh sách có thể cuộn trong ứng dụng của mình bằng Jetpack Compose.

Bạn sẽ làm việc với ứng dụng Affirmations (Lời động viên), sử dụng một danh sách lời động viên kết hợp với hình ảnh đẹp để mang lại năng lượng tích cực cho ngày của bạn!

Dữ liệu đã có sẵn ở đó, bạn chỉ cần lấy và hiển thị những dữ liệu này lên giao diện người dùng.

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

  • Có kiến thức cơ bản về List (danh sách) trong Kotlin
  • Trải nghiệm xây dựng bố cục bằng Jetpack Compose
  • Có kinh nghiệm chạy các ứng dụng trên một thiết bị hoặc trình mô phỏng

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

  • Cách tạo thẻ Material Design bằng Jetpack Compose
  • Cách tạo danh sách có thể cuộn bằng Jetpack Compose

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

  • Bạn sẽ dùng một ứng dụng hiện có và thêm một danh sách có thể cuộn vào giao diện người dùng

Thành phẩm sẽ có dạng như sau:

f6f09800b74f4700.png

Bạn cần có

  • Một máy tính có quyền truy cập Internet, trình duyệt web và Android Studio
  • Quyền truy cập vào GitHub

Tải mã khởi đầu xuống

Trong Android Studio, hãy mở thư mục basic-android-kotlin-compose-training-affirmations.

  1. Chuyển đến trang kho lưu trữ GitHub được cung cấp cho dự án.
  2. Xác minh rằng tên nhánh khớp với tên nhánh được chỉ định trong lớp học lập trình. Ví dụ: trong ảnh chụp màn hình sau đây, tên nhánh là main.

1e4c0d2c081a8fd2.png

  1. Trên trang GitHub cho dự án này, nhấp vào nút Code (Mã). Thao tác này sẽ khiến một cửa sổ bật lên.

1debcf330fd04c7b.png

  1. Trong cửa sổ bật lên, 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 (Mở).

d8e9dbdeafe9038a.png

Lưu ý: Nếu Android Studio đã mở thì chuyển sang chọn tuỳ chọn File (Tệp) > Open (Mở) trong trình đơn.

8d1fda7396afe8e5.png

  1. Trong trình duyệt tệp, hãy chuyển đến vị trí của thư mục dự án chưa 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) 8de56cba7583251f.png để tạo và chạy ứng dụng. Hãy đảm bảo ứng dụng được tạo như mong đợi.

2. Tạo lớp dữ liệu cho mục trong danh sách

Tạo lớp dữ liệu cho một Affirmation (Lời động viên)

Trong ứng dụng Android, danh sách bao gồm các mục trong danh sách. Đối với các phần dữ liệu đơn lẻ, dữ liệu này có thể đơn giản như một chuỗi hoặc một số nguyên. Đối với những mục trong danh sách có nhiều phần dữ liệu, chẳng hạn như hình ảnh và văn bản, bạn sẽ cần một lớp chứa tất cả các thuộc tính này. Lớp dữ liệu là một loại lớp chỉ chứa các thuộc tính, có thể cung cấp một số phương thức hữu ích để xử lý những thuộc tính đó.

  1. Tạo một gói mới nằm trong com.example.affirmations. 4a51cb670bbec405.png

Đặt tên cho gói mới này là model. Gói model sẽ chứa mô hình dữ liệu được biểu diễn thông qua một lớp dữ liệu. Lớp dữ liệu đó sẽ chứa các thuộc tính biểu thị thông tin liên quan đến một "Affirmation" (Lời động viên), bao gồm cả tài nguyên chuỗi và tài nguyên hình ảnh. Gói (package) là các thư mục chứa các lớp và thậm chí là các thư mục khác.

9ea1f8880ca90ea0.png

  1. Tạo một lớp mới trong gói com.example.affirmations.model. a9515a8b47715a22.png

Đặt tên cho lớp mới là Affirmation (Lời động viên) và đặt lớp đó là Data Class (Lớp dữ liệu).

b36be7f428fd1672.png

  1. Mỗi Affirmation bao gồm một hình ảnh và một chuỗi. Tạo hai thuộc tính val trong lớp dữ liệu Affirmation. Một thuộc tính đặt tên là stringResourceId và thuộc tính còn lại là imageResourceId. Cả hai thuộc tính này đều phải là số nguyên.

Affirmation.kt

data class Affirmation(
   val stringResourceId: Int,
   val imageResourceId: Int
)
  1. Gắn thẻ thuộc tính stringResourceId bằng chú giải @StringRes và gắn thẻ imageResourceId bằng @DrawableRes. stringResourceId biểu thị mã nhận dạng của văn bản lời động viên được lưu trữ trong một tài nguyên chuỗi. imageResourceId biểu thị mã nhận dạng của hình ảnh minh hoạ lời động viên được lưu trữ trong một tài nguyên drawable.

Affirmation.kt

data class Affirmation(
   @StringRes val stringResourceId: Int,
   @DrawableRes val imageResourceId: Int
)
  1. Bây giờ, hãy mở tệp Datasource.kt trong gói com.example.affirmations.data rồi bỏ ghi chú nội dung của lớp Datasource.

Datasource.kt

class Datasource() {
   fun loadAffirmations(): List<Affirmation> {
       return listOf<Affirmation>(
           Affirmation(R.string.affirmation1, R.drawable.image1),
           Affirmation(R.string.affirmation2, R.drawable.image2),
           Affirmation(R.string.affirmation3, R.drawable.image3),
           Affirmation(R.string.affirmation4, R.drawable.image4),
           Affirmation(R.string.affirmation5, R.drawable.image5),
           Affirmation(R.string.affirmation6, R.drawable.image6),
           Affirmation(R.string.affirmation7, R.drawable.image7),
           Affirmation(R.string.affirmation8, R.drawable.image8),
           Affirmation(R.string.affirmation9, R.drawable.image9),
           Affirmation(R.string.affirmation10, R.drawable.image10))
   }
}

3. Thêm danh sách vào ứng dụng

Tạo thẻ cho mục trong danh sách

Ứng dụng này nhằm hiển thị danh sách lời động viên. Bước đầu tiên trong việc định cấu hình giao diện người dùng để hiển thị danh sách là tạo một mục trong danh sách. Mỗi mục lời động viên bao gồm một hình ảnh và một chuỗi. Dữ liệu cho từng mục này đi kèm với mã khởi đầu và bạn sẽ tạo thành phần giao diện người dùng để hiển thị mục đó.

Mục này sẽ bao gồm một hàm composable Card, chứa một composable Image và một composable Text. Trong Compose, Card là một bề mặt hiển thị nội dung và các hành động trong một vùng chứa duy nhất. Thẻ Affirmation (Lời động viên) sẽ có dạng như sau:

95111184aed54fa3.png

Thẻ hiển thị một hình ảnh cùng với một đoạn văn bản bên dưới. Bạn có thể tạo được bố cục dọc này bằng cách dùng một hàm composable Column được bọc trong một hàm composable Card. Bạn có thể tự mình thử hoặc làm theo các bước dưới đây để tạo được bố cục này.

  1. Mở tệp MainActivity.kt.

1e348baaf91552f4.png

  1. Tạo một phương thức mới bên dưới phương thức AffirmationApp() có tên là AffirmationCard() và gắn chú giải @Composable cho nó.

MainActivity.kt

@Composable
fun AffirmationApp() {
   val context = LocalContext.current
   AffirmationsTheme {
    
   }
}

@Composable
fun AffirmationCard() {
  
}
  1. Chỉnh sửa chữ ký của phương thức để nhận một đối tượng Affirmation làm tham số. Đối tượng Affirmation này được lấy từ gói model.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation) {
  
}
  1. Thêm một tham số modifier vào chữ ký. Đặt giá trị mặc định là Modifier cho tham số này.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
  
}
  1. Bên trong phương thức AffirmationCard, hãy gọi hàm composable Card. Truyền vào các tham số sau: modifierelevation. Truyền một đối tượng Modifier có thuộc tính padding được đặt là 8.dp cho tham số modifier. Truyền giá trị 4.dp cho elevation. Thuộc tính elevation sẽ được đề cập chi tiết hơn ở phần sau.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {Card(modifier = modifier.padding(8.dp), elevation = 4.dp) {
  }
}
  1. Thêm một hàm composable Column vào bên trong hàm composable Card. Các thành phần bên trong composable Column sẽ tự động sắp xếp theo chiều dọc trên giao diện người dùng. Điều này cho phép bạn đặt hình ảnh lên phía trên đoạn văn bản tương ứng. Ngược lại, hàm composable Row sẽ sắp xếp các thành phần bên trong nó theo chiều ngang.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
  Card(modifier = modifier.padding(8.dp), elevation = 4.dp) {
    Column {
    }
  }
}
  1. Thêm một hàm composable Image vào bên trong phần thân lambda của hàm composable Column. Hãy nhớ rằng hàm composable Image luôn yêu cầu hiển thị tài nguyên và contentDescription. Tài nguyên phải là painterResource được truyền vào tham số painter. Phương thức painterResource sẽ tải các drawable vector hoặc các định dạng ảnh raster như PNG. Ngoài ra, hãy truyền stringResource cho tham số contentDescription.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
  Card(modifier = Modifier.padding(8.dp), elevation = 4.dp) {
    Column {
      Image(
        painter = painterResource(affirmation.imageResourceId),
        contentDescription = stringResource(affirmation.stringResourceId)
      )
    }
  }
}
  1. Ngoài các tham số paintercontentDescription, hãy truyền thêm một modifier và một contentScale. contentScale xác định cách hình ảnh hiển thị và thay đổi kích thước theo tỷ lệ. Thiết lập thuộc tính fillMaxWidth và chiều cao 194.dp cho đối tượng Modifier. contentScale nên được đặt là ContentScale.Crop.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
  Card(modifier = Modifier.padding(8.dp), elevation = 4.dp) {
    Column {
      Image(
        painter = painterResource(affirmation.imageResourceId),
        contentDescription = stringResource(affirmation.stringResourceId),
        modifier = Modifier
          .fillMaxWidth()
          .height(194.dp),
        contentScale = ContentScale.Crop
      )
    }
  }
}
  1. Bên trong Column, hãy tạo một hàm composable Text sau hàm composable Image. Truyền stringResource của affirmation.stringResourceId cho tham số text, truyền đối tượng Modifier với thuộc tính padding được đặt là 16.dp và đặt giao diện văn bản bằng cách truyền MaterialTheme.typography.h6 vào tham số style.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
  Card(modifier = Modifier.padding(8.dp), elevation = 4.dp) {
    Column {
      Image(
        painter = painterResource(affirmation.imageResourceId),
        contentDescription = stringResource(affirmation.stringResourceId),
        modifier = Modifier
          .fillMaxWidth()
          .height(194.dp),
        contentScale = ContentScale.Crop
      )
      Text(
        text = stringResource(affirmation.stringResourceId),
        modifier = Modifier.padding(16.dp),
        style = MaterialTheme.typography.h6
      )
    }
  }
}

Xem trước hàm composable AffirmationCard

Thẻ này là thành phần cốt lõi của giao diện người dùng cho ứng dụng Affirmations và bạn đã nỗ lực tạo ra thẻ đó! Để kiểm tra xem thẻ hiển thị có chính xác hay không, bạn có thể tạo một hàm composable có thể xem trước mà không cần khởi chạy toàn bộ ứng dụng.

  1. Tạo một phương thức private có tên là AffirmationCardPreview(). Gắn chú giải @Preview@Composable cho phương thức này.

MainActivity.kt

@Preview 
@Composable
private fun AffirmationCardPreview() {
}
  1. Bên trong phương thức, hãy gọi hàm composable AffirmationCard và truyền cho nó một đối tượng Affirmation mới, đồng thời truyền tài nguyên chuỗi R.string.affirmation1 và tài nguyên drawable R.drawable.image1 vào hàm khởi tạo của đối tượng đó.

MainActivity.kt

@Preview 
@Composable
private fun AffirmationCardPreview() {
  AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
  1. Mở thẻ Split (Chia màn hình) và bạn sẽ thấy bản xem trước của AffirmationCard. Nếu cần, hãy nhấp vào Build & Refresh (Tạo và làm mới) trong ngăn Design (Thiết kế) để hiển thị bản xem trước. 84904da4a33413ce.png

Tạo danh sách

Thành phần mục trong danh sách là yếu tố nền tảng của danh sách. Sau khi tạo xong mục trong danh sách, bạn có thể tận dụng mục đó để tạo thành phần danh sách.

  1. Tạo một phương thức có tên là AffirmationList(), gắn chú giải @Composable cho nó và khai báo một List chứa các đối tượng Affirmation làm tham số trong chữ ký phương thức.

MainActivity.kt

@Composable
private fun AffirmationList(affirmationList: List<Affirmation>) {
}
  1. Khai báo một đối tượng modifier làm tham số trong chữ ký phương thức với giá trị mặc định là Modifier.

MainActivity.kt

@Composable
private fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
}
  1. Trong Jetpack Compose, bạn có thể tạo một danh sách có thể cuộn bằng cách sử dụng hàm composable LazyColumn. Sự khác biệt giữa LazyColumnColumnColumn nên được dùng khi bạn có ít các mục để hiển thị, vì Compose sẽ tải tất cả các mục này cùng một lúc. Column chỉ có thể chứa một số lượng các hàm composable cố định hoặc được xác định trước. LazyColumn có thể thêm nội dung theo yêu cầu, phù hợp với danh sách dài, đặc biệt khi không xác định được độ dài của danh sách. Theo mặc định, LazyColumn cũng cung cấp chức năng cuộn mà không cần thêm mã. Khai báo một hàm composable LazyColumn bên trong phương thức AffirmationList().

MainActivity.kt

@Composable
private fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
  LazyColumn {
  }
}
  1. Trong phần thân lambda của LazyColumn, hãy gọi phương thức items() và truyền vào affirmationList. Phương thức items() là cách bạn thêm các mục vào LazyColumn. Phương thức này hơi đặc thù với composable này và thường không phổ biến đối với hầu hết các hàm composable khác.

MainActivity.kt

@Composable
private fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
  LazyColumn {
    items(affirmationList){
    }
  }
}
  1. Việc gọi phương thức items() yêu cầu một hàm lambda. Trong hàm đó, hãy chỉ định tham số affirmation đại diện cho một mục lời động viên từ affirmationList.

MainActivity.kt

@Composable
private fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
  LazyColumn {
    items(affirmationList){ affirmation ->
    }
  }
}
  1. Đối với mỗi lời động viên trong danh sách, hãy gọi hàm composable AffirmationCard() và truyền affirmation đó vào.

MainActivity.kt

@Composable
private fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
  LazyColumn {
    items(affirmationList){ affirmation ->
      AffirmationCard(affirmation)
    }
  }
}

Hiển thị danh sách

  1. Trong lambda, hãy gọi hàm composable AffirmationList và truyền DataSource().loadAffirmations() cho tham số affirmationList.

MainActivity.kt

@Composable
fun AffirmationApp() {
   AffirmationsTheme {
       Scaffold(
           content = {
               AffirmationList(affirmationList = Datasource().loadAffirmations())
           }
       )
   }
}

Chạy ứng dụng Affirmations trên thiết bị hoặc trình mô phỏng và xem thành phẩm!

f6f09800b74f4700.png

4. Lấy mã giải pháp

Nếu bạn muốn xem mã giải pháp, hãy xem mã trên GitHub.

  1. Chuyển đến trang kho lưu trữ GitHub được cung cấp cho dự án.
  2. Xác minh rằng tên nhánh khớp với tên nhánh được chỉ định trong lớp học lập trình. Ví dụ: trong ảnh chụp màn hình sau đây, tên nhánh là main.

1e4c0d2c081a8fd2.png

  1. Trên trang GitHub cho dự án này, nhấp vào nút Code (Mã). Thao tác này sẽ khiến một cửa sổ bật lên.

1debcf330fd04c7b.png

  1. Trong cửa sổ bật lên, 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 (Mở).

d8e9dbdeafe9038a.png

Lưu ý: Nếu Android Studio đã mở thì chuyển sang chọn tuỳ chọn File (Tệp) > Open (Mở) trong trình đơn.

8d1fda7396afe8e5.png

  1. Trong trình duyệt tệp, hãy chuyển đến vị trí của thư mục dự án chưa 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) 8de56cba7583251f.png để tạo và chạy ứng dụng. Hãy đảm bảo ứng dụng được tạo như mong đợi.

5. Kết luận

Giờ đây, bạn đã biết cách tạo thẻ, mục trong danh sách và danh sách có thể cuộn bằng Jetpack Compose! Xin lưu ý rằng đây chỉ là những công cụ cơ bản để tạo danh sách. Bạn có thể thoả sức sáng tạo và tuỳ chỉnh các mục trong danh sách theo ý muốn!

Tóm tắt

  • Sử dụng các hàm composable Card để tạo mục trong danh sách.
  • Sửa đổi giao diện người dùng nằm trong một hàm composable Card.
  • Tạo một danh sách có thể cuộn bằng cách sử dụng hàm composable LazyColumn.
  • Tạo danh sách bằng cách sử dụng các mục trong danh sách tuỳ chỉnh.