Android Bài 23: Sử Dụng Drawable – Ảnh State List & Các Ảnh Drawable Còn Lại

Posted by

Chào mừng các bạn đến với bài học Android thứ 23, bài học về sử dụng drawable. Đây là bài học trong chuỗi bài về lập trình ứng dụng Android của Yellow Code Books.

Hôm nay chúng ta cùng nhau chốt lại các cách sử dụng đến resource drawable trong Android. Phải nói là có khá nhiều cách để bạn linh hoạt sử dụng, nào là dùng ảnh bitmap này, dùng ảnh 9-Patch hay ảnh Vector này, rồi bạn còn có thể dùng đến XML để mà vẽ các hình khối, và bài hôm nay còn nói đến cách dùng State List và một vài cách khác nữa. Dù rằng cách dùng drawable dạng ảnh bitmap là phổ biến nhất, nhưng các cách dùng khác của drawable vẫn đôi khi phát huy tác dụng của nó ở một số trường hợp. Khả năng mà một project nào đó có dùng đến tất cả các dạng drawable mà mình nêu trên đây là hoàn toàn tồn tại đấy nhé. Vì vậy mà mình mới cất công viết nhiều bài như vậy cho bạn tham khảo.

Bài học hôm nay dùng rất nhiều ảnh động để minh họa, do các hiệu ứng từ bài học mang lại khá là đẹp. Do đó bạn nên cân nhắc đọc bài viết này ở nơi có wifi tốt tốt nhé.

Mời các bạn cùng bắt đầu với drawable State List.

State List Là Gì?

Mình nói thiệt là câu hỏi này rất quan trọng đấy, nếu bạn là người mới toanh đang đang từng bước học lập trình Android, thì bạn sẽ tiết kiệm được kha khá thời gian lục tìm trên mạng kiến thức của bài hôm nay, và bạn không ngờ rằng từ khóa để bạn tìm thấy thứ bạn cần chính là “State List”.

Để mình dẫn dắt từ từ. Bạn biết rằng bất cứ giao diện của ứng dụng nào cũng cần có Button đúng không nào. Bạn hãy thử xây dựng một Button đi, nhưng đừng set background hay src gì cho Button đó cả, chỉ dùng màu mặc định từ hệ thống thôi, khi này nếu bạn thực thi ứng dụng và nhấn vào Button đó, bạn sẽ thấy một hiệu ứng lún xuống của Button, nó báo cho bạn biết rằng Button này đã nhận lệnh. Như này.

Sử dụng drawable - Mô phỏng hiệu ứng button

Giờ thì bạn hãy tiến hành set background hay src cho nó, bằng bất cứ thể loại resource drawable nào mà bạn đã biết. Kết quả sau đó thực thi ứng dụng lên thì sao… Button với background hay src do bạn chỉ định đó, nó không có hiệu ứng lún như Button mặc định, nó đơ đơ sao á!?!

Bạn đã từng gặp vấn đề như vậy chưa. Mình có nhớ khi mới làm quen với Android, cũng với tình huống này, mình tìm trên mạng thế nào mà lại ra một chỉ dẫn từ một developer ở một đất nước nào đó rằng, bạn nên dùng Java code để check trạng thái của Button và set background tương ứng. Wow như vậy thì chuối quá!

Thực tế thì không cần tìm đâu xa, Google có hướng dẫn chúng ta kỹ càng, chỉ là không biết đường tìm mà thôi. Dù sao thì hôm nay, khi viết lại bài này để chia sẻ cách thức xây dựng Button (hay bất kỳ widget nào) có được sự phản hồi lại user, mình vẫn nhớ như in cái cảm giác tìm tòi ban đầu đó. Vừa buồn cười vừa thú vị.

State List chính là chìa khóa để bạn tạo ra các Trạng thái khác nhau cho các widget đó bằng cách định nghĩa vào một resource drawable như các bài học trước bạn đã làm quen. Sau đó khi ứng dụng được thực thi, tùy vào ngữ cảnh lúc đó mà hệ thống sẽ quyết định dùng đến Trạng thái tương ứng cho widget mà bạn đã khai báo.

Có hai dạng State List mà bạn phải làm quen, đó là Drawable State ListColor State List. Cách sử dụng của chúng là như nhau. Chúng ta cùng tìm hiểu chúng ở các mục dưới đây.

Drawable State List

Trước hết mời bạn nhìn vào công thức sau để biết các cách có thể có để định nghĩ một Drawable State List.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize=["true" | "false"]
    android:dither=["true" | "false"]
    android:variablePadding=["true" | "false"] >
    <item
        android:drawable="@[package:]drawable/drawable_resource"
        android:state_pressed=["true" | "false"]
        android:state_focused=["true" | "false"]
        android:state_hovered=["true" | "false"]
        android:state_selected=["true" | "false"]
        android:state_checkable=["true" | "false"]
        android:state_checked=["true" | "false"]
        android:state_enabled=["true" | "false"]
        android:state_activated=["true" | "false"]
        android:state_window_focused=["true" | "false"] />
</selector>

Trong cấu trúc này.

– Thẻ gốc của định nghĩa một Drawable State List chính là thẻ selector. Thẻ này có tác dụng chính là chứa các trạng thái của widget bên trong nó.

– Mỗi thẻ item tiếp theo sẽ chứa đựng các trạng thái. Bạn sẽ chỉ định một hay một vài trạng thái cho item, và một resource drawable tương ứng cho item đó. Mỗi trạng thái chỉ có một trong hai giá trị là “true” hay “false”. Một số trạng thái điển hình được mình liệt kê như sau.

android:state_pressed, định nghĩa trạng thái khi widget đó có được nhấn (touch) hay click không.

android:state_focused, định nghĩa trạng thái khi widget đó có được focus vào không, như là với EditText ấy, khi user click vào EditText, thì khi đó trạng thái focus được kích hoạt.

android:state_hovered, định nghĩa trạng thái khi con trỏ chuột có “lướt” qua Widget hay không, dùng cho một số thiết bị Android có điều khiển bằng trỏ chuột, trạng thái này nhiều khi được hành xử giống với state_focused.

android:state_selected, định nghĩa trạng thái khi dùng với cần gạt hay các console điều khiển. Khi các widget này được chọn đến sẽ được hệ thống thực thi trạng thái này.

android:state_checkable, định nghĩa trạng thái khi mà widget đó được set là cho phép check hay không. Bạn nên hiểu là chúng ta dùng trạng thái này để biểu diễn một widget không cho user được check và một widget cho check là như thế nào. Còn để biểu diễn các trạng thái khi check và uncheck là các trạng thái dưới đây.

android:state_checked, định nghĩa trạng thái check hay uncheck của widget, vừa được nhắc đến bên trên.

android:state_enabled, tương tự như state_checkable, state_enabled này dùng để biểu diễn một widget có đang cho phép tương tác hay không.

android:state_activated, định nghĩa trạng thái widget đó đang được kích hoạt hay không.

android:state_window_focused, định nghĩa trạng thái khi mà cửa sổ chứa đựng widget này có được focus hay không. Bạn nên biết là dù cho cửa sổ nào đó đang được hiển thị, thì không có nghĩa là nó đang được focus. Chẳng hạn như khi user đang tương tác với một cửa sổ, mà một notification show lên, sẽ làm cửa sổ đó đi vào trạng thái không được focus dù là vẫn đang được nhìn thấy bởi user.

android:drawable, đây không phải là một trạng thái, như mình có nhắc đến ở gạch đầu dòng trên kia, đây là nơi bạn khai báo resource drawable tương ứng cho mỗi item.

Tổ Chức Drawable State List Trong res/

Cũng tương tự như với resource Shape XML, Drawable State List cũng được để trong res/drawable/.

Để tạo một Drawable State List, tương tự như các drawable trước, bạn click chuột phải vào thư mục res/drawable/ và chọn New > Drawable resource file. Hộp thoại xuất hiện tiếp theo bạn vẫn đặt tên cho resource và khai báo thẻ gốc là selector.

Sử dụng drawable - Tạo drawable state list

Ví Dụ Khai Báo Drawable State List

Mình biết lý thuyết trên đây hơi khó hiểu. Vậy chúng ta hãy thử cùng xây dựng một trạng thái hoàn hảo cho Button ở ví dụ đầu bài học nào.

Để có đầy đủ các trạng thái cần thiết, thì trước tiên mình tạo ra một background bo tròn màu xám, dành cho Button bị disable. Background này không gì tốt hơn là sử dụng Shape XML từ bài trước. Bạn có thể đặt tên resource background này là button_disable.xml.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="@android:color/darker_gray" />

    <corners android:radius="5dp" />

</shape>

Sau đó mình tạo background dành cho khi Button được nhấn. Mình đặt tên là button_pressed.xml.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="@color/colorPrimaryDark" />

    <corners android:radius="5dp" />

</shape>

Rồi đến background bình thường dành cho khi Button không bị tác động gì khác. Mình đặt tên là button_normal.xml.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="@color/colorPrimary" />

    <corners android:radius="5dp" />

</shape>

Bạn có thể thấy là việc định nghĩa ra ba trạng thái cho Button trên đây của mình chỉ khác nhau chỗ màu sắc thôi đúng không nào. Nếu bạn không dùng TourNote để thực hành, thì các định nghĩa màu như trên có thể sẽ không có, và khi đó bạn có thể chỉ định một mã màu nào đó bất kỳ trực tiếp vào khai báo.

Khâu quan trọng tiếp theo chúng ta sẽ định nghĩa một Drawable State List. Bạn hãy áp dụng ý mà mình nói ở mục trên đây để tạo ra một Drawable State List có tên là button.xml trong thư mục res/drawable/. Trong trường hợp bạn chưa biết cách tạo các resource dạng này thì có thể tham khảo lại bài học trước của mình nhé.

button.xml có nội dung như sau.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/button_pressed" android:state_pressed="true" />

    <item android:drawable="@drawable/button_disable" android:state_enabled="false" />

    <item android:drawable="@drawable/button_normal" />
    
</selector>

Bạn xem, trong file XML trên, bên trong thẻ selector mình có định nghĩa ba item cho Button. item với state_pressed=”true” mình chỉ định resource drawable là button_pressed vừa tạo trên kia, định nghĩa này có nghĩa là khi Button nhận sự kiện click thì resource drawable này được hệ thống tự động hiển thị. Sau đó, item với state_enable=”false” mình chỉ định resource drawable là button_disable vừa tạo trên kia, định nghĩa này có nghĩa là khi Button bị disable thì resource drawable này được hiển thị. Cuối cùng là item đặc biệt, mình không chỉ định trạng thái gì cho nó cả, chỉ khai báo resource drawable là button_normal, định nghĩa này có nghĩa là tất cả các trạng thái còn lại, bao gồm cả trạng thái mà Button không bị tác động gì cả, đều sử dụng resource drawable này.

Truy Xuất Đến Drawable State List

Việc truy xuất đến resource này vẫn giống với truy xuất đến resource Shape XML từ bài trước. Dưới đây là kết quả thử nghiệm các trạng thái của Button, bao gồm cả disable (dựa vào một CheckBox để enable/disable Button này). Bạn thử code đi nhé, đơn giản mà đúng không. 😉

Sử dụng drawable - Mô phỏng drawable state list button

Color State List

Thực ra phần Color State List này không nằm trong khuôn khổ các bài viết về sử dụng resource drawable, vì nó không liên quan đến thư mục này. Vậy vì sao mình nói đến resource này hôm nay? Thứ nhất, vì mình đã hứa với các bạn từ bài 17 – Bài học về Color, rằng Color State List sẽ được nói sau. Thứ hai, Color State List khá giống với Drawable State List về cách sử dụng cũng như cách tổ chức. Chỉ khác nhau ở chỗ một thằng thì dùng resource color, còn một thằng thì dùng resource drawable để biểu diễn các trạng thái. Khác nhau nữa đó là cách tổ chức chúng trong các thư mục res/, chúng ta cùng xem sơ qua Color State List nhé.

Về mặt công thức, thì Color State List không khác mấy so với Drawable State List. Do đó bạn có thể xem sơ qua mà không cần mình giải thích chi tiết.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:color="hex_color"
        android:state_pressed=["true" | "false"]
        android:state_focused=["true" | "false"]
        android:state_selected=["true" | "false"]
        android:state_checkable=["true" | "false"]
        android:state_checked=["true" | "false"]
        android:state_enabled=["true" | "false"]
        android:state_window_focused=["true" | "false"] />
</selector>

Tổ Chức Color State List Trong res/

Khác với Drawable State List ở trên, Color State List phải được tổ chức trong thư mục res/color/.

Để tạo một Color State List, bạn phải tạo ra thư mục color/ bên trong res/ trước. Thư mục color/ này cũng đã được mình nhắc một chút khi liệt kê tất cả các thư mục có thể có bên trong res/bài 8 rồi đó nhé, hi vọng bạn còn nhớ.

Sau khi có thư mục color/ rồi thì bạn click chuột phải vào nó và chọn New > Color resource file.

Sử dụng drawable - Tạo color state list

Hộp thoại xuất hiện tiếp theo bạn hãy đặt tên cho resource Color State List này. File resource được tạo ra sau bước này sẽ có sẵn thẻ gốc là selector cho bạn.

Sử dụng drawable - Đặt tên khi tạo color state list

Ví Dụ Khai Báo Color State List

Mình lấy lại ví dụ trên, nhưng khi này chúng ta sẽ tập trung vào tạo hiệu ứng cho màu text của Button. Lần này chúng ta làm đơn giản hơn nhiều do không phải khai báo các resource drawable, vì tất cả chỉ là màu sắc.

Bạn xem source code cho button_color.xml như sau.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:color="@android:color/darker_gray" android:state_pressed="true" />

    <item android:color="@android:color/black" android:state_enabled="false" />

    <item android:color="@android:color/white" />

</selector>

Bạn đã thấy các trạng thái ở Color State List như ví dụ trên không khác gì so với Drawable State List cả. Chỉ có điều chúng ta sử dụng android:color thay cho android:drawable trên kia mà thôi.

Truy Xuất Đến Color State List

Bạn truy xuất đến chúng giống như truy xuất đến resource color vậy, với XML thì bạn dùng @color/tên_color_state_list, còn với Java code thì bạn dùng R.color.tên_color_state_list.

Kết quả cho việc set button_color vào cho thuộc tính textColor của Button như sau. Bạn có thể thấy hiệu ứng rõ nhất là text ở trạng thái disable, nếu như bình thường không khai báo gì cả thì text này luôn là màu trắng, còn với ví dụ này bạn đã dùng màu đen để thay thế. Bạn có thể tự thử nghiệm.

Sử dụng drawable - Mô phỏng sử dụng color state list button

Các Resource Drawable Còn Lại

Chúng ta đã điểm qua các ảnh dạng drawable phổ biến từ các bài học trước và bài học hôm nay rồi, mục này mình sẽ điểm qua các resource drawable còn lại ít sử dụng hơn, nhưng biết đâu ở đâu đó bạn cần đến chúng.

Ảnh Dạng Level List

Ảnh này cũng na ná với Layer List hay State List mà chúng ta đã biết. Có một điều khác nhau giữa chúng là ảnh Level List giúp chỉ định các level cho từng item. Sau đó khi sử dụng ảnh, chúng ta sẽ dùng đến hàm setLevel() để hệ thống xác định loại ảnh nào cần được load.

Ảnh dạng này thích hợp cho các biểu diễn cần đến các cấp độ, như biểu diễn mức pin còn lại của hệ thống chẳng hạn.

Đây là link gốc cho bạn tham khảo.

Minh họa sau mình dùng đến nhiều ảnh để định nghĩa ra Level List, trong đó mình sử dụng thêm một SeekBar giúp giả lập điều khiển các level tương ứng cho ảnh.

Sử dụng drawable - Mô phỏng sử dụng level list

Ảnh Transition

Ảnh này cũng tương tự các ảnh kiểu List trên kia. Nhưng với ảnh này bạn chỉ được định nghĩa tối đa hai item trong một thẻ gốc transition. Sau đó khi vẽ ảnh này lên, hệ thống sẽ tạo hiệu ứng chuyển dần từ item 1 sang item 2.

Ảnh dạng này thích hợp cho các loại widget có hai trạng thái, và bạn muốn các trạng thái này được chuyển đổi giao diện qua lại có kèm hiệu ứng như thế này.

Bạn có thể xem thêm ở link gốc này.

Ví dụ sau cho bạn thấy việc chuyển đổi giữa hai ảnh kèm hiệu ứng.

Sử dụng drawable - Mô phỏng sử dụng transition

Ảnh Inset

Ảnh này đơn giản lắm, mình không cần hình minh họa. Ảnh này giúp bạn chỉ định một ảnh drawable bên trong thẻ inset, và chỉ định các giá trị inset cho nó. Dựa vào đó, hệ thống sẽ vẽ ảnh nhỏ hơn ảnh thực tế dựa trên các giá trị inset này.

Ảnh dạng này thích hợp cho bạn muốn vẽ một background cho widget nào đó mà không muốn background đó chiếm hết không gian của widget đó. Chắc bạn đang tưởng tượng tới các thông số padding hay margin của widget? Ồ, insetpadding/margin, chúng không giống nhau như bạn tưởng đâu nhé.

Bạn có thể thử vẽ một ảnh Inset như link gốc này, và so sánh với cách sử dụng các thuộc tính paddingmargin xem nào.

Ảnh Clip

Bạn có thể đoán ra đây là định nghĩa tạo ra một ảnh bị cắt xén so với ảnh gốc. Vâng bạn đã đoán gần đúng. Thực tế thì ảnh dạng này vừa hỗ trợ bạn cắt xén, vừa hỗ trợ các tùy chọn cắt xén như hướng cắt, phần nào bị cắt, sau đó bạn có thể chỉ định tỉ lệ cắt bằng việc set level cho nó. Ảnh dạng này thích hợp cho các hiệu ứng loading mà bạn bắt gặp đâu đó ở splash screen của một số ứng dụng.

Bạn có thể xem cách sử dụng ở link gốc này.

Còn đây là minh họa của mình, mình lại dùng một SeekBar để điều khiển level clip của ảnh.

Sử dụng drawable - Mô phỏng sử dụng clip

Trên đây là các kiến thức còn lại cuối cùng về cách sử dụng các resource dạng drawable. Hi vọng qua loạt bài học này, bạn sẽ có đầy đủ kiến thức, cũng như các ý tưởng để có thể tạo ra các sản phẩm đẹp-độc-lạ cho thị trường các sản phẩm Android từ cộng đồng các lập trình viên người Việt giỏi giang chúng ta. Vì chung quy lại, sản phẩm có đẹp hay không, bên cạnh màu sắc và các bố trí hợp lý các thành phần UI, thì các resource drawable cũng đóng vai trò quan trọng, quan trọng cả cho bộ mặt sản phẩm, và cả cho hiệu năng của sản phẩm nữa.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy đánh giá 5 sao nếu thấy thích bài viết, hãy comment bên dưới nếu có thắc mắc, hãy để lại địa chỉ email của bạn để nhận được thông báo mới nhất khi có bài viết mới, và nhớ chia sẻ các bài viết của Yellow Code Books đến nhiều người khác nữa nhé.

Bài Kế Tiếp

Chúng ta sẽ làm quen với khái niệm và cách sử dụng Action Bar. Action Bar chính là thanh title của ứng dụng mà bạn đang thấy, bạn sẽ được biết cách thiết kế thêm các thành phần lên trên cái thanh còn khá nhạt nhẽo và trống trải này.

Advertisements
Rating: 5.0/5. From 8 votes.
Please wait...

7 comments

  1. bài viết hay quá, sao không post bài tiếp đi Ad, đang chờ những bài tiếp theo mong Ad tiếp tục post bài tiếp

    1. Hihi xin lỗi các bạn nhiều, dạo này mình bận quá, tuần này mình viết tiếp bài nữa của Android nhé 😉

Gửi phản hồi