Drawable trong Android
Khái niệm Drawable
Drawable là khái niệm chung về đồ họa để chỉ những gì có thể vẽ, đơn giản là hình ảnh (Drawable hình ảnh trong Android gọi là BitmapDrawable).
Drawable định nghĩa shape, color, gradient, border, ... có thể sử dụng nó vào View trong Activity.
Trong Android, Drawable được sử dụng rất nhiều:
- Background của
View. - src của
ImageView. - State (press, normal, ...) của
View. - ...
Các loại Drawable trong Android
Có nhiều loại Drawable, sử dụng nhiều nhất là BitmapDrawable (đặt src cho ImageView):
- BimapDrawable
- ColorDrawable
- GradientDrawable
- ShapeDrawable
- RippleDrawable (Android 5.0)
- VectorDrawable
- AnimatedDrawable (Android 5.0)
- StateListDrawable
- 9 Paths Drawables
Ngoài những Drawable trên, có thể tạo Drawable khác kế thừa từ các Drawable trên (lớp cha của tất cả Drawable là lớp Drawable).
Sử dụng Drawable cho View
Có thể truy cập Drawable từ thư mục drawable của project như sau:
- Trong XML: sử dụng
@drawable/drawable_name. - Trong Java: sử dụng
R.drawable.drawable_name.
Ví dụ sử dụng Drawable để làm src cho ImageView trong XML:
<ImageView
android:src="@drawable/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />Và trong Java:
ImageView imvAvatar = (ImageView)findViewById(R.id.imv_avatar); imvAvatar.setImageResource(R.drawable.android);
Có thể tạo đối tượng Drawable trong Java và sử dụng các phương thức hỗ trợ đặt Drawable như sau:
ColorDrawable colorDrawable = new ColorDrawable(Color.RED); imvAvatar.setImageDrawable(colorDrawable);
Drawable bitmapDrawable = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
bitmapDrawable = getResources().getDrawable(R.drawable.android, getTheme());
else
bitmapDrawable = getResources().getDrawable(R.drawable.android);
if(bitmapDrawable != null)
imvAvatar.setBackground(bitmapDrawable);Tuy nhiên, phương thức setBackground() yêu cầu Android API 16, nên sử dụng các phương thức setDrawable() thông qua resource id. Ví dụ như phương thức setImageResouce(int resId) của ImageView.
1 số Drawable phổ biến
BitmapDrawable
Thực chất là 1 Bitmap nhưng Drawable có thể đặt nhiều thuộc tính, trước khi vẽ nên cho Drawable các giá trị như kích thước chiều rộng, chiều cao và alpha (độ trong suốt).
Để sử dụng BitmapDrawable, trong XML thêm hình ảnh vào thư mục Drawable.
Sau đó sử dụng @drawable/drawable_name để gán Drawable cho View:
Trong XML:
<TextView
android:id="@+id/tv_title"
android:drawableLeft="@drawable/facebook"
android:text="facebook"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />Trong Java:
// Set BitmapDrawable left for TextView BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),BitmapFactory.decodeResource(getResources(), R.drawable.facebook)); ((TextView)findViewById(R.id.tv_title)).setCompoundDrawablesWithIntrinsicBounds(bitmapDrawable, null, null, null);
ShapeDrawable
ShapeDrawable cho phép vẽ các hình học cơ bản trong Android, nhấn chuột phải vào thư mục drawable để tạo drawable resource:
Nhập tên drawable để hoàn thành:
Tạo file có tên là circle_drawable với nội dung file như dưới đây:
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#2980b9"/>
</shape>Và sử dụng trong XML:
<View
android:background="@drawable/circle_drawable"
android:layout_width="300dp"
android:layout_height="300dp"/>Bây giờ tạo 1 drawable có hình dạng như 1 row chat của messenger facebook như sau facebook_chat_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:topLeftRadius="20dp" android:bottomRightRadius="20dp" android:bottomLeftRadius="5dp" android:topRightRadius="5dp"/>
<padding android:left="5dp" android:right="5dp" android:top="10dp" android:bottom="10dp"/>
<solid android:color="#2980b9" />
</shape>Đặt Drawable là màu nền cho TextView:
<TextView
android:layout_margin="20dp"
android:id="@+id/tv_message_chat"
android:text="This is message"
android:textColor="@android:color/white"
android:background="@drawable/facebook_drawable_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />Trong Java có thể làm như sau:
findViewById(R.id.tv_message_chat).setBackgroundResource(R.drawable.facebook_drawable_chat);
Sửa lại 1 số thông tin trong file facebook_chat_drawable.xml và build lại project rồi xem kết quả:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="5dp" />
<padding
android:bottom="10dp"
android:left="5dp"
android:right="5dp"
android:top="10dp" />
<gradient
android:angle="90"
android:centerColor="#3498db"
android:endColor="#16a085"
android:startColor="#1abc9c" />
</shape>
StateListDrawable
StateListDrawable dùng để mô tả những state (normal, focus, press, disable, ...) của View, thường xuyên sử dụng cho Button và các item trên ListView hoặc RecyclerView.
Để tạo StateListDrawable cũng hoàn toàn tương tự như ShapeDrawable.
Tiến hành tạo Drawable trong thư mục Drawable có tên là button_selector.xml với nội dung như sau:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" >
<color android:color="#3498db"/>
</item>
<item android:state_pressed="true">
<color android:color="#2980b9"/>
</item>
</selector>Sử dụng Drawable cho Button:
<Button
android:background="@drawable/button_selector"
android:text="Save Change"
android:textColor="@android:color/white"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
button_state_normal.xml định nghĩa trạng thái bình thường (normal) của Button:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="30dp" />
<padding
android:bottom="5dp"
android:left="10dp"
android:right="10dp"
android:top="5dp" />
<solid android:color="#1abc9c" />
</shape>button_state_pressed.xml định nghĩa trạng thái nhấn (pressed) của Button:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="30dp" />
<padding
android:bottom="5dp"
android:left="10dp"
android:right="10dp"
android:top="5dp" />
<solid android:color="#16a085" />
</shape>button_selector_save_change.xml định nghĩa selector của button gồm 2 state: normal và pressed.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_state_normal" android:state_pressed="false" />
<item android:drawable="@drawable/button_state_pressed" android:state_pressed="true" />
</selector>Button sử dụng selector:
<Button
android:text="Save Change"
android:background="@drawable/selector_button_save_change"
android:textColor="@android:color/white"
android:layout_centerInParent="true"
android:layout_width="200dp"
android:layout_height="wrap_content" />Build và chạy chương trình xem kết quả có sự khác biệt gì không?
Ngoài 2 state normal và pressed trong Android, còn có nhiều state khác nữa, đa số các View trong Android đều có những state này:
VectorDrawable
VectorDrawable được giới thiệu ở Android 5.0 (API 21).
VectorDrawable có nhiều lợi thế như kích thước của file nhỏ so với việc sao chép 1 hình ảnh vào project, tự động scale theo tỉ lệ màn hình trên từng thiết bị.
Trước tiên cần enable support VectorDrawable trong tập tin build.gradle như sau:
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.example.nguyennghia.vectordrawable"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
// Enable for using vector drawables
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'com.android.support:design:24.0.0'
}Tiếp theo để tạo VectorDrawable, nhấp chuột phải vào thư mục drawable → New → Vector Asset:
Hộp thoại Vector Asset Studio hiện lên, nhấn Choose để chọn icon muốn xuất ra VectorDrawable, sau đó nhấn OK để Android Studio tạo tập tin drawable.
File vector được sinh ra có nội dung như sau:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8L11,8v6l4.75,2.85 0.75,-1.23 -4,-2.37L12.5,8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
</vector>Sử dụng thuộc tính srcCompat để gán VectorDrawable cho View trong XML:
<ImageView
android:layout_alignParentRight="true"
app:srcCompat="@drawable/ic_access_alarm_black_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />Làm lại và chạy thử để xem kết quả.
Custom Drawable
Ở trên đã giới thiệu những Drawable được giới thiệu trong Android SDK.
Ví dụ bên dưới sẽ vẽ cung tròn (ArcDrawable) lên Canvas.
Tạo lớp ArcDrawable kế thừa từ Drawable như sau:
package com.example.nguyennghia.drawabledemo;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
/**
* Created by nguyennghia on 7/25/16.
*/
public class ArcDrawable extends Drawable {
private Paint mPaint;
private RectF mRect;
public ArcDrawable(int color) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10);
}
@Override
public void draw(Canvas canvas) {
if (mRect == null)
mRect = new RectF();
mRect.left = getBounds().left + mPaint.getStrokeWidth() / 2;
mRect.top = getBounds().top + mPaint.getStrokeWidth() / 2;
mRect.right = getBounds().right - mPaint.getStrokeWidth() / 2;
mRect.bottom = getBounds().bottom - mPaint.getStrokeWidth() - 2;
canvas.drawArc(mRect, 45, 270, false, mPaint);
}
@Override
public void setAlpha(int alpha) {
if (mPaint != null)
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}Và sử dụng Drawable này như sau:
ImageView imvAvatar = (ImageView)findViewById(R.id.imv_avatar);
ArcDrawable arcDrawable = new ArcDrawable(Color.parseColor("#16a085"));
imvAvatar.setImageDrawable(arcDrawable);
Và dưới đây là hình ảnh của Drawable đã sử dụng trong bài viết từ đầu đến cuối: