Android Kotlin Fundamentals 01.3: Tài nguyên hình ảnh và khả năng tương thích

Lớp học lập trình này nằm trong khóa học về Khái niệm cơ bản về Android Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tất cả các lớp học lập trình trong khóa học đều có trên trang đích của các lớp học lập trình cơ bản về Android Kotlin.

Giới thiệu

Trong lớp học lập trình này, bạn cải thiện ứng dụng DiceScroller từ lớp học lập trình cuối cùng và tìm hiểu cách thêm và sử dụng tài nguyên hình ảnh trong ứng dụng. Bạn cũng có thể tìm hiểu về khả năng tương thích của ứng dụng với các phiên bản Android khác nhau và cách Android Jetpack có thể giúp ích.

Những điều bạn nên biết

  • Cách tạo dự án ứng dụng mới và chạy ứng dụng trên trình mô phỏng hoặc thiết bị thực.
  • Các thành phần cơ bản của dự án ứng dụng, bao gồm thư mục tài nguyên (res) và tệp bản dựng Gradle.
  • Cách chỉnh sửa tệp bố cục của ứng dụng.
  • Cách tìm và sửa đổi các đối tượng chế độ xem trong mã của ứng dụng.

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

  • Cách thêm tệp vào tài nguyên của ứng dụng.
  • Cách sử dụng hình ảnh trong bố cục của ứng dụng.
  • Cách tìm chế độ xem hiệu quả hơn trong mã của ứng dụng.
  • Cách sử dụng hình ảnh giữ chỗ trong thiết kế ứng dụng của bạn bằng vùng chứa tên XML.
  • Giới thiệu về các cấp độ API Android cho ứng dụng của bạn và cách hiểu các cấp độ API tối thiểu, được nhắm mục tiêu và đã biên dịch.
  • Cách sử dụng các thư viện Jetpack trong ứng dụng của bạn để hỗ trợ các phiên bản Android cũ.

Những việc bạn sẽ làm

  • Sửa đổi ứng dụng DiceScroller từ lớp học lập trình cuối cùng để đưa hình ảnh vào giá trị xúc xắc, thay vì một số.
  • Thêm tệp hình ảnh vào tài nguyên của ứng dụng.
  • Cập nhật bố cục và mã của ứng dụng để dùng hình ảnh cho giá trị xúc xắc thay vì số.
  • Cập nhật mã của bạn để tìm chế độ xem hiệu quả hơn.
  • Hãy cập nhật mã của bạn để sử dụng hình ảnh trống khi ứng dụng khởi động.
  • Cập nhật ứng dụng của bạn để sử dụng thư viện Android Jetpack để tương thích ngược với các phiên bản Android cũ.

Trong lớp học lập trình này, bạn xây dựng trên ứng dụng DiceScroller mà bạn đã bắt đầu trong lớp học lập trình trước đó và bạn thêm những hình ảnh xúc xắc thay đổi khi xúc xắc được cuộn. Ứng dụng DiceScroller cuối cùng trông giống như sau:

Nếu không làm việc qua lớp học lập trình cuối cùng, bạn có thể tải ứng dụng mới xuống tại đây: DiceScroller.

Vào cuối lớp học lập trình cuối cùng, bạn có một ứng dụng cập nhật một chế độ xem văn bản bằng một số từ 1 đến 6 mỗi khi người dùng nhấn vào một nút. Tuy nhiên, ứng dụng có tên DiceScroller chứ không phải Trình tạo số 1-6, vì vậy sẽ tốt hơn nếu viên xúc xắc thực sự giống như xúc xắc. Trong nhiệm vụ này, bạn thêm một số hình ảnh xúc xắc vào ứng dụng của mình. Sau đó, thay vì cập nhật văn bản khi nhấn nút, bạn sẽ hoán đổi hình ảnh khác cho mỗi kết quả dạng cuộn.

Bước 1: Thêm hình ảnh

  1. Mở dự án ứng dụng DiceScroller trong Android Studio nếu dự án chưa mở. Nếu trước đây bạn không tham gia lớp học lập trình, bạn có thể tải ứng dụng này xuống tại đây: DiceScroller.
  2. Trong Dự án > chế độ xem Android, mở rộng thư mục res rồi mở rộng có thể vẽ.



    Ứng dụng của bạn sử dụng nhiều tài nguyên, bao gồm hình ảnh và biểu tượng, màu sắc, chuỗi và bố cục XML. Tất cả tài nguyên đó được lưu trữ trong thư mục res. Thư mục drawable là nơi bạn sẽ đặt tất cả tài nguyên hình ảnh cho ứng dụng của mình. Trong thư mục drawable, bạn có thể tìm thấy các tài nguyên cho biểu tượng trình chạy của ứng dụng.
  3. Nhấp đúp vào ic_launch_background.xml. Lưu ý rằng đây là các tệp XML mô tả biểu tượng dưới dạng hình ảnh vectơ. Vectơ cho phép hình ảnh của bạn được vẽ ở nhiều kích thước và độ phân giải khác nhau. Các hình ảnh bit như PNG hoặc GIF có thể cần phải điều chỉnh kích thước cho các thiết bị khác nhau, điều này có thể khiến chất lượng bị giảm.
  4. Nhấp vào Xem trước ở cột bên phải của trình chỉnh sửa XML để xem vectơ có thể vẽ ở dạng hình ảnh.


  5. Tải hình ảnh xúc xắc cho ứng dụng của bạn từ DiceImages.zip. Giải nén tệp lưu trữ. Bạn nên có một thư mục tệp XML trông như sau:

  1. Trong Android Studio, hãy nhấp vào trình đơn thả xuống ở đầu chế độ xem dự án hiện có tên Android và chọn Dự án. Ảnh chụp màn hình dưới đây cho thấy cấu trúc của ứng dụng của bạn trông như thế nào trong hệ thống tệp.


  2. Mở rộng DiceScroller > app > src > main > res > Drawable.
  3. Kéo tất cả các tệp XML riêng lẻ từ thư mục DiceImages vào Android Studio và vào thư mục có thể vẽ. Nhấp vào Ok.
  1. Chuyển dự án trở lại chế độ xem Android và nhận thấy rằng các tệp hình ảnh xúc xắc nằm trong thư mục có thể vẽ.
  2. Nhấp đúp vào dice_1.xml và nhận thấy mã XML cho hình ảnh này. Nhấp vào nút Xem trước để xem bản đồ này có thể vẽ như thế nào.

Bước 2: Cập nhật bố cục để sử dụng hình ảnh

Giờ đây, bạn có các tệp hình ảnh xúc xắc trong thư mục res/drawables của mình, bạn có thể truy cập các tệp đó từ bố cục và mã của ứng dụng. Trong bước này, bạn thay thế TextView để hiển thị các số bằng ImageView để hiển thị các hình ảnh đó.

  1. Mở tệp bố cục activity_main.xml nếu tệp chưa mở. Nhấp vào thẻ Văn bản để xem mã XML của bố cục.
  2. Xoá phần tử <TextView>.
  3. Thêm một phần tử <ImageView> có các thuộc tính sau đây:
<ImageView
   android:id="@+id/dice_image"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center_horizontal"
   android:src="@drawable/dice_1" />

Bạn dùng ImageView để hiển thị một hình ảnh trong bố cục của mình. Thuộc tính mới duy nhất cho phần tử này là android:src để cho biết tài nguyên nguồn của hình ảnh. Trong trường hợp này, nguồn hình ảnh @drawable/dice_1 có nghĩa là Android phải tìm kiếm trong các tài nguyên có thể vẽ (res/drawable) cho hình ảnh có tên dice_1.

  1. Nhấp vào nút Xem trước để xem trước bố cục. Giá trị này sẽ có dạng như sau:

Bước 3: Cập nhật mã

  1. Mở MainActivity. Đến đây, bạn đã thấy hàm rollDice()
private fun rollDice() {
   val randomInt = Random().nextInt(6) + 1

   val resultText: TextView = findViewById(R.id.result_text)
   resultText.text = randomInt.toString()
}

Xin lưu ý rằng tham chiếu đến R.id.result_text có thể được làm nổi bật bằng màu đỏ— đó là vì bạn đã xóa TextView khỏi bố cục và mã đó không còn tồn tại.

  1. Xóa hai dòng ở cuối hàm xác định biến resultText và đặt thuộc tính văn bản của biến này. Bạn không còn sử dụng TextView trong bố cục, vì vậy, bạn không cần phải sử dụng một trong hai dòng này.
  2. Hãy dùng findViewByID() để tham chiếu đến ImageView mới trong bố cục theo mã (R.id.dice_image) và chỉ định chế độ xem đó cho biến diceImage mới:
val diceImage: ImageView = findViewById(R.id.dice_image)
  1. Thêm một khối when để chọn hình ảnh xúc xắc cụ thể dựa trên giá trị của randomInteger:
val drawableResource = when (randomInt) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   else -> R.drawable.dice_6
}

Giống như mã nhận dạng, bạn có thể tham chiếu hình ảnh xúc xắc trong thư mục có thể vẽ bằng các giá trị trong lớp R. Ở đây R.drawable đề cập đến thư mục có thể vẽ của ứng dụng và dice_1 là một tài nguyên hình ảnh xúc phạm cụ thể trong thư mục đó.

  1. Cập nhật nguồn của ImageView bằng phương thức setImageResource() và tham chiếu đến hình ảnh xúc xắc mà bạn vừa tìm thấy.
diceImage.setImageResource(drawableResource)
  1. Biên dịch và chạy ứng dụng. Bây giờ, khi bạn nhấp vào nút Cuộn, hình ảnh sẽ cập nhật bằng hình ảnh thích hợp.

Mọi thứ trong ứng dụng của bạn hoạt động nhưng có nhiều thứ khác để phát triển ứng dụng hơn là chỉ có mã hoạt động. Bạn cũng đã nắm được cách viết các ứng dụng có hiệu suất tốt và hoạt động tốt. Điều này có nghĩa là ứng dụng của bạn phải chạy tốt, ngay cả khi người dùng của bạn không có thiết bị Android đắt nhất hoặc có kết nối mạng tốt nhất. Ứng dụng của bạn cũng sẽ tiếp tục chạy suôn sẻ khi bạn thêm các tính năng khác, đồng thời mã của bạn phải dễ đọc và được tổ chức tốt.

Trong nhiệm vụ này, bạn sẽ tìm hiểu về một cách giúp ứng dụng của mình hiệu quả hơn.

  1. Mở MainActivity nếu ứng dụng chưa mở. Trong phương thức rollDice(), hãy lưu ý phần khai báo cho biến diceImage:
val diceImage : ImageView = findViewById(R.id.dice_image)

rollDice() là trình xử lý lượt nhấp cho nút Cuộn, mỗi khi người dùng nhấn vào nút đó, ứng dụng của bạn sẽ gọi findViewById() và nhận được thông tin tham chiếu khác đến ImageView này. Lý tưởng nhất là bạn nên giảm thiểu số lượng lệnh gọi đến findViewById() vì hệ thống Android đang tìm kiếm toàn bộ hệ phân cấp chế độ xem mỗi lần, và đó là một thao tác tốn kém.

Trong một ứng dụng nhỏ như ứng dụng này, đó không phải là vấn đề lớn. Nếu bạn đang chạy một ứng dụng phức tạp hơn trên điện thoại chậm, việc liên tục gọi findViewById() có thể khiến ứng dụng của bạn bị trễ. Thay vào đó, bạn nên gọi findViewById() một lần và lưu trữ đối tượng View trong một trường. Việc giữ tệp tham chiếu đến ImageView trong một trường sẽ cho phép hệ thống truy cập trực tiếp vào View bất cứ lúc nào. Điều này giúp cải thiện hiệu suất.

  1. Ở đầu lớp học, trước ngày onCreate(), hãy tạo một trường để lưu giữ ImageView.
var diceImage : ImageView? = null

Lý tưởng nhất là bạn nên khởi tạo biến này ở đây khi biến đó được khai báo hoặc trong một hàm dựng, nhưng các hoạt động trên Android thì không dùng hàm dựng. Trên thực tế, chế độ xem trong bố cục không thể truy cập được trong đối tượng trong bộ nhớ cho đến khi chúng được tăng cường trong phương thức onCreate(), bằng lệnh gọi tới setContentView(). Bạn không thể khởi chạy biến diceImage cho đến khi điều đó xảy ra.

Một lựa chọn là xác định biến diceImage là có thể có giá trị null, như trong ví dụ này. Đặt giá trị này thành null khi giá trị này được khai báo, sau đó chỉ định cho ImageView thực tế trong onCreate() bằng findViewById(). Tuy nhiên, điều này sẽ làm phức tạp mã của bạn hơn, vì giờ đây, bạn phải kiểm tra giá trị null mỗi lần bạn muốn sử dụng diceImage. Có một cách hay hơn!

  1. Thay đổi khai báo diceImage để sử dụng từ khóa lateinit và xóa chỉ định null:
lateinit var diceImage : ImageView

Từ khóa lateinit hứa hẹn trình biên dịch Kotlin rằng biến sẽ được khởi tạo trước khi mã gọi bất kỳ thao tác nào trên biến đó. Do đó, chúng ta không cần phải khởi tạo biến thành null tại đây và có thể coi biến đó là biến không thể có giá trị null khi sử dụng biến đó. Bạn nên dùng lateinit với các trường lưu giữ lượt xem theo cách này.

  1. Trong onCreate(), sau phương thức setContentView(), hãy sử dụng findViewById() để nhận ImageView.
diceImage = findViewById(R.id.dice_image)
  1. Xóa dòng cũ trong rollDice() khai báo và nhận ImageView. Bạn đã thay thế dòng này bằng phần khai báo trường trước đó.
val diceImage : ImageView = findViewById(R.id.dice_image)
  1. Chạy lại ứng dụng để thấy rằng ứng dụng vẫn hoạt động như dự kiến.

Hiện tại, bạn đang dùng dice_1 làm hình ảnh đầu tiên cho con xúc xắc. Thay vào đó, giả sử bạn không muốn hiển thị hình ảnh nào cho đến khi xúc xắc lần đầu tiên được cuộn. Có một vài cách để thực hiện việc này.

  1. Mở activity_layout.xml trong thẻ Văn bản.
  2. Trong phần tử <ImageView>, hãy đặt thuộc tính android:src thành "@drawable/empty_dice":
android:src="@drawable/empty_dice" 

Hình ảnh empty_dice là một trong những hình ảnh mà bạn đã tải xuống và thêm vào thư mục drawable. Nó có cùng kích thước với các hình xúc xắc khác, chỉ là hình ảnh trống. Hình ảnh này là hình ảnh sẽ hiển thị khi ứng dụng khởi động lần đầu tiên.

  1. Nhấp vào thẻ Thiết kế. Hình ảnh xúc xắc hiện đang trống, nhưng hình ảnh đó cũng không hiển thị trong bản xem trước.



    Các nội dung thiết kế có thể được xác định linh động vào thời gian chạy, ví dụ như bất kỳ ứng dụng nào lấy dữ liệu từ Internet đều có thể bắt đầu bằng một màn hình trống hoặc trống. Nhưng sẽ hữu ích khi bạn thiết kế một ứng dụng có một số loại dữ liệu phần giữ chỗ trong bố cục để bạn biết mình đang bố trí gì.
  2. Trong activity_layout.xml, hãy sao chép dòng android:src và dán một bản sao thứ hai. Thay đổi từ "android" thành "tools", để hai thuộc tính của bạn trông giống như sau:
android:src="@drawable/empty_dice" 
tools:src="@drawable/empty_dice" />

Trong trường hợp này, bạn đã thay đổi vùng chứa tên XML của thuộc tính này từ vùng chứa tên android mặc định thành vùng chứa tên tools. Vùng chứa tên tools được dùng khi bạn muốn xác định nội dung trình giữ chỗ chỉ dùng trong bản xem trước hoặc trình chỉnh sửa thiết kế trong Android Studio. Các thuộc tính sử dụng vùng chứa tên tools sẽ bị xóa khi bạn biên dịch ứng dụng.

Vùng chứa tên được dùng để giải quyết sự không rõ ràng khi đề cập đến các thuộc tính có cùng tên. Ví dụ: cả hai thuộc tính này trong thẻ <ImageView> đều có cùng tên (src), nhưng vùng chứa tên khác nhau.

  1. Kiểm tra phần tử <LinearLayout> tại thư mục gốc của tệp bố cục và nhận thấy hai vùng chứa tên được xác định tại đây.
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   ...
  1. Thay đổi thuộc tính tools:src trong thẻ ImageView thành dice_1 thay vì empty_dice:
android:src="@drawable/empty_dice" 
tools:src="@drawable/dice_1" />

Xin lưu ý rằng hình ảnh dice_1 hiện đã được đặt làm hình ảnh giữ chỗ trong bản xem trước.

  1. Biên dịch và chạy ứng dụng. Lưu ý rằng hình ảnh xúc giác trống trong ứng dụng thực tế cho đến khi bạn nhấp hoặc nhấn vào Cuộn.

Một trong những điều tuyệt vời về việc phát triển cho Android là số lượng thiết bị trong số đó có thể chạy trên mã—từ Nexus One sang Pixel, để tạo thành các yếu tố như máy tính bảng, Pixelbook, đến đồng hồ, TV và ô tô.

Khi bạn viết cho Android, bạn không viết các ứng dụng hoàn toàn riêng biệt cho từng thiết bị này. Ngay cả những ứng dụng chạy trên các kiểu dáng khác nhau như đồng hồ và TV cũng có thể chia sẻ mã. Tuy nhiên, bạn vẫn cần có những hạn chế và chiến lược tương thích để hỗ trợ tất cả các khía cạnh này.

Trong nhiệm vụ này, bạn sẽ tìm hiểu cách nhắm mục tiêu ứng dụng của mình cho các cấp độ API (phiên bản) cụ thể và cách sử dụng các thư viện Android Jetpack để hỗ trợ các thiết bị cũ.

Bước 1: Khám phá các cấp độ API

Trong lớp học lập trình trước đây, khi tạo dự án, bạn đã chỉ định cấp độ API cụ thể của Android mà ứng dụng của bạn nên hỗ trợ. Hệ điều hành Android có các số phiên bản khác nhau được đặt tên theo những món ăn ngon theo thứ tự bảng chữ cái. Mỗi phiên bản hệ điều hành đều có các tính năng và chức năng mới. Ví dụ: Android Oreo có hỗ trợ Ứng dụng hình trong hình, trong khi Android Pie giới thiệu Slices. Các cấp độ API tương ứng với các phiên bản Android. Ví dụ: API 19 tương ứng với Android 4.4 (KitKat).

Do một số yếu tố, bao gồm nội dung phần cứng có thể hỗ trợ, liệu người dùng có chọn cập nhật thiết bị của họ hay không và nhà sản xuất có hỗ trợ các cấp hệ điều hành khác nhau hay không, người dùng sẽ chắc chắn chọn được thiết bị chạy các phiên bản hệ điều hành khác nhau.

Khi tạo dự án ứng dụng, bạn sẽ chỉ định cấp độ API tối thiểu mà ứng dụng của bạn hỗ trợ. Nghĩa là bạn chỉ định phiên bản Android cũ nhất mà ứng dụng của bạn hỗ trợ. Ứng dụng của bạn cũng có một cấp mà ứng dụng đó được biên soạn và một cấp mà ứng dụng nhắm mục tiêu. Mỗi cấp độ này là một thông số cấu hình trong tệp bản dựng Gradle của bạn.

  1. Mở rộng thư mục Gradle Script và mở tệp build.gradle (Mô-đun: ứng dụng).

    Tệp này xác định các thông số và phần phụ thuộc của bản dựng dành riêng cho mô-đun ứng dụng. Tệp build.gradle (Dự án: DiceScroller) xác định toàn bộ thông số bản dựng cho dự án. Trong nhiều trường hợp, mô-đun ứng dụng của bạn là mô-đun duy nhất trong dự án, do đó, sự phân chia này có thể tùy ý. Tuy nhiên, nếu ứng dụng của bạn phức tạp hơn và bạn chia ứng dụng thành nhiều phần hoặc nếu ứng dụng của bạn hỗ trợ các nền tảng như đồng hồ Android, thì bạn có thể gặp phải các mô-đun khác nhau trong cùng một dự án.
  2. Kiểm tra phần android nằm ở đầu tệp build.gradle. (Mẫu bên dưới không phải là toàn bộ, nhưng nó chứa những gì bạn quan tâm nhất trong lớp học lập trình này.)
android {
   compileSdkVersion 28
   defaultConfig {
       applicationId "com.example.android.diceroller"
       minSdkVersion 19
       targetSdkVersion 28
       versionCode 1
       versionName "1.0"
   }
  1. Kiểm tra thông số compileSdkVersion.
compileSdkVersion 28

Thông số này chỉ định cấp độ API Android mà Gradle nên sử dụng để biên dịch ứng dụng của bạn. Đây là phiên bản Android mới nhất mà ứng dụng của bạn có thể hỗ trợ. Tức là ứng dụng của bạn có thể sử dụng các tính năng API đi kèm trong cấp độ API này trở xuống. Trong trường hợp này, ứng dụng của bạn hỗ trợ API 28, tương ứng với Android 9 (Pie).

  1. Hãy kiểm tra thông số targetSdkVersion, bên trong phần defaultConfig:
targetSdkVersion 28

Giá trị này là API gần đây nhất mà bạn đã thử nghiệm ứng dụng của mình. Trong nhiều trường hợp, giá trị này giống với compileSdkVersion.

  1. Kiểm tra thông số minSdkVersion.
minSdkVersion 19

Thông số này là quan trọng nhất trong ba tham số, vì thông số này xác định phiên bản Android cũ nhất mà ứng dụng của bạn sẽ chạy. Các thiết bị chạy hệ điều hành Android cũ hơn cấp độ API này không thể chạy ứng dụng của bạn.

Việc chọn cấp độ API tối thiểu cho ứng dụng có thể khó khăn. Đặt cấp độ API quá thấp và bạn sẽ bỏ lỡ các tính năng mới hơn của hệ điều hành Android. Đặt thành quá cao và ứng dụng của bạn có thể chỉ chạy trên các thiết bị mới hơn.

Khi bạn thiết lập dự án và chuyển đến nơi bạn xác định cấp độ API tối thiểu cho ứng dụng của mình, hãy nhấp vào Giúp tôi chọn để xem hộp thoại Phân phối phiên bản API. Hộp thoại cung cấp thông tin về số lượng thiết bị sử dụng các cấp hệ điều hành khác nhau và các tính năng đã được thêm vào hoặc thay đổi ở cấp độ hệ điều hành. Bạn cũng có thể xem ghi chú phát hành dành cho tài liệu Android và trang tổng quan để biết thêm thông tin về các hệ quả của việc hỗ trợ các cấp độ API khác nhau.

Bước 2: Khám phá khả năng tương thích

Việc viết nội dung cho các cấp độ API Android khác nhau là một thách thức phổ biến mà các nhà phát triển ứng dụng phải đối mặt. Do đó, nhóm khung Android đã làm rất nhiều việc để giúp bạn.

Năm 2011, nhóm phát hành thư viện hỗ trợ đầu tiên, một thư viện do Google phát triển cung cấp các lớp học tương thích ngược và các chức năng hữu ích. Năm 2018, Google công bố Android Jetpack. Đây là một tập hợp gồm các thư viện bao gồm nhiều lớp và chức năng trước đó của thư viện hỗ trợ, đồng thời mở rộng trên thư viện hỗ trợ.

  1. Mở MainActivity.
  2. Xin lưu ý rằng lớp MainActivity của bạn không mở rộng từ chính Activity, mà từ AppCompatActivity.
class MainActivity : AppCompatActivity() { 
...

AppCompatActivity là lớp tương thích để đảm bảo hoạt động của bạn trông giống nhau trên các cấp hệ điều hành khác nhau trên các nền tảng.

  1. Nhấp vào biểu tượng dấu + bên cạnh dòng bắt đầu bằng import để mở rộng dữ liệu nhập cho lớp học của bạn. Xin lưu ý rằng lớp AppCompatActivity được nhập từ gói androidx.appcompat.app. Vùng chứa tên của các thư viện Android Jetpack là androidx.
  2. Mở build.gradle (Mô-đun: ứng dụng) và cuộn xuống phần phần phụ thuộc.
dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
   implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
   implementation 'androidx.core:core-ktx:1.0.1'
   implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
   androidTestImplementation 
        'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}

Hãy lưu ý đến phần phụ thuộc trên thư viện appcompat, một phần của androidx và chứa lớp AppCompatActivity.

Bước 3: Thêm khả năng tương thích cho tài nguyên có thể vẽ vectơ

Bạn sẽ sử dụng kiến thức mới của mình về vùng chứa tên, Gradle và khả năng tương thích để tạo một mức điều chỉnh cuối cùng cho ứng dụng của bạn nhằm tối ưu hóa kích thước ứng dụng trên các nền tảng cũ hơn.

  1. Mở rộng thư mục res rồi mở rộng Drawable. Nhấp đúp vào một trong các hình xúc xắc.

    Như bạn đã tìm hiểu trước đó, tất cả hình ảnh của xúc xắc thực sự là các tệp XML xác định màu sắc và hình dạng cho xúc xắc. Những loại tệp này được gọi là hình vẽ vectơ. Một ưu điểm của các định dạng vẽ vectơ Ngoài ra, một vectơ vẽ được cũng là một tệp nhỏ hơn nhiều so với hình ảnh ở định dạng bitmap.

    Một điều quan trọng cần lưu ý về đồ vẽ vectơ là chúng được hỗ trợ trong API 21 trở đi. Tuy nhiên, SDK tối thiểu của ứng dụng được đặt thành API 19. Nếu bạn đã thử ứng dụng của mình trên một thiết bị hoặc trình mô phỏng API 19, bạn sẽ thấy rằng ứng dụng có vẻ đang xây dựng và chạy tốt. Vậy quy trình này hoạt động như thế nào?

    Khi bạn xây dựng ứng dụng, quá trình tạo Gradle sẽ tạo một tệp PNG từ mỗi tệp vectơ. Các tệp PNG này sẽ được dùng trên mọi thiết bị Android dưới 21. Các tệp PNG bổ sung này làm tăng kích thước ứng dụng của bạn. Các ứng dụng không cần thiết quá lớn không tuyệt vời – các ứng dụng này giúp người dùng tải xuống chậm hơn và chiếm nhiều dung lượng hơn trên thiết bị\39; dung lượng hạn chế. Các ứng dụng lớn cũng có nguy cơ bị gỡ cài đặt cao hơn và người dùng không thể tải xuống hoặc hủy việc tải xuống các ứng dụng đó.

    Tin vui là có một thư viện tương thích với Android X dành cho đồ họa vectơ có thể quay trở lại API cấp 7.
  2. Mở build.gradle (Mô-đun: ứng dụng). Thêm dòng này vào phần defaultConfig:
vectorDrawables.useSupportLibrary = true
  1. Nhấp vào nút Đồng bộ hóa ngay. Mỗi khi sửa đổi một tệp build.gradle, bạn cần phải đồng bộ hóa các tệp bản dựng với dự án.
  2. Mở tệp bố cục main_activity.xml. Thêm vùng chứa tên này vào thẻ <LinearLayout> gốc, bên dưới vùng chứa tên tools:
xmlns:app="http://schemas.android.com/apk/res-auto"

Vùng chứa tên app dành cho các thuộc tính đến từ mã tùy chỉnh hoặc từ thư viện chứ không phải từ khung Android chính.

  1. Thay đổi thuộc tính android:src trong phần tử <ImageView> thành app:srcCompat.
app:srcCompat="@drawable/empty_dice"


Thuộc tính app:srcCompat sử dụng thư viện Android X để hỗ trợ các đối tượng có thể vẽ bằng vectơ trong các phiên bản Android cũ, quay trở lại API cấp 7.

  1. Tạo và chạy ứng dụng. Bạn sẽ không thấy bất kỳ nội dung nào khác trên màn hình, nhưng bây giờ ứng dụng của bạn không cần phải sử dụng các tệp PNG đã tạo cho các hình xúc xắc bất kể vị trí chạy, nghĩa là một tệp ứng dụng nhỏ hơn.

Dự án Android Studio: DiceScrollerfinal

Thử thách: Sửa đổi ứng dụng DiceScroller để có hai xúc xắc. Khi người dùng nhấn vào nút Cuộn, mỗi con xúc xắc phải có một giá trị độc lập với con xúc xắc.

Mẹo: Hãy tạo một hàm riêng tư mới để lấy hình ảnh có thể vẽ ngẫu nhiên và trả về một số nguyên cho tài nguyên có thể vẽ. Sử dụng hàm đó cho từng hình ảnh xúc xắc.

private fun getRandomDiceImage() : Int { ... }

Mã giải pháp xác thực mã hóa

Dự án Android Studio: Diceiceerer-Challenge

Tài nguyên ứng dụng:

  • Tài nguyên của ứng dụng có thể bao gồm hình ảnh và biểu tượng, màu chuẩn được dùng trong ứng dụng, chuỗi và bố cục XML. Tất cả tài nguyên đó được lưu trữ trong thư mục res.
  • Thư mục tài nguyên drawable là nơi bạn nên đặt tất cả tài nguyên hình ảnh cho ứng dụng của mình.

Sử dụng vectơ có thể vẽ trong chế độ xem hình ảnh:

  • Tài nguyên có thể vẽ được là các hình ảnh được mô tả ở định dạng XML. Hình vẽ được vẽ linh hoạt hơn hình ảnh bitmap (chẳng hạn như tệp PNG) vì chúng có thể được điều chỉnh theo kích thước hoặc độ phân giải bất kỳ.
  • Để thêm tài nguyên có thể vẽ vào bố cục của ứng dụng, hãy dùng phần tử <ImageView>. Nguồn của hình ảnh nằm trong thuộc tính android:src. Để tham chiếu đến thư mục tài nguyên có thể vẽ, hãy sử dụng @drawable, ví dụ: "@drawable/image_name".
  • Sử dụng chế độ xem ImageView trong mã MainActivity cho hình ảnh. Bạn có thể sử dụng setImageResource() để thay đổi hình ảnh trong chế độ xem thành tài nguyên khác. Hãy dùng R.drawable để chỉ các mục có thể vẽ cụ thể, ví dụ: setImageResource(R.drawable.image_name).

Từ khóa lateinit:

  • Thu nhỏ các lệnh gọi đến findViewById() trong mã của bạn bằng cách khai báo các trường để lưu những chế độ xem đó và khởi tạo các trường trong onCreate(). Sử dụng từ khóa lateinit cho trường này để tránh cần phải khai báo giá trị có thể có giá trị null.

Vùng chứa tên tools cho các thuộc tính tại thời điểm thiết kế:

  • Hãy dùng thuộc tính tools:src trong phần tử <ImageView> của bố cục để chỉ hiển thị một hình ảnh trong bản xem trước hoặc trình chỉnh sửa thiết kế của Android Studio. Sau đó, bạn có thể dùng một hình ảnh trống cho android:src cho ứng dụng cuối cùng.
  • Hãy sử dụng vùng chứa tên tools trong tệp bố cục Android để tạo nội dung hoặc chỉ dẫn giữ chỗ cho bố cục trong Android Studio. Dữ liệu do các thuộc tính tools khai báo không được sử dụng trong ứng dụng cuối cùng.

Các cấp độ API:

  • Mỗi hệ điều hành Android đều có số và tên phiên bản chính thức (ví dụ: Android 9.0, "Pie") và cấp độ API (API 28). Sử dụng các cấp độ API trong tệp Gradle của ứng dụng để chỉ định các phiên bản Android mà ứng dụng của bạn hỗ trợ.
  • Thông số compileSdkVersion trong tệp build.gradle chỉ định cấp độ API Android mà Gradle nên sử dụng để biên dịch ứng dụng của bạn.
  • Thông số targetSdkVersion chỉ định cấp độ API gần đây nhất mà bạn đã thử nghiệm ứng dụng. Trong nhiều trường hợp, thông số này có cùng giá trị với compileSdkVersion.
  • Thông số minSdkVersion chỉ định cấp độ API cũ nhất mà ứng dụng của bạn có thể chạy.

Android Jetpack:

  • Android Jetpack là một tập hợp thư viện do Google phát triển, cung cấp các lớp học tương thích ngược và các chức năng hữu ích để hỗ trợ phiên bản Android cũ. Jetpack thay thế và mở rộng trên tập hợp thư viện trước đây được gọi là Thư viện hỗ trợ Android.
  • Các lớp nhập từ gói androidx tham chiếu đến thư viện Jetpack. Phần phụ thuộc vào Jetpack trong tệp build.gradle cũng bắt đầu bằng androidx.

Khả năng tương thích ngược đối với tài nguyên có thể vẽ vectơ:

  • Các tài nguyên có thể vẽ ect chỉ được hỗ trợ trong các phiên bản Android cao hơn API 21. Trong các phiên bản cũ, Gradle tạo hình ảnh PNG cho các hình ảnh có thể vẽ đó khi ứng dụng của bạn được xây dựng.
  • Bạn có thể chỉ định rằng Thư viện hỗ trợ Android sẽ được dùng cho các vectơ được vẽ trong phiên bản API cũ hơn với thông số cấu hình vectorDrawables.useSupportLibrary = true trong tệp build.gradle.
  • Sau khi bạn bật thư viện hỗ trợ cho các tài nguyên có thể vẽ vectơ, hãy sử dụng thuộc tính app:srcCompat trong phần tử <ImageView> (thay vì android:src) để chỉ định nguồn có thể vẽ vectơ cho hình ảnh đó.

Vùng chứa tên app:

  • Vùng chứa tên app trong tệp bố cục XML của bạn dành cho các thuộc tính đến từ mã tùy chỉnh hoặc từ thư viện, chứ không phải từ khung Android chính.

Khóa học từ Udacity:

Tài liệu dành cho nhà phát triển Android:

Các tài liệu khác:

Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:

  • Giao bài tập về nhà nếu được yêu cầu.
  • Trao đổi với học viên cách nộp bài tập về nhà.
  • Chấm điểm bài tập về nhà.

Người hướng dẫn có thể sử dụng những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.

Nếu bạn đang tự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.

Thay đổi ứng dụng

Thêm nút Xóa vào ứng dụng DiceScroller để đặt hình ảnh xúc trở lại hình ảnh trống.

Trả lời những câu hỏi này

Câu hỏi 1

Thuộc tính <ImageView> nào cho biết hình ảnh nguồn chỉ nên được dùng trong Android Studio?

  • android:srcCompat
  • app:src
  • tools:src
  • tools:sourceImage

Câu hỏi 2

Phương thức nào thay đổi tài nguyên hình ảnh cho ImageView trong mã Kotlin? xmx

  • setImageResource()
  • setImageURI()
  • setImage()
  • setImageRes()

Câu hỏi 3

Từ khoá lateinit trong khai báo biến thể hiện điều gì trong mã Kotlin?

  • Biến này không bao giờ được khởi tạo.
  • Biến này chỉ được khởi tạo trong thời gian chạy ứng dụng.
  • Biến này được tự động khởi chạy tới null.
  • Biến này sẽ được khởi tạo sau. Tôi hứa!

Câu hỏi 4

Cấu hình Gradle nào cho biết cấp API gần đây nhất mà ứng dụng của bạn đã được kiểm thử?

  • minSdkVersion
  • compileSdkVersion
  • targetSdkVersion
  • testSdkVersion

Câu hỏi 5

Bạn sẽ thấy một dòng nhập trong mã bắt đầu bằng androidx. Như thế nghĩa là sao?

  • Lớp này nằm trong thư viện Android Jetpack.
  • Lớp học nằm trong thư viện bên ngoài sẽ được tải động khi ứng dụng chạy.
  • Lớp này là lớp "bổ sung" và không bắt buộc.
  • Lớp này nằm trong phần hỗ trợ XML của Android.

Gửi ứng dụng để chấm điểm

Kiểm tra để đảm bảo ứng dụng có các thông tin sau:

  • Bố cục ứng dụng phải bao gồm một chế độ xem hình ảnh và hai nút.
  • Mã của ứng dụng phải đặt hai trình xử lý lượt nhấp, một trình xử lý cho mỗi nút.
  • Trình xử lý lượt nhấp cho nút Xóa phải đặt hình ảnh xúc giác thành R.drawable.empty_dice.

Bắt đầu bài học tiếp theo: 1.4: Tìm hiểu cách tự trợ giúp

Để biết đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích của các lớp học lập trình cơ bản về Android Kotlin.