Khi cần hiển thị hình ảnh tĩnh trong ứng dụng, bạn có thể sử dụng lớp Drawable
và các lớp con của lớp này để vẽ hình dạng và
hình ảnh. Drawable
là khái niệm trừu tượng chung cho
nội dung nào đó có thể vẽ được. Các lớp con khác nhau giúp hỗ trợ hình ảnh cụ thể
tình huống và bạn có thể mở rộng chúng để xác định các đối tượng có thể vẽ của riêng mình
hoạt động theo những cách độc đáo.
Ngoài việc sử dụng hàm khởi tạo lớp, còn có hai cách để xác định và tạo thực thể cho Drawable
:
- Tăng cường tài nguyên hình ảnh (tệp bitmap) đã lưu trong dự án.
- Tăng cường một tài nguyên XML xác định các thuộc tính có thể vẽ.
Lưu ý: Thay vào đó, bạn có thể thích sử dụng vectơ vẽ được. Vectơ này xác định hình ảnh bằng một tập hợp các điểm, đường kẻ và đường cong, cùng với thông tin màu sắc có liên quan. Thao tác này cho phép các vectơ vẽ được có thể điều chỉnh cho phù hợp với nhiều kích thước mà không làm giảm chất lượng. Để biết thêm thông tin, hãy xem Vector tổng quan về đối tượng có thể vẽ.
Tạo đối tượng có thể vẽ từ hình ảnh tài nguyên
Bạn có thể thêm đồ hoạ vào ứng dụng bằng cách tham chiếu một tệp hình ảnh trên nguồn dự án. Các loại tệp được hỗ trợ là PNG (ưu tiên), JPG (có thể chấp nhận), và GIF (không nên chọn). Biểu tượng ứng dụng, biểu trưng và các đồ hoạ khác, chẳng hạn như đồ hoạ được sử dụng trong trò chơi rất phù hợp với kỹ thuật này.
Để sử dụng tài nguyên hình ảnh, hãy thêm tệp của bạn vào res/drawable/
của dự án. Sau khi ở trong dự án, bạn có thể tham chiếu hình ảnh
từ mã hoặc bố cục XML của bạn. Dù bằng cách nào, mã này đều được tham chiếu đến việc sử dụng
mã nhận dạng tài nguyên, tức là tên tệp không có đuôi tệp. Cho
ví dụ: tham chiếu my_image.png
là my_image
.
Lưu ý: Tài nguyên hình ảnh được đặt trong
Thư mục res/drawable/
có thể được tự động tối ưu hoá bằng
nén hình ảnh không tổn hao bằng công cụ aapt
trong quá trình tạo bản dựng
của chúng tôi. Ví dụ: PNG màu thực không yêu cầu quá 256 màu
có thể được chuyển đổi thành tệp PNG 8 bit với bảng màu. Kết quả là một hình ảnh
có chất lượng tương đương nhưng cần ít bộ nhớ hơn. Do đó, tệp nhị phân hình ảnh
được đặt trong thư mục này có thể thay đổi tại thời điểm xây dựng. Nếu bạn định đọc một
hình ảnh dưới dạng luồng bit để chuyển đổi hình ảnh đó thành bitmap, hãy đặt hình ảnh của bạn vào
Thay vào đó, thư mục res/raw/
, trong đó công cụ aapt
không có
sửa đổi chúng.
Đoạn mã sau đây minh hoạ cách tạo ImageView
sử dụng
hình ảnh được tạo từ một tài nguyên có thể vẽ rồi thêm vào bố cục:
Kotlin
private lateinit var constraintLayout: ConstraintLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Instantiate an ImageView and define its properties val i = ImageView(this).apply { setImageResource(R.drawable.my_image) contentDescription = resources.getString(R.string.my_image_desc) // set the ImageView bounds to match the Drawable's dimensions adjustViewBounds = true layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) } // Create a ConstraintLayout in which to add the ImageView constraintLayout = ConstraintLayout(this).apply { // Add the ImageView to the layout. addView(i) } // Set the layout as the content view. setContentView(constraintLayout) }
Java
ConstraintLayout constraintLayout; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a ConstraintLayout in which to add the ImageView constraintLayout = new ConstraintLayout(this); // Instantiate an ImageView and define its properties ImageView i = new ImageView(this); i.setImageResource(R.drawable.my_image); i.setContentDescription(getResources().getString(R.string.my_image_desc)); // set the ImageView bounds to match the Drawable's dimensions i.setAdjustViewBounds(true); i.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // Add the ImageView to the layout and set the layout as the content view. constraintLayout.addView(i); setContentView(constraintLayout); }
Trong các trường hợp khác, bạn có thể cần xử lý tài nguyên hình ảnh dưới dạng đối tượng Drawable
, như minh hoạ sau đây
ví dụ:
Kotlin
val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)
Java
Resources res = context.getResources(); Drawable myImage = ResourcesCompat.getDrawable(res, R.drawable.my_image, null);
Cảnh báo: Mỗi tài nguyên duy nhất trong dự án của bạn
chỉ có thể duy trì một trạng thái, bất kể bạn có bao nhiêu đối tượng khác nhau
tạo thực thể cho nó. Ví dụ: nếu bạn tạo thực thể cho 2 đối tượng Drawable
từ cùng một tài nguyên hình ảnh và
thay đổi thuộc tính (chẳng hạn như alpha) cho một đối tượng, thì thao tác này cũng ảnh hưởng đến
ứng dụng khác. Khi xử lý nhiều thực thể của một tài nguyên hình ảnh,
Trong quá trình chuyển đổi trực tiếp đối tượng Drawable
, bạn nên thực hiện
tween
ảnh động.
Đoạn mã XML dưới đây cho biết cách thêm một tài nguyên có thể vẽ vào ImageView
trong bố cục XML:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" android:contentDescription="@string/my_image_desc" />
Để biết thêm thông tin về cách sử dụng tài nguyên của dự án, hãy xem bài viết Tài nguyên và thành phần.
Lưu ý: Khi sử dụng tài nguyên hình ảnh làm nguồn cho đối tượng có thể vẽ, hãy đảm bảo hình ảnh có kích thước thích hợp cho nhiều mật độ pixel khác nhau. Nếu hình ảnh không chính xác thì chúng sẽ được điều chỉnh theo tỷ lệ cho phù hợp, điều này có thể gây ra cấu phần phần mềm trong đối tượng có thể vẽ của bạn. Để biết thêm thông tin, hãy đọc bài viết Hỗ trợ các mật độ pixel.
Tạo đối tượng có thể vẽ từ tài nguyên XML
Nếu có Drawable
mà bạn muốn tạo không phụ thuộc vào các biến do
mã hoặc tương tác của người dùng, thì việc xác định Drawable
trong XML là một lựa chọn tốt. Đồng đều
nếu bạn muốn Drawable
thay đổi thuộc tính trong quá trình người dùng tương tác
với ứng dụng của mình, bạn nên cân nhắc định nghĩa đối tượng trong XML, vì bạn có thể sửa đổi các thuộc tính sau
đã tạo thực thể cho đối tượng.
Sau khi bạn xác định Drawable
trong XML, hãy lưu tệp vào
Thư mục res/drawable/
của dự án. Ví dụ sau đây trình bày tệp XML
xác định một
TransitionDrawable
tài nguyên kế thừa từ Drawable
:
<!-- res/drawable/expand_collapse.xml --> <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/image_expand"/> <item android:drawable="@drawable/image_collapse"/> </transition>
Sau đó, truy xuất và tạo thực thể cho đối tượng bằng cách gọi
Resources#getDrawable()
và chuyển mã nhận dạng tài nguyên của tệp XML. Bất kỳ hạng nào
Lớp con Drawable
có hỗ trợ phương thức inflate()
có thể được xác định trong XML và tạo thực thể
theo ứng dụng của bạn.
Mỗi lớp có thể vẽ hỗ trợ tăng cường XML sử dụng các thuộc tính XML cụ thể
giúp xác định các thuộc tính của đối tượng. Đoạn mã sau đây sẽ tạo thực thể cho
TransitionDrawable
và đặt tệp đó làm nội dung của
Đối tượng ImageView
:
Kotlin
val transition= ResourcesCompat.getDrawable( context.resources, R.drawable.expand_collapse, null ) as TransitionDrawable val image: ImageView = findViewById(R.id.toggle_image) image.setImageDrawable(transition) // Description of the initial state that the drawable represents. image.contentDescription = resources.getString(R.string.collapsed) // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000) // After the transition is complete, change the image's content description // to reflect the new state.
Java
Resources res = context.getResources(); TransitionDrawable transition = (TransitionDrawable) ResourcesCompat.getDrawable(res, R.drawable.expand_collapse, null); ImageView image = (ImageView) findViewById(R.id.toggle_image); image.setImageDrawable(transition); // Description of the initial state that the drawable represents. image.setContentDescription(getResources().getString(R.string.collapsed)); // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000); // After the transition is complete, change the image's content description // to reflect the new state.
Để biết thêm thông tin về các thuộc tính XML được hỗ trợ, hãy tham khảo các lớp được liệt kê ở trên.
Hình dạng có thể vẽ
Đối tượng ShapeDrawable
có thể là một lựa chọn phù hợp
khi bạn muốn vẽ đồ hoạ hai chiều một cách linh động. Bạn có thể
vẽ các hình dạng gốc theo phương thức lập trình trên đối tượng ShapeDrawable
và áp dụng kiểu mà ứng dụng của bạn cần.
ShapeDrawable
là lớp con của Drawable
. Vì lý do này, bạn có thể sử dụng
ShapeDrawable
ở bất cứ nơi nào dự kiến có Drawable
. Cho
ví dụ: bạn có thể dùng đối tượng ShapeDrawable
để đặt nền
của một khung hiển thị bằng cách truyền khung hiển thị đó vào phương thức setBackgroundDrawable()
của khung hiển thị đó. Bạn cũng có thể vẽ hình dạng
của riêng bạn và thêm khung hiển thị đó vào bố cục trong ứng dụng.
Vì ShapeDrawable
có phương thức draw()
riêng, nên bạn có thể tạo một phương thức
lớp con của View
vẽ ShapeDrawable
trong quá trình diễn ra sự kiện onDraw()
, như minh hoạ trong
ví dụ về mã sau đây:
Kotlin
class CustomDrawableView(context: Context) : View(context) { private val drawable: ShapeDrawable = run { val x = 10 val y = 10 val width = 300 val height = 50 contentDescription = context.resources.getString(R.string.my_view_desc) ShapeDrawable(OvalShape()).apply { // If the color isn't set, the shape uses black as the default. paint.color = 0xff74AC23.toInt() // If the bounds aren't set, the shape can't be drawn. setBounds(x, y, x + width, y + height) } } override fun onDraw(canvas: Canvas) { drawable.draw(canvas) } }
Java
public class CustomDrawableView extends View { private ShapeDrawable drawable; public CustomDrawableView(Context context) { super(context); int x = 10; int y = 10; int width = 300; int height = 50; setContentDescription(context.getResources().getString( R.string.my_view_desc)); drawable = new ShapeDrawable(new OvalShape()); // If the color isn't set, the shape uses black as the default. drawable.getPaint().setColor(0xff74AC23); // If the bounds aren't set, the shape can't be drawn. drawable.setBounds(x, y, x + width, y + height); } protected void onDraw(Canvas canvas) { drawable.draw(canvas); } }
Bạn có thể dùng lớp CustomDrawableView
trong mã mẫu
ở trên giống như cách bạn sử dụng bất kỳ khung hiển thị tuỳ chỉnh nào khác. Ví dụ: bạn có thể
thêm phương thức lập trình vào một hoạt động trong ứng dụng, như minh hoạ sau
ví dụ:
Kotlin
private lateinit var customDrawableView: CustomDrawableView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customDrawableView = CustomDrawableView(this) setContentView(customDrawableView) }
Java
CustomDrawableView customDrawableView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); customDrawableView = new CustomDrawableView(this); setContentView(customDrawableView); }
Nếu bạn muốn sử dụng thành phần hiển thị tuỳ chỉnh trong bố cục XML, thì
Lớp CustomDrawableView
phải ghi đè hàm khởi tạo View(Context, AttributeSet)
. Hàm này sẽ được gọi khi lớp này được
được tăng cường từ XML. Ví dụ sau đây trình bày cách khai báo
CustomDrawableView
trong bố cục XML:
<com.example.shapedrawable.CustomDrawableView android:layout_width="fill_parent" android:layout_height="wrap_content" />
Lớp ShapeDrawable
, giống như nhiều lớp khác
các loại đối tượng có thể vẽ trong gói android.graphics.drawable
, cho phép bạn
xác định các thuộc tính khác nhau của đối tượng bằng cách sử dụng các phương thức công khai. Một số ví dụ
các thuộc tính bạn có thể muốn điều chỉnh bao gồm độ trong suốt alpha, bộ lọc màu,
chuyển màu, độ mờ và màu sắc.
Bạn cũng có thể xác định các hình dạng gốc có thể vẽ bằng tài nguyên XML. Để biết thêm thông tin, hãy xem Chọn hình dạng cho đối tượng có thể vẽ trong Các loại tài nguyên có thể vẽ.
Đối tượng có thể vẽ 9Patch
Hình ảnh NinePatchDrawable
là
hình ảnh bitmap có thể co giãn mà bạn có thể dùng làm nền của khung hiển thị. của Android
tự động đổi kích thước đồ hoạ cho phù hợp với nội dung trong chế độ xem. Một
ví dụ về cách sử dụng hình ảnh NinePatch làm nền mà Android tiêu chuẩn sử dụng
nút—các nút phải co giãn để vừa với các chuỗi có độ dài khác nhau. Đáp
Hình ảnh NinePatch là hình ảnh PNG chuẩn có đường viền phụ 1 pixel.
Bạn phải lưu hình ảnh với đuôi 9.png
trong
Thư mục res/drawable/
của dự án.
Sử dụng đường viền để xác định các vùng tĩnh và có thể co giãn của hình ảnh. Bạn chỉ định phần có thể co giãn bằng cách vẽ một (hoặc nhiều) chiều rộng 1 pixel (các) đường màu đen ở phần bên trái và phần trên của đường viền (các pixel đường viền khác phải hoàn toàn trong suốt hoặc có màu trắng). Bạn có thể tạo nhiều phần co giãn như bạn muốn. Kích thước tương đối của các phần co giãn vẫn giữ nguyên, do đó phần lớn nhất luôn là phần lớn nhất.
Bạn cũng có thể xác định một phần có thể vẽ tuỳ chọn của hình ảnh (một cách hiệu quả,
các đường đệm) bằng cách vẽ một đường ở bên phải và một đường ở dưới cùng. Nếu một
Đối tượng View
đặt đồ hoạ NinePatch làm nền
sau đó chỉ định văn bản của thành phần hiển thị, nó sẽ tự kéo giãn để tất cả văn bản
chỉ chiếm vùng được chỉ định bởi các dòng bên phải và dưới cùng (nếu có).
Nếu không bao gồm các dòng khoảng đệm, Android sẽ sử dụng các dòng bên trái và trên cùng để
xác định vùng có thể vẽ này.
Để làm rõ sự khác biệt giữa các dòng, dòng bên trái và dòng trên cùng xác định pixel nào của hình ảnh được phép sao chép để kéo dài hình ảnh. Các đường dưới cùng và đường bên phải xác định vùng tương đối trong hình ảnh nội dung của khung hiển thị được phép chiếm.
Hình 1 là ví dụ về thành phần đồ hoạ NinePatch dùng để xác định một nút:
Hình ảnh NinePatch này xác định một vùng có thể co giãn gồm phần bên trái và trên cùng và vùng có thể vẽ cùng với các đường dưới cùng và bên phải. Ở hình ảnh trên cùng, các đường chấm màu xám xác định các vùng hình ảnh được sao chép để kéo dài hình ảnh. Hình chữ nhật màu hồng trong hình ảnh dưới cùng xác định khu vực cho phép nội dung của khung hiển thị. Nếu nội dung không vừa với vùng này, thì hình ảnh sẽ được kéo giãn cho vừa khít.
Công cụ Vẽ 9-patch mang đến là một cách cực kỳ tiện lợi để tạo hình ảnh NinePatch bằng đồ hoạ WYSIWYG trình chỉnh sửa. Tính năng này thậm chí còn đưa ra cảnh báo nếu khu vực bạn đã xác định cho vùng có thể co giãn có nguy cơ tạo ra các thành phần lạ của bản vẽ do pixel nhân bản.
Tệp XML bố cục mẫu sau đây minh hoạ cách thêm một thành phần đồ hoạ NinePatch
vào một vài nút. Hình ảnh NinePatch được lưu vào
res/drawable/my_button_background.9.png
.
<Button android:id="@+id/tiny" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerInParent="true" android:text="Tiny" android:textSize="8sp" android:background="@drawable/my_button_background"/> <Button android:id="@+id/big" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:text="Biiiiiiig text!" android:textSize="30sp" android:background="@drawable/my_button_background"/>
Xin lưu ý rằng layout_width
và layout_height
các thuộc tính được thiết lập thành wrap_content
để nút này trông gọn gàng
xung quanh văn bản.
Hình 2 cho thấy 2 nút được kết xuất từ hình ảnh XML và NinePatch hiển thị ở trên. Hãy lưu ý chiều rộng và chiều cao của nút thay đổi theo văn bản như thế nào. và hình nền sẽ kéo giãn để phù hợp với kích thước này.
Đối tượng có thể vẽ tuỳ chỉnh
Khi muốn tạo một số bản vẽ tuỳ chỉnh, bạn có thể thực hiện bằng cách mở rộng lớp Drawable
(hoặc bất kỳ lớp con nào của lớp này).
Phương thức quan trọng nhất để triển khai là draw(Canvas)
vì hàm này cung cấp đối tượng Canvas
mà bạn phải sử dụng để cung cấp
hướng dẫn vẽ của bạn.
Mã sau đây cho thấy một lớp con đơn giản của Drawable
để vẽ một đường tròn:
Kotlin
class MyDrawable : Drawable() { private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) } override fun draw(canvas: Canvas) { // Get the drawable's bounds val width: Int = bounds.width() val height: Int = bounds.height() val radius: Float = Math.min(width, height).toFloat() / 2f // Draw a red circle in the center canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint) } override fun setAlpha(alpha: Int) { // This method is required } override fun setColorFilter(colorFilter: ColorFilter?) { // This method is required } override fun getOpacity(): Int = // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE PixelFormat.OPAQUE }
Java
public class MyDrawable extends Drawable { private final Paint redPaint; public MyDrawable() { // Set up color and text size redPaint = new Paint(); redPaint.setARGB(255, 255, 0, 0); } @Override public void draw(Canvas canvas) { // Get the drawable's bounds int width = getBounds().width(); int height = getBounds().height(); float radius = Math.min(width, height) / 2; // Draw a red circle in the center canvas.drawCircle(width/2, height/2, radius, redPaint); } @Override public void setAlpha(int alpha) { // This method is required } @Override public void setColorFilter(ColorFilter colorFilter) { // This method is required } @Override public int getOpacity() { // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE return PixelFormat.OPAQUE; } }
Sau đó, bạn có thể thêm đối tượng có thể vẽ ở bất cứ nơi nào bạn muốn, chẳng hạn như vào
ImageView
như minh hoạ dưới đây:
Kotlin
val myDrawing = MyDrawable() val image: ImageView = findViewById(R.id.imageView) image.setImageDrawable(myDrawing) image.contentDescription = resources.getString(R.string.my_image_desc)
Java
MyDrawable mydrawing = new MyDrawable(); ImageView image = findViewById(R.id.imageView); image.setImageDrawable(mydrawing); image.setContentDescription(getResources().getString(R.string.my_image_desc));
Trên Android 7.0 (API cấp 24) trở lên, bạn cũng có thể xác định các thực thể của đối tượng có thể vẽ tuỳ chỉnh với XML theo những cách sau:
- Dùng tên lớp đủ điều kiện làm tên phần tử XML. Đối với phương pháp này, phương pháp tuỳ chỉnh
lớp có thể vẽ phải là lớp cấp cao nhất công khai:
<com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ffff0000" />
- Dùng
drawable
làm tên thẻ XML và chỉ định lớp đủ điều kiện tên khỏi thuộc tính lớp. Phương pháp này có thể được dùng cho cả lớp công khai cấp cao nhất và các lớp tĩnh bên trong công khai:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
Thêm sắc thái màu vào đối tượng có thể vẽ
Với Android 5.0 (API cấp 21) trở lên, bạn có thể phủ màu bitmap và 9-patch được xác định là
mặt nạ alpha. Bạn có thể phủ màu cho chúng bằng tài nguyên màu sắc hoặc thuộc tính giao diện phân giải thành màu
(ví dụ: ?android:attr/colorPrimary
). Thông thường, bạn tạo các nội dung này
chỉ một lần và tự động tô màu chúng cho phù hợp với chủ đề của bạn.
Bạn có thể phủ màu cho BitmapDrawable
, NinePatchDrawable
hoặc VectorDrawable
bằng phương thức setTint()
. Bạn có thể
bạn cũng có thể đặt chế độ và màu phủ màu trong bố cục bằng android:tint
và
Thuộc tính android:tintMode
.
Trích xuất màu nổi bật từ hình ảnh
Thư viện hỗ trợ Android có lớp Palette
, cho phép bạn trích xuất các màu nổi bật từ một hình ảnh.
Bạn có thể tải đối tượng có thể vẽ dưới dạng Bitmap
rồi truyền đối tượng đó đến Palette
để truy cập vào các màu.
Để biết thêm thông tin, hãy đọc phần Chọn màu bằng
Palette API.