Được chỉnh sửa ngày 19/03/2019.
Chào mừng các bạn quay trở lại với bài học Android thứ 8, bài học về quản lý resource Android. Đây là bài học trong chuỗi bài viết về lập trình ứng dụng Android bằng Java của Yellow Code Books.
Ở bài trước chúng ta đã làm quen sơ qua cách thức tạo một giao diện cho Android rồi đúng không nào. Đến bài học hôm nay các bạn sẽ chưa cần phải tạo một giao diện nào khác đâu, mà hãy cùng mình nhìn lại cách thức chúng ta sử dụng các “nguyên liệu” để tạo thành một giao diện hoàn chỉnh của bài hôm trước. “Nguyên liệu” mà mình muốn nhắc đến đó chính là khái niệm Resource.
Resource Là Gì?
Trong lập trình Android, có một sự phân biệt rõ ràng giữa resource và source code. Nếu như source code là Java code, thì resource là những file còn lại không liên quan đến Java code đó, chẳng hạn như các file hình ảnh, âm thanh, video, các file text, màu sắc,… và thậm chí là file activity_main.xml mà bạn đã làm quen hôm trước, cũng chính là các resource mà chúng ta cần quan tâm.
Resource Để Ở Đâu Trong Project?
Nếu như source code được để trong thư mục java/ thì resource được để trong thư mục res/. Bạn hãy nhìn vào hai thư mục mà mình vừa nhắc đến bên dưới đây.
Nếu bạn xổ thư mục res/ này ra, bạn sẽ nhìn thấy rất nhiều thư mục con trong đó. Những thư mục con này được tạo sẵn khi bạn tạo mới một project, số lượng thư mục tạo sẵn và tên các thư mục này phụ thuộc vào từng thời điểm của Android Platform. Ngoài các thư mục được tạo sẵn này bạn có thể tạo thêm nhiều thư mục khác nữa (tất nhiên theo một quy luật đặt tên nhất định mà chúng ta sẽ nói rõ hơn ở các bài sau).
Sử Dụng Resource Như Thế Nào?
Bạn nên ghi nhớ rằng, resource của Android được chia ra làm 2 dạng, Default Resource (resource theo mặc định) và Alternative Resource (resource có chọn lựa).
Default Resource
Default resource có thể dịch là resource theo mặc định. Các resource dạng này sẽ được hiển thị tùy theo chỉ định mặc định của máy.
Ví dụ như bạn để một ảnh Bitmap vào trong default resource, thì ảnh của bạn sẽ được dùng cho tất cả các kích cỡ màn hình ở ngoài thị trường, nó có thể sẽ trông đẹp ở một số màn hình, nhưng có thể sẽ mờ, hay vỡ ở một số màn hình khác.
Thêm một ví dụ nữa, chẳng hạn như bạn đang xây dựng ứng dụng hỗ trợ song ngữ Anh-Việt, thì với file ngôn ngữ được để ở default resource, file này sẽ được hệ thống dùng đến mà không hề có sự chọn lựa rằng khi nào nên hiển thị giao diện ứng dụng với ngôn ngữ tiếng Anh, khi nào tiếng Việt.
Nói vậy không có nghĩa là default resource vô dụng, vậy khi nào thì dùng default resource được? Chúng ta đi tiếp phần dưới đây để cùng nhau sáng tỏ.
Cách Sử Dụng Default Resource
Nếu bạn có một ảnh, hay một file âm thanh, hay một text, mà bạn không cần phải phân biệt chúng giữa các thiết bị khác nhau trên thị trường, thì bạn có thể để các file này ở default resource.
Mình ví dụ như resource là một file âm thanh notification của app. Ồ file này thì dù cho máy có màn hình kiểu nào, LG hay Samsung, chạy ở quốc gia nào cũng được. Cho file này vào default resource thôi.
Hay bạn có một ảnh dạng Vector, ảnh dạng này sẽ không bị bể khi bạn zoom nó như là ảnh Bitmap, do đó nó không cần phân biệt theo các loại màn hình khác nhau. Cho file ảnh này vào default resource thôi.
Thêm một ví dụ nữa, đó là nếu bạn có các resource dạng màu sắc. Ồ màn hình nào cũng hiển thị màu giống nhau, không cần phân biệt. Cho resource này vào default resource thôi.
Nhiêu đây ví dụ chắc bạn cũng đã hiểu khi nào thì dùng resource dạng default resource. Vậy thì, cho resource vào default resource là thế nào? Dễ lắm, bạn chỉ cần để các resource mà bạn có vào các thư mục con của thư mục res/ theo một quy tắc sau. Nên nhớ là các thư mục con này hoặc sẽ có sẵn khi bạn tạo mới project (như hình mình để ở trên kia), hoặc bạn tự tạo nhưng nhớ đúng với tên mình liệt kê ra đây (tất cả loại resource này sẽ được mình nói rõ ở từng bài học riêng biệt, bạn yên tâm nếu đọc sơ qua danh sách dưới đây mà không hiểu gì nhé).
– animator/: Thư mục này dùng để chứa các animation theo dạng XML. Đây là các resource định nghĩa về chuyển động cho các thành phần UI của ứng dụng.
– anim/: Thư mục này cũng dùng để chứa các animation theo dạng XML. Nhưng khác với animator, các resource theo dạng anim tạo chuyển động theo một số cấu trúc định sẵn, và vì vậy anim sẽ không linh hoạt bằng animator.
– color/: Thư mục này dùng để chứa định nghĩa về các màu sắc được biến đổi theo trạng thái (state list of colors) cho ứng dụng theo dạng XML.
– drawable/: Thư mục này dùng để chứa các ảnh bitmap (hỗ trợ các ảnh theo định dạng .png, .9.png, .jpg, .gif); Chứa các ảnh biến đổi theo trạng thái (state list); Chứa các hình khối (shape) do bạn tự vẽ bằng XML; Và còn nhiều loại khác nhưng ít phổ biến hơn các loại mình vừa nêu.
– mipmap/: Thư mục này dùng để chứa các icon của ứng dụng.
– layout/: Thư mục này dùng để chứa đựng tất cả các giao diện màn hình được code bằng XML. Bạn nhớ lại đi, bài trước chúng ta thử code giao diện ở file activity_main.xml, file lày có phải nằm trong thư mục layout/ không nào?
– menu/: Thư mục này dùng để chứa đựng tất cả các định nghĩa cho menu của ứng dụng, bao gồm Options Menu, Context Menu, hay Sub Menu.
– raw/: Thư mục dùng để chứa đựng các file mà bạn được phép định nghĩa nội dung tùy thích, không bị gò bó phải là XML hay Java code gì cả.
– values/: Thư mục này dùng để chứa đựng các file XML với các kiểu dữ liệu đơn giản, như string, integer, color. Chúng ta sẽ nói rõ hơn chút xíu về thư mục values/ này ở phần tiếp theo của bài học hôm nay.
– xml/: Thư mục này dùng để chứa đựng các file XML tùy ý, tuy tùy ý nhưng phải theo cấu trúc XML.
Vậy thôi đấy, có nhiều lắm không bạn. Bạn yên tâm chúng ta sẽ nói rất cụ thể về từng loại resource trong từng thư mục default resource này sau.
Và có một điều nên nhớ rằng, trong Android thư mục res/ chỉ chứa duy nhất một cấp thư mục con thôi. Do đó bạn không thể tạo một thư mục con như này được res/values/my_res/.
Thực Hành Tạo Default Resource Cho TourNote
Chào mừng các bạn quay lại ứng dụng TourNote.
Trước hết, hãy nhớ lại, bài trước bạn đã cho hiển thị dòng chữ “Bạn chưa có ghi chú nào cho mình, hãy tạo mới ghi chú từ hôm nay” lên màn hình. Bạn làm rất đúng, và ứng dụng chạy lên thiết bị ngon lành.
Nhưng… hãy nhìn lại activity_main.xml, có một số dấu hiệu cảnh báo bên trong editor này. Nếu ở tab Design, đảm bảo TextView ở giữa màn hình được chọn, bạn sẽ thấy icon hình biển báo màu vàng với dấu chấm than bên trong, click vào icon này bạn sẽ thấy nội dung cảnh báo được liệt kê ở một cửa sổ nhỏ bên dưới nó.
Hay ở tab Text, bạn cũng thấy vệt vàng xuất hiện ở bên phải text, đưa chuột vào đó bạn cũng nhìn thấy rõ nội dung cảnh báo.
Các vệt vàng xuất hiện trong editor chính là các vệt cảnh báo, nó cho bạn biết là bạn đã làm gì đó chưa được đúng đắn. Mặc dù nó không phải là các vệt lỗi được hiển thị bằng màu đỏ, nếu có vệt đỏ bạn sẽ không build app được mà phải sửa ngay.
Quay lại một chút với nội dung các vệt vàng này. Bạn đều thấy cả hai nói rằng bạn đang hardcode, dịch ra tiếng Việt là code cứng, hay có thể hiểu là “code chuối”. Bởi vì như bạn thấy đó, các resource trong Android được tách bạch rõ ràng vai trò theo các thư mục. Nghĩa là sao? Bạn xem, activity_main.xml là file nằm trong thư lục layout/, chính vì vậy nó sẽ chỉ chứa thông tin về giao diện mà thôi. Do đó, việc bạn để hẳn một chuỗi trong file layout đó là tạm được, nhưng sẽ bị cảnh báo rằng bạn đang code cứng.
Vậy bạn đã hiểu mình phải làm gì để xóa bỏ lời cảnh báo rồi phải không nào, bạn phải khai báo chuỗi ở nơi khác, và gọi đến chúng từ file layout này. Lợi ích của việc làm này từ từ bạn sẽ hiểu khi chúng ta qua bước kế tiếp trong bài này.
Ở bước tiếp theo này, mình muốn các bạn nên tổ chức rằng cách chuỗi nên để trong file strings.xml. Để có thể hiểu rõ hơn tại sao chuỗi lại để trong đây thì đến các bài học về từng loại resource mình sẽ trình bày cụ thể hơn. Để mở file chứa chuỗi này ra thì bạn tìm trong thư mục values/ như hình sau, rồi click đúp vào file đó.
Khi file này được mở ra, các bạn có thể thấy có ít nhất một string được khai báo sẵn.
Cách thức khai báo một string cụ thể sẽ được mình nói rõ ở bài viết về resource loại này sau, còn bây giờ hãy thêm vào một dòng string mới.
<resources>
<string name="app_name">TourNote</string>
<string name="empty_note">Bạn chưa có ghi chú nào cho mình, hãy tạo mới ghi chú cho mình từ hôm nay</string>
</resources>
Bạn chỉ cần lưu ý rằng String bạn vừa khai báo có tên là empty_note, nội dung của nó nằm trong cặp dấu nháy kép sau khi khai báo tên. Giờ bạn hãy quay lại file activity_main.xml và thay thế chỗ bị cảnh báo vạch vàng lúc nãy bằng một trong hai cách sau.
Nếu bạn thao tác với tab Design. Hãy đảm bảo TextView ở giữa màn hình được chọn. Tìm đến field text mà bài học trước bạn đã sửa chuỗi bằng cách gõ thủ công, rồi tìm đến dấu ba chấm (…) bên góc phải của field này.
Khi nhấn vào dấu ba chấm này, một cửa sổ nhỏ xuất hiện, nơi đây chứa đựng tất cả các resource dạng chuỗi có thể sử dụng được trong project của bạn. Bao gồm cả chuỗi có tên empty_note mà bạn vừa thêm vào ở bước trên. Bạn chỉ việc chọn đến chuỗi đó và nhấn OK thôi.
Khi này field text ở TextView đã có sự thay đổi. Thay vì nội dung “Bạn chưa có…” thì đã được thay thế bằng “@string/empty_note”. Nếu bạn chưa hiểu cách hiển thị chuỗi này như thế nào thì cũng đừng vội nản nhé, bài học sau chúng ta sẽ tìm hiểu kỹ. Lúc này đây bạn hãy nhìn lại editor của activity_main.xml xem sao. Icon cảnh báo đã biến mất, nhưng màn hình hiển thị trực quan giao diện vẫn như bài học trước.
Nếu bạn xem qua tab Text của cửa sổ này, bạn cũng sẽ thấy nội dung của code cũng thay đổi tương ứng, và cũng chẳng còn icon cảnh báo đâu nữa nhé.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/empty_note"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Giờ đây bạn có thể thực thi lại chương trình để kiểm tra xem thao tác của bài học hôm nay có ảnh hưởng gì đến ứng dụng của bạn không nhé.
Alternative Resource
Alternative resource có thể dịch là resource có chọn lựa. Ngược với default resource là các resource mà bạn không cần quan tâm chúng hiển thị thế nào cho từng loại thiết bị. Thì alternative resource là các resource mà bạn chính là người phải chỉ định cho hệ thống biết khi nào nên dùng resource nào, tức là có sự chọn lựa resource theo ý bạn. Bạn hãy nhìn vào hình minh họa sau.
Hình minh họa trên có nghĩa là, giả sử bạn có một layout theo chuẩn alternative resource, khi đó bạn có thể chỉ định rõ cho hệ thống rằng nếu ứng dụng chạy trên phone (như hình bên trái) thì hãy hiển thị layout đó dạng list, còn nếu ứng dụng chạy trên tablet (hình bên phải) thì hãy hiển thị layout dạng table.
Vậy cách sử dụng alternative resource có khác với default resource hay không? Mời bạn xem tiếp bước sau.
Cách Sử Dụng Alternative Resource
Cách sử dụng alternative resource đơn giản không kém cách sử dụng default resource, mình chắc chắn đó. Bởi vì bạn vẫn dựa vào cách thức tổ chức thư mục theo dạng default resource, rồi bạn chỉ cần thêm một điều kiện vào cuối tên của mỗi thư mục đó để hệ thống biết khi nào nên load resource nào mà thôi.
Công thức cụ thể cho alternative resource như sau.
<default_resource>-<config_qualifier>
Trong đó default_resource là các thư mục con của res/ mà mình có nhắc đến ở trên như drawable/, layout/,… Còn config_qualifier chính là phần chỉ định cho hệ thống biết khi nào nên sử dụng resource nào. Hai thành phần này được ngăn cách nhau bởi dấu “-“.
Nhìn vào hình dưới đây, bạn sẽ thấy các thư mục được tô sáng chính là các thư mục alternative resource, vì chúng có chỉ định config_qualifier. Để biết rõ về các chỉ định config_qualifier này thì mời bạn đọc bài viết này để hiểu rõ hơn nhé..
Bạn hãy nhìn vào ví dụ các file ảnh ic_launcher.png được để vào trong các alternative resource như hình sau.
Khi đó giả sử ở một thiết bị nào đó, khi hệ thống muốn load ảnh ic_launcher.png này lên dùng, thì việc đầu tiên hệ thống sẽ phải xác định xem ic_launcher.png ở thư mục nào là thích hợp nhất cho thiết bị đó. Nếu hệ thống tìm thấy một thư mục thích hợp, ví dụ mipmap-xxxhdpi/ thì ic_launcher.png trong thư mục đó sẽ được load lên dùng. Còn nếu hệ thống không tìm thấy một thư mục thích hợp, nó sẽ tìm đến thư mục default resource, tức là tìm đến thư mục với tên mipmap/. Chính vì vậy bạn thường thấy có cả các default resource lẫn alternative resource được định nghĩa đồng thời trong cùng một ứng dụng.
Thực Hành Tạo Alternative Resource Cho TourNote
Bây giờ chúng ta sẽ nâng cấp resource string lúc nãy, sao cho app có thể hỗ trợ song ngữ Anh-Việt. Nếu thiết bị của user đang dùng là tiếng Việt, TourNote được khởi động cũng hiển thị tiếng Việt, ngược lại nếu thiết bị của user là tiếng Anh thì TourNote cũng tiếng Anh. Còn nếu thiết bị của user hiển thị ngôn ngữ khác với 2 ngôn ngữ mà bạn hỗ trợ, thì hiển thị mặc định là tiếng Anh.
Trước hết bạn cần phải tạo một alternative resource. Bạn hãy tạo một thư mục con của res/ với tên chính xác là values-vi/.
Chắc bạn cũng biết tại sao phải đặt tên như vậy, nhưng để chắc cú thì mình cũng xin nhắc lại. Chúng ta đã có sẵn một thư mục default resource có tên là values/, trong đó chúng ta chứa một file strings.xml, file này chứa đựng các text của ứng dụng, các text này vẫn chưa được đặt điều kiện config_qualifier do đó nó sẽ được đọc lên ở bất kỳ trường hợp nào của thiết bị. Giờ chúng ta thêm một thư mục có phần default_resource cũng là values, nhưng thêm vào config_qualifier là -vi ý muốn nói hệ thống hãy tìm đến thư mục alternative resource này ngay khi ngôn ngữ máy là vi (tiếng Việt). Còn nếu bạn nào muốn biết tại sao phải là -vi mà không phải -vn hay -vietname thì bạn nên biết là nó đều có quy tắc cả và mình nói hết tất tần tật quy tắc này ở đây rồi nhé.
Giờ đây mình tin chắc bạn đã hiểu rồi, vậy hãy tạo mới thư mục resource như đã nói bằng cách click chuột phải vào res/ chọn New > Directory và gõ vào values-vi.
Sau khi bạn tạo xong thư mục mới, bạn nên kiểm tra chắc chắn rằng thư mục mới đó phải nằm ở con của res/ và ngang cấp với values/ như hình sau nhé.
Nếu bạn tạo sai vị trí của values-vi/ hoặc đặt sai tên, bạn cứ nhấn chuột phải vào thư mục sai đó và chọn delete rồi tạo mới lại theo bước trên đây.
Giờ thì với thư mục value-vi/ mới được tạo, chưa có gì trong đó cả, bạn cần có một file string với nội dung là các text tiếng Việt, bạn có thể copy strings.xml có sẵn trong values/ qua cho tiện.
Để copy strings.xml bên values/, bạn vào thư mục này, click phải chuột vào strings.xml chọn Copy.
Rồi click chuột phải lại vào values-vi/ chọn Paste. Một dialog xuất hiện hỏi bạn có đổi tên không, bạn hãy nhấn OK mà không đổi tên gì cả, vì chúng ta mong muốn 2 tên file đều là strings.xml.
Bạn có nhận ra không. Hiện tại thì file strings.xml bên values/ đang sai vì chúng ta mong muốn thư mục này phải chứa resource default là tiếng Anh.
Vậy bạn hãy mở strings.xml bên values/ để chỉnh sửa thành tiếng Anh như sau.
Nên nhớ là name=”empty_note” là giống nhau ở cả 2 file string này.
Nào, giờ bạn thử vào settings của thiết bị (máy ảo Android hay máy thật mà bạn chuẩn bị run ứng dụng) chỉnh ngôn ngữ là tiếng Anh rồi run ứng dụng. Sau đó vào lại settings của thiết bị chỉnh ngôn ngữ thành tiếng Việt rồi run lại ứng dụng. Hình ảnh của 2 lần run như sau.
Nên nhớ là name=”empty_note” là giống nhau ở cả 2 file string này.
Nào, giờ bạn thử vào settings của thiết bị (máy ảo Android hay máy thật mà bạn chuẩn bị run ứng dụng) chỉnh ngôn ngữ là tiếng Anh rồi run ứng dụng. Sau đó vào lại settings của thiết bị chỉnh ngôn ngữ thành tiếng Việt rồi run lại ứng dụng. Hình ảnh của 2 lần run như sau.
Download Source Code Mẫu
Bạn có thể download source code mẫu ở đây.
Bạn vừa cùng mình làm quen với khái niệm resource trong Android, và cùng nhau xây dựng tạm một resource dạng string. Bạn cũng đã phân biệt hai dạng resource, đó là default resource và alternative resource.
Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao bên dưới mỗi bài nếu thấy thích.
– Comment bên dưới mỗi bài nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
Bài Kế Tiếp
Bạn sẽ tạm dừng tìm hiểu sâu hơn về cách sử dụng resource, chúng ta sẽ sử dụng kiến thức resource cơ bản ở bài này để cùng mình ứng dụng vào cách sử dụng một vài Widget, để mang đến TourNote một giao diện pro hơn.
Xuất sắc!
quá tuyệt
Bài viết rất hay và dễ hiểu, chân thành cám ơn yellowcodebooks đã bỏ công sức ra viết bài cho độc giả
Bài viết bạn rất hay nhưng chữ nhiều quá bạn ơi
Hihi biết sao giờ bạn. Viết ít quá lại không đủ để diễn đạt.
Anh ơi hình như tổ chức thư mục res/ khác hay sao ạ. Được phép có thư mục con cấp thấp hơn. VD thư mục mipmap/ có 2 thư mục con là ic_launcher vs ic_launcher_round, trong các thư mục con này chứa các file alternative resource mà cái config-qualifier của nó được ghi trong đóng mở ngoặc bên cạnh tên file như là: ic_launcher.png (hdpi),… mà em cũng không rõ là Android Studio tự nhận diện config-qualifier hay là mình phải thêm vào đâu đó.
Em dùng Android Studio 3.5, API 29. Em cảm ơn ạ
Ôi trời hóa ra là cái Tab Android nó hiển thị kiểu khác ạ -_- Chuyển qua Tab Project Files thì mọi thứ giống như trong bài anh nói. Thanks a
Là do bạn đang xem phân cấp thư mục trong cửa sổ project theo dạng ‘Android’ đấy bạn. Kiểu xem này không đúng với cấu trúc project thực tế, mà Android Studio giúp nhóm các resource lại với nhau theo group, chứ không phải theo thư mục đâu bạn nhé, khi này các resource bên trong mipmap/ hay mipmap-hdpi/, mipmap-xhdpi/,… sẽ bị gom lại thành một group mipmap/ic_launcher chẳng hạn rồi mở ngoặc ra thành (hdpi) hay (xhdpi). Nói chung kiểu view theo group này nên dành cho các dev đã rất rành structure của một project Android. Các bạn mới mới tiếp cận Android nên view theo dạng ‘Project’ sẽ dễ hiểu hơn.
em không hiêu tại sao alter-resources chỉ thêm -vi thôi mà hệ thống vẫn tự nhận diện được để đổi ngôn ngữ
anh có thể giải thích hộ e được không hoặc link đến bài viết (nếu có)
Bạn có thử xem qua bài viết cụ thể hơn về Alternative Resource của mình chưa: https://yellowcodebooks.com/2016/10/20/android-tat-tan-tat-ve-alternative-resource/