Được chỉnh sửa ngày 21/7/2017.
Chào mừng các bạn đã đến với bài học Android thứ 22 trong chuỗi bài học lập trình ứng dụng Android của Yellow Code Books.
Bài hôm nay sẽ là bài học tiếp theo trong chuỗi bài về sử dụng ảnh drawable trong Android. Chúng ta hãy cùng nhau ôn lại xem cho đến bài học hôm nay, đã có tất cả bao nhiêu cách để có thể sử dụng ảnh dạng drawable nào.
– Ảnh bitmap – Là dạng drawable được tổ chức theo ma trận các điểm ảnh, các ảnh bitmap được Android hỗ trợ bao gồm PNG, JPG và GIF.
– Ảnh 9-Patch và ảnh Vector – Trong đó 9-Patch thì còn tận dụng lại từ PNG gốc rồi phát triển hơn. Còn Vector thì mang đến cho chúng ta một cách tổ chức và hiển thị ảnh hoàn toàn khác.
– Ảnh Shape XML và ảnh Layer List – Thì bài học hôm nay chúng ta sẽ nói đến.
Chú ý là các bài ví dụ hôm nay mình đều lấy source code hiện tại của TourNote ra để làm mẫu. Nếu bạn nào chưa có thực hành TourNote ngày nào thì có thể down source code về từ link này, hoặc bạn có thể tự tạo project mới hoàn toàn để kiểm chứng các dòng code của mình rồi cùng nhau bình luận nếu bạn tìm thấy vấn đề nào đó nhé.
Ảnh Shape XML
Có nhiều bạn gọi ảnh dạng này là Ảnh XML, có nơi chỉ gọi là Shape, nhưng để chính xác nhất mình nghĩ chúng ta nên thống nhất cách gọi là Shape XML.
Shape XML, như tên gọi của nó, ảnh dạng này cho phép bạn thiết kế bất cứ hình khối nào dựa trên XML. Sau đó khi ứng dụng được thực thi, hệ thống sẽ căn cứ vào thiết kế trên XML mà vẽ ảnh ra cho bạn.
Nghe qua có vẻ giống ảnh Vector lắm đúng không nào. Nhưng thực chất chúng chỉ giống nhau là dùng code để vẽ và hệ thống sẽ dựa vào đó mà hiện thực hình vẽ khi ứng dụng được chạy mà thôi. Những cái khác nhau thì nhiều. Như Shape XML dựa trên định nghĩa các hình khối, còn Vector thì có nhiều định nghĩa vô cùng phức tạp. Với Shape XML bạn có thể code các hình khối bằng tay, trong khi với Vector bạn phải dùng công cụ để vẽ, và các công cụ vẽ này sẽ tạo ra ảnh .svg hoặc .psd, rồi bạn phải dùng công cụ Vector Asset Studio để chuyển các ảnh này về lại định dạng .xml. Khác biệt nữa là vẽ ảnh bằng Shape XML không tốn CPU như với ảnh Vector (vì mình không thấy cảnh báo nào như vậy cho Shape XML từ Google cả).
Dựa trên so sánh nhỏ trên thì đủ để bạn biết rằng ảnh Shape XML rất tốt, chúng vừa giúp tốn ít không gian hệ thống để lưu trữ các ảnh bitmap theo kiểu alternative resource khác nhau. Chúng còn không tốn chi phí xử lý của CPU, tức thời gian để vẽ chúng lên màn hình khá nhanh. Khuyết điểm duy nhất của ảnh Shape XML này là độ khó và độ đa dạng của nó. Thường thì với Shape XML bạn chỉ vẽ được các hình khối đơn giản, như background có bo tròn cho Button này, background có viền xám xung quanh cho EditText này,…
Cách Vẽ Ảnh Shape XML
Với việc bạn có thể vẽ bao nhiêu ảnh Shape XML vào trong project của bạn là tùy thuộc vào sự sáng tạo và độ tỉ mẩn của bạn. Mình chỉ đưa công thức cụ thể và ví dụ một số trường hợp cho bạn thấy cách vẽ Shape XML là như thế nào thôi nhé, việc còn lại là ở bạn.
Công thức, hay cú pháp đầy đủ để vẽ ra một Shape XML như sau.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape=["rectangle" | "oval" | "line" | "ring"] > <corners android:radius="integer" android:topLeftRadius="integer" android:topRightRadius="integer" android:bottomLeftRadius="integer" android:bottomRightRadius="integer" /> <gradient android:angle="integer" android:centerX="float" android:centerY="float" android:centerColor="integer" android:endColor="color" android:gradientRadius="integer" android:startColor="color" android:type=["linear" | "radial" | "sweep"] android:useLevel=["true" | "false"] /> <padding android:left="integer" android:top="integer" android:right="integer" android:bottom="integer" /> <size android:width="integer" android:height="integer" /> <solid android:color="color" /> <stroke android:width="integer" android:color="color" android:dashWidth="integer" android:dashGap="integer" /> </shape>
Trên đây chỉ là công thức thôi, nó cho bạn xem các trường hợp tổng quát nhất có thể có để giúp bạn định nghĩa các hình khối, không nhất thiết bạn phải có đầy đủ hết các khai báo đã được nêu trong công thức. Và chắc chắn bạn nào chưa từng bao giờ sử dụng Shape XML sẽ hơi khó hiểu với công thức trên, vậy mình giúp bạn ở một số ý nhỏ gạch đầu dòng sau.
– Để bắt đầu vẽ một hình khối bằng Shape XML, bạn nhất định phải khai báo thẻ shape ở gốc. Sau đó, với thuộc tính android:shape của nó sẽ giúp chỉ định một trong các hình khối bạn muốn như sau. “rectangle” là hình chữ nhật, hình khối này là mặc định, có nghĩa là nếu bạn không khai báo thuộc tính này thì hệ thống sẽ hiểu hình khối đang khai báo này là hình chữ nhật. “oval” là hình oval, dĩ nhiên rồi. “line” sẽ vẽ một đường thẳng với chiều dài dãn ra rộng hết không gian chứa nó, loại hình line này cần đến định nghĩa stroke kèm theo sẽ được mình nói đến ở gạch đầu dòng bên dưới. “ring” sẽ vẽ một “vành” tròn được giới hạn bởi hai đường tròn làm biên, nếu bạn đã dùng shape là ring thì bạn phải định nghĩa thêm vài thuộc tính nữa ở thẻ này. Bao gồm: android:innerRadius hoặc android:innerRadiusRatio giúp dịnh nghĩa đường kính vòng tròn bên trong, một cái dùng độ lớn còn một cái dùng tỉ lệ để định nghĩa; android:thickness hoặc android:thicknessRatio giúp định nghĩa khoảng cách giữa hai đường tròn, một cái dùng độ lớn còn một cái dùng tỉ lệ để định nghĩa; android:useLevel cho biết ring này có dùng drawable dạng LevelListDrawable hay không, trong khuôn khổ bài học này bạn cứ để false cho thuộc tính này.
– Bên trong thẻ shape có các thẻ con, đầu tiên mình muốn nói đến thẻ corners. Thẻ này dùng để chỉ định độ bo tròn cho các góc của hình khối rectangle. Bạn có thể dễ dàng thử nghiệm các thuộc tính của corners này.
– Thẻ tiếp theo có thể có bên trong shape là thẻ gradient. Thẻ này rất thú vị, nó giúp bạn chỉ định nhiều nhất là ba điểm màu làm mốc, thông qua các thuộc tính android:startColor, android:centerColor và android:endColor. Sau đó nó sẽ giúp vẽ ra dãy màu biến thiên dần theo ba màu mốc đó. Thẻ này còn được kèm với các thuộc tính hữu dụng khác như android:type giúp bạn chỉ định kiểu gradient là “linear” – tuyến tính, “radial” – tròn như ra-đa, hay “sweep” – kiểu dẻ quạt. android:angle giúp chỉ định một số nguyên để định nghĩa góc xoay của dãy màu với type là linear. Hay android:gradientRadius giúp định nghĩa bán kính của hình tròn ra-đa.
– Thẻ padding và size giúp chỉ định độ lớn của biên và độ lớn của hình khối.
– Thẻ solid giúp tô màu “đặc” cho hình khối (ngược lại với cách tô màu biến thiên gradient trên kia). Do tô có một màu nên nó chỉ cần một thuộc tính android:color để chỉ định màu.
– Thẻ stroke chỉ định độ rộng cho đường biên của hình khối thông qua thuộc tính android:width. Ngoài ra thuộc tính android:color của nó giúp chỉ định màu cho đường biên. android:dashGap và android:dashWidth (nếu có định nghĩa) sẽ giúp chỉ định đường biên này có dạng đứt nét, các giá trị tương ứng của hai thuộc tính này sẽ giúp định nghĩa khoảng cách giữa hai nét đứt với nhau và độ dài của mỗi nét đứt.
Ví Dụ Vài Ảnh Shape XML
Dưới đây là một vài ảnh Shape XML mà bạn có thể tham khảo.
Một background bo tròn cho Button.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/colorAccent" /> <corners android:radius="5dp" /> <stroke android:width="2dp" android:color="@color/colorPrimary" /> </shape>
Một hình tròn mang phong cách mới của ứng dụng Skype.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <gradient android:angle="135" android:endColor="#FFFF00" android:startColor="#FFB000" android:type="linear" /> <size android:width="40dp" android:height="40dp" /> </shape>
Một vành tròn, có thể dùng làm icon nào đó trong ứng dụng.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring" android:innerRadiusRatio="6" android:thicknessRatio="4" android:useLevel="false"> <gradient android:endColor="@color/colorAccent" android:startColor="@color/colorPrimary" android:gradientRadius="15dp" android:centerX="0.5" android:centerY="0.5" android:type="radial" /> <size android:width="40dp" android:height="40dp" /> </shape>
Tổ Chức Ảnh Shape XML Trong res/
Tương tự như ảnh 9-Patch và ảnh Vector của bài học hôm trước, ảnh Shape XML hôm nay vẫn không cần thiết phải tổ chức theo thư mục alternative resource, vậy bạn chỉ cần đặt tất cả các Shape XML này vào một thư mục duy nhất là res/drawable/ là được.
Để tạo một ảnh Shape XML, bạn tìm đếm thư mục res/drawable/, nhấn phải chuột vào thư mục này và chọn New > Drawable resource file.
Popup nhỏ tiếp theo xuất hiện, bạn hãy đặt tên cho resource này (như bao cách đặt tên cho resource khác), và chỉ định thẻ gốc, trong trường hợp này thẻ gốc của chúng ta là shape nhé.
File bg_button.xml sau đó sẽ được tạo ra bên trong res/drawable/. Giờ thì bạn hãy thoải mái thực hành vẽ ra các hình khối như các ví dụ trên đây của mình được rồi đó. Kết quả code của bạn lên editor sẽ được hiển thị tức thời ở cửa sổ Preview.
Truy Xuất Đến Ảnh Shape XML
Bạn sử dụng ảnh Shape XML bình thường như khi sử dụng ảnh bitmap thôi. Dưới đây là đoạn code sử dụng Shape XML đã được vẽ ở ví dụ trên để làm background cho Button.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.yellowcode.tournote.MainActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@drawable/bg_button" android:padding="4dp" android:text="Test Button" /> </RelativeLayout>
Ảnh Layer List
Phần này mình xem như là phần mở rộng hơn của Shape XML trên đây thôi. Nếu như Shape XML chỉ giúp bạn vẽ các hình khối đơn giản, thì Layer List giúp kết hợp nhiều hình khối đơn giản đó lại với nhau để tạo thành một hình khối phức tạp hơn.
Cách Vẽ Ảnh Layer List
Như mình có nói, Layer List là sự kết hợp của nhiều Shape XML với nhau. Mỗi Shape XML như vậy được để trong một thẻ item. Các thẻ item này lại nằm trong một thẻ gốc là layer-list. Như cú pháp sau.
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:drawable="@[package:]drawable/drawable_resource" android:id="@[+][package:]id/resource_name" android:top="dimension" android:right="dimension" android:bottom="dimension" android:left="dimension" /> </layer-list>
Mình nói sơ qua các thành phần trong cú pháp.
– Thẻ layer-list là thẻ gốc, nó hầu như không có tác dụng gì ngoài việc chứa các thẻ item bên trong nó.
– Mỗi thẻ item sẽ có vài thuộc tính kèm theo để định nghĩa bổ sung cho thẻ shape bên trong nó. Các thuộc tính của thẻ item bao gồm. android:drawable giúp chỉ định một resource drawable khác thay cho việc dùng thẻ shape. android:id giúp tạo id cho thẻ item. android:top, android:bottom, android:left, android:right giúp chỉ định các khoảng cách so với các biên của item đó. android:width, android:height, android:gravity là các thuộc tính mà bạn đã quen thuộc từ các bài học trước.
Ví Dụ Vài Ảnh Layer List
Mình để ra đây vài ảnh Layer List, chủ yếu cho vui vui để bạn tham khảo. Khi vào thực tế chắc chắn bạn sẽ cần phải vắt óc suy nghĩ cách để kết hợp các hình khối như thế này. Tin mình đi, nó thú vị lắm.
Một hình trái tim
Hình trái tim này được tạo ra từ một hình thoi (hình chữ nhật xoay nghiêng), và hai hình oval.
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:bottom="21dp" android:left="32dp" android:right="32dp" android:top="37dp"> <rotate android:fromDegrees="45"> <shape> <solid android:color="@color/colorPrimary" /> <size android:width="100dp" android:height="100dp" /> </shape> </rotate> </item> <item android:bottom="52dp" android:right="68dp"> <shape android:shape="oval"> <solid android:color="@color/colorPrimary" /> </shape> </item> <item android:bottom="52dp" android:left="68dp"> <shape android:shape="oval"> <solid android:color="@color/colorPrimary" /> </shape> </item> </layer-list>
Một mô phỏng cho cái gương soi
?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="oval"> <solid android:color="@color/colorPrimaryDark" /> </shape> </item> <item android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp"> <shape android:shape="oval"> <solid android:color="@color/colorAccent" /> </shape> </item> <item android:bottom="20dp" android:left="20dp" android:right="20dp" android:top="20dp"> <shape android:shape="oval"> <solid android:color="@color/colorAccent" /> <stroke android:width="1.5dp" android:color="@color/colorPrimary" android:dashGap="2dp" android:dashWidth="4dp" /> </shape> </item> </layer-list>
Pin cho map
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:width="20dp" android:height="20dp" android:left="6dp" android:top="28dp"> <rotate android:fromDegrees="45" android:pivotX="75%" android:pivotY="40%"> <shape android:shape="rectangle"> <solid android:color="@color/colorPrimary" /> </shape> </rotate> </item> <item android:width="32dp" android:height="40dp" android:bottom="8dp"> <shape> <solid android:color="@color/colorPrimaryDark" /> <corners android:radius="4dp" /> </shape> </item> <item android:width="32dp" android:height="26dp" android:bottom="4dp" android:top="16dp"> <shape> <solid android:color="@color/colorPrimary" /> <corners android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp" /> </shape> </item> </layer-list>
Tổ Chức Ảnh Layer List Trong res/
Phần này tương tự như tổ chức ảnh Shape XML trên kia nhé.
Truy Xuất Đến Ảnh Layer List
Cũng tương tự như truy xuất đến ảnh Shape XML luôn.
Vậy là chúng ta đã đi qua cách sử dụng một dạng resource drawable mới mẻ, bằng cách kết hợp các hình khối lại với nhau. Cách sử dụng resource kiểu này của Android theo mình đánh giá là tương đối thú vị. Bài tiếp theo sẽ là một trong những bài chốt lại chuỗi bài về resource drawable này.
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
Bài tiếp theo chúng ta sẽ xem qua cách sử dụng Color State List, Drawable State List, và các ảnh drawable còn lại.
One Reply to “Android Bài 22: Sử Dụng Drawable – Ảnh Shape XML & Ảnh Layer List”