Search…

Canvas trong Android - Phần 2

Nguyễn NghĩaNguyễn Nghĩa
13/11/20207 min read
Vẽ bitmap và text chuyên sâu với Canvas trên Android cùng với những phép biến đổi cơ bản trên Canvas như translate, rotate, scale, cách sử dụng lưu, khôi phục Canvas trong Android.

Vẽ các đối tượng bitmap lên Canvas

Vẽ bitmap

Vẽ bitmap lên Canvas có áp dụng ma trận:

drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)

Vẽ bitmap lên Canvas với các đối số:

drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)
  • bitmap: source bitmap dùng để vẽ lên Canvas.
  • src: hình chữ nhật cắt bitmap để vẽ.
    • Ví dụ muốn bớt 1 phần của bitmap để vẽ thay vì vẽ toàn bộ hình ảnh.
    • Trường hợp vẽ toàn hình ảnh sẽ truyền vào null.
  • dst: hình chữ nhật mô tả tọa độ và khung hiển thị để vẽ lên Canvas.
    • Kiểu dữ liệu của các thuộc tính trong dst (left, top, right, bottom) là số thực float.

Giống như phương thức trên nhưng kiểu dữ liệu của các thuộc tính dst đều kiểu nguyên (left, top, right, bottom):

drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint)

Vẽ bitmap bắt đầu ở vị trí lefttop xác định trên màn hình, điểm vẽ tính từ góc trái trên của bitmap:

drawBitmap(@NonNull Bitmap bitmapfloat leftfloat top, @Nullable Paint paint)

Ví dụ vẽ bitmap lên Canvas

Chuẩn bị bitmap để vẽ:

mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);

Vẽ bitmap ở center-bottom màn hình

Trong phương thức onDraw() hiện thực như sau:

float left = (getWidth() - mBitmap.getWidth()) / 2.0f;
float top = getHeight() - mBitmap.getHeight();
canvas.drawBitmap(mBitmap, left, top, mPaint);

Kết quả:

Vẽ 1/4 bitmap ở right-bottom tính từ tâm của bitmap

Trong phương thức onDraw() hiện thực như sau:

Rect src = new Rect(0, 0, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);
int left = getWidth() - src.width();
int top = getHeight() - src.height();
Rect des = new Rect(left, top, left + src.width(), top + src.height());
canvas.drawBitmap(mBitmap, src, des, mPaint);

Vẽ bitmap tại vị trí center-middle màn hình

float left = (getWidth() - mBitmap.getWidth()) / 2.0f;
float top = (getHeight() - mBitmap.getHeight()) / 2.0f;
RectF dst = new RectF(left, top, left + mBitmap.getWidth(), top + mBitmap.getHeight());
canvas.drawBitmap(mBitmap, null, dst, mPaint);

Vẽ các đối tượng text lên Canvas

Vẽ text

Với bất kỳ ứng dụng nào trong Android cũng có văn bản hay những đoạn văn phức tạp. Android cung cấp lớp có tên là TextPaint được kế thừa từ Paint giúp vẽ text lên Canvas tốt hơn v àTextPaint được khuyến khích sử dụng thay vì sử dụng Paint.

Trong phương thức initPaint() thêm vào phần khởi tạo TextPaint sử dụng để vẽ text:

private void initPaint() {
  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mPaint.setColor(Color.BLUE);
  mPaint.setStrokeWidth(30);
  mPaint.setStyle(Paint.Style.STROKE);
  mPaint.setStrokeCap(Paint.Cap.ROUND);
  mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
  mTextPaint.setColor(Color.parseColor("#16a085"));
  mTextPaint.setTextSize(50);
}

Vẽ text thông thường

Vẽ chuỗi ký tự với tọa độ bắt đầu vẽ x, y:

drawText(@NonNull String textfloat xfloat y, @NonNull Paint paint)

Vẽ chuỗi ký tự với start, end là vẽ từ ký tự bắt đầu tại vị trí start đến ký tự kết thúc ở vị trí end và x, y là tọa độ bắt đầu vẽ:

drawText(@NonNull String text, int start, int end, float x, float y, @NonNull Paint paint)

Vẽ 1 mảng char[], bắt đầu từ ký tự tại vị trí index và số lượng phần tử cần vẽ count, và vẽ ở tọa độ x, y.

drawText(@NonNull char[] text, int index, int count, float x, float y, @NonNull Paint paint)

Vẽ CharSequence với start, end là vẽ từ ký tự bắt đầu tại vị trí start đến ký tự kết thúc ở vị trí end và x, y là tọa độ bắt đầu vẽ:

drawText(@NonNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint)

Ví dụ vẽ text lên Canvas

Ví dụ vẽ chuỗi "Eitguide Android" ở vị trí x = 100, y = 400:

float x = 100;
float y = 400;
canvas.drawText("Eitguide Android", x, y, mTextPaint);

Sử dụng tiếp chuỗi "Eitguide Android" nhưng chỉ vẽ “guide Androi":

float x = 100;
float y = 400;
String str = "Eitguide Android";
canvas.drawText(str, 3, str.length() - 1, x, y, mTextPaint);

Trường hợp chuỗi quá dài so với View, ví dụ chuỗi "We can actually use any custom font that we'd like within our applications.", chỉ hiển thị chuỗi "..." khi chuỗi chạm mép của View:

float x = 0;
float y = 400;
CharSequence str = TextUtils.ellipsize("We can actually use any custom font that we'd like within our applications.",
mTextPaint, getWidth(), TextUtils.TruncateAt.END);
canvas.drawText(str, 0, str.length(), x, y, mTextPaint);

Vẽ text với Spannable

Vẽ text đầy đủ các định dạng với Spannable trong Android như sau:

  • Đoạn chuỗi đầu tiên được in đậm.
  • Đoạn chuỗi tiếp theo có màu nền đỏ.
  • Đoạn chuỗi tiếp theo có chữ màu xanh.
  • Đoạn chuỗi tiếp theo in nghiêng.
  • Đoạn chuỗi cuối được gạch chân.

Với việc sử dụng Spannable để setSpan() cho CharSequence có thể đặt bất cứ kiểu nào cho chuỗi và sử dụng StaticLayout để vẽ lên Canvas:

float x = 0;
float y = 400;
CharSequence str = TextUtils.ellipsize("We can actually use any custom font that we'd like within our applications.", mTextPaint, getWidth(), TextUtils.TruncateAt.END);
SpannableString wordToSpan = new SpannableString(str);
wordToSpan.setSpan(new StyleSpan(Typeface.BOLD), 0, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
wordToSpan.setSpan(new BackgroundColorSpan(Color.RED), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
wordToSpan.setSpan(new ForegroundColorSpan(Color.CYAN), 15, 20, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordToSpan.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 15, 25, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
wordToSpan.setSpan(new UnderlineSpan(), 25, wordToSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
StaticLayout staticLayout = new StaticLayout(wordToSpan, mTextPaint, getWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
staticLayout.draw(canvas);

Các tính năng nâng cao của Canvas trong Android

Các phép biến đổi hình học

Canvas có các phép biến đổi hình học như translate, scale, rotate để giúp vẽ các đối tượng 1 cách linh hoạt hơn.

Translate

Translate thực hiện tịnh tiến (dịch chuyển) gốc tọa độ Canvas 1 đoạn dx và dy:

translate(float dx, float dy)

Mặc định gốc toạ độ nằm ở gốc trái-trên của View, sau khi tịnh tiến 1 khoảng dx, dy thì gốc toạ độ sẽ nằm ở dxdy.

Ví dụ khi chưa dịch chuyển và vẽ 1 điểm ở vị trí (x, y) = (200, 400) sẽ có kết quả dx = 200, dy = 400 và sau đó vẽ 1 điểm tại vị trí (x, y) = (0, 0).

Scale

Scale là phép biến đổi tỷ lệ (thay đổi kích thước) của vật thể, nghĩa là thay đổi kích thước của các đối tượng vẽ trên Canvas:

scale(float sx, float sy)

Mặc định tỷ lệ sx, sy có giá trị 1. Nếu đặt sx = 2, sy = 4 thì đối tượng sẽ có kích thước theo chiều x lớn hơn 2 lần và theo chiều y lớn hơn 4 lần.

Rotate

Rotate là phép biển đổi xoay đối tượng theo 1 góc xác định:

void rotate(float degrees)

Ví dụ muốn xoay các đối tượng vẽ 45 độ:

canvas.rotate(45);

Save và Restore Canvas

Đặt trường hợp muốn vẽ 2 đối tượng:

  • Vẽ đối tượng thứ 1 lớn hơn đối tượng đối tượng gốc của nó hơn 4 lần cả chiều x và chiều y.
  • Vẽ đối tượng thứ 2 giống hình dạng của nó không có scale.

Các bước thực hiện như sau:

  • Đặt Canvas với sx = 4, sy = 4: canvas.setScale(4, 4)
  • Vẽ đối tượng thứ nhất.
  • Về lại trạng thái ban đầu: canvas.setScale(-4, -4)
  • Vẽ đối tượng thứ hai.

Mỗi khi thay đổi trạng thái của Canvas (translate, scale, rotate) muốn đưa về trạng thái cũ thì phải nhớ những thông số làm thay đổi trạng thái và đặt ngược lại giá trị đó để đưa Canvas về trạng thái cũ. Trường hợp tịnh tiến và phép quay thì sử dụng đổi dấu của các thông số, trường hợp phép tỷ lệ thì lấy nghịch đảo của thông số (1/scaleX, 1/scaleY).

Save Canvas

Trước khi thay đổi trạng thái của Canvas gọi canvas.Save() để lưu lại trạng thái hiện tại của Canvas. Khi đã lưu trạng thái thì có thể tự do sử dụng các phép biến đổi Canvas.

Restore Canvas

Gọi canvas.Restore() để đưa Canvas về trạng thái được lưu trước đó.

canvas.Restore() chỉ có thể gọi được nếu đã gọi canvas.Save(), nếu vẫn gọi canvas.Restore() mà chưa gọi canvas.Save() ở trước thì Android sẽ ném ra ngoại lệ.

Bài chung series

IO Stream

IO Stream Co., Ltd

developer@iostream.co
383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024