+2

Canvas trong Android (Phần 2)

Giới thiệu

  • Tiếp nối bài viết Canvas trong Android (Phần 1) thì bài viết hôm nay tôi sẽ giới thiệu vẽ những thành phần còn lại còn lại lên canvas đó là vẽ Bitmap và vẽ Text chuyên sâu. Và sau đó là giới thiệu những các phép biến đổi cơ bản trên canvas (translate, rotate, scale) và cách sử dụng save, restore canvas trong Android.

Vẽ các đối tượng hình ảnh, text lên canvas

Vẽ Bitmap

  • Các phương thức dùng để vẽ bitmap lên Canvas
drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)
  • Vẽ bitmap lên canvas có apply matrix.
drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)
  • Vẽ bitmap lên canvas với các đối số là

bitmap: source bitmap dùng để vẽ lên canvas

src: là hình chữ nhật cắt bitmap để vẽ. Ví dụ bạn muốn gắt một phần nào đó của bitmap để vẽ chứ không muốn vẽ toàn bộ hình ảnh. Trường hợp vẽ toàn hình ảnh sẽ truyền vào null.

dst: là hình chữ nhật mô tả toạ độ để vẽ lên canvas.

Lưu ý: Kiểu dữ liệu của các thuộc tính của dst đều là số thực float (left, top, right, bottom).

drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint)
  • 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, float left, float top, @Nullable Paint paint)
  • Vẽ bitmap bắt đầu ở vị trí left và top xác định trên màn hình. Và điểm vẽ tính từ góc trái trên của bitmap.
  • Ví dụ mình sẽ vẽ một bitmap ở dưới giữa màn hình như sau: Trước tiên chúng ta sẽ chuẩn bị 1 bitmap để vẽ:
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
  • Trong method onDraw ta hiện thực như sau:
       //Draw Bitmap
        float left = (getWidth() - mBitmap.getWidth()) / 2.0f;
        float top = getHeight() - mBitmap.getHeight();
        canvas.drawBitmap(mBitmap, left, top, mPaint);

alt

Bây giờ tôi muốn cắt 1/4 góc trái bên của bitmap và vẽ nó ở right-bottom của màn hình:

        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);

alt

Ví dụ cuối cùng về vẽ bitmap là tôi sẽ vẽ một bitmap full (không có cắt xén) và vẽ nó ở chính giữa 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);

alt

  • Vậy là chúng ta đã đi qua xong phần vẽ bitmap lên canvas. Và dưới đây là một phần khá hứng thú đã là vẽ text lên canvas.

Vẽ text

  • Việc vẽ text được sử dụng khá nhiều trong Android. Vì với bất kì ứng dụng nào, bạn cũng thấy text có mặt bất cứ ở đâu. Và việc vẽ những đoạn text phức tạp cũng sẽ được đề cập ở ngay phần dưới đây.

  • Với việc vẽ text Android có cung cấp cho chúng ta class có tên là TextPaint kế thừa từ Paint giúp chúng ta vẽ text lên canvas mạnh mẽ hơn. Và khi và text tôi khuyến khích các bạn nên sử dụng TextPaint thay vì sử dụng Paint.

Trong method initPaint() đã viết ở phần 1 tôi 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

  • Các phương thức để vẽ text
drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
  • Vẽ string với vị trí bắt đầu vẽ x, y.
drawText(@NonNull String text, int start, int end, float x, float y, @NonNull Paint paint)
  • Vẽ String với các đối số start, end xác định vẽ từ vị trí nào tới vị trí nào của string với vị trí bắt đầu x, y.
drawText(@NonNull char[] text, int index, int count, float x, float y, @NonNull Paint paint)
  • Vẽ một mảng char, bắt đầu với index và số lượng phần tử cần vẽ và vẽ ở vị trí x, y.
drawText(@NonNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint)
  • Vẽ CharSequence với vị trí bắt đầu và kết thúc ở vị trí x, y.

Ví dụ tôi muốn vẽ chuỗi “Demo Android” ở vị trí x = 100, y = 500:

        float x = 100;
        float y = 400;
        canvas.drawText("Eitguide Android", x, y, mTextPaint);
  • Trường hợp đoạn text 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.” Và tôi muốn hiển thị chuỗi “…” khi chuỗi chạm mép của view thì tôi làm như sau:
  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);

alt

Tiếp theo chúng ta sẽ tìm hiểu các vẽ text phức tạp hơn đó là trong 1 text chúng ta có thể vẽ như sau.

  • Tôi muốn vẽ text đủ các style như hình dưới đây:

alt

Đoạn text đầu tiên tôi cho in đậm text Đoạn tiếp theo sẽ có background màu đỏ Đoạn tiết có foreground màu xanh Đoạn tiếp nữa có style chữ in nghiêng Và đoạn cuối sẽ là chữ bình thường và gạch chân

  • Và làm sao chúng ta làm được chuyện đó. Giải pháp là chúng ta sử dụng StaticLayout.
        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);
  • Với việc sử dụng Spanable để setSpan cho CharSequence chúng ta có thể set bất cứ style nào cho text. Và sử dụng StaticLayout để vẽ lên cavas.

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

  • Canvas cho chúng ta thực hiện các phép biến đổi hình học như translate, scale, rotate để giúp chúng ta vẽ các đối tượng một cách linh hoạt hơn.

Translate

  • Thực hiện dịch chuyển canvas một đoạn dx, và dy:
translate(float dx, float dy)
  • Việc translate canvas được hiểu như là thay đổi gốc toạ độ để vẻ. Mặt định góc toạ độ sẽ nằm ở gốc trái trên của View. Sau khi tịnh tiến một khoảng dx, dy thì gốc toạ độ sẽ nằm ở dx, và dy.

  • Ví dụ khi bạn chưa translate canvas và bạn vẽ một điểm ở vị trí (x, y) = (200, 400) sẽ có kết quả giống với

  • kết quả bạn translate canvas một đoạn dx = 200, dy = 400 và ngay sau đó vẽ một điểm tại vị trí (x, y) = (0, 0).

Scale.

  • Scale là phép biến đổi thay đổi kích thước của vật thể. Trương trường hợp này 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 scale sx, sy có giá trị 1. Nếu chúng ta set 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à kích thước lớn hơn chiều y là 4 lần

Rotate.

  • Rotate là phép biển đổi xoay đối tượng theo một góc xác định.
	void rotate(float degrees);
  • Ví dụ khi muốn xoay các đối tượng vẽ 45 độ. Ta set dễ dàng như sau:
	canvas.rotate(45f);

Save và Restore Canvas

  • Thử set trường hợp tôi muốn vẽ hai đối tượng. Tôi muốn vẽ đối tượng thứ nhất lớn hơn đối tượng thứ đố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 tôi vẽ y nguyên giống hình dạng của nó không có scale. Các bước là như sau:

Bước 1:

  • Set Scale cho canvas sx, sy = 4;
canvas.setScale(4, 4);

Bước 2:

  • Vẽ đối tượng thứ nhất

Bước 3:

  • Scale canvas về lại trạng thái ban đầu
canvas.setScale(-4, -4)

Bước 4:

  • Vẽ đối tượng thứ 2

Bước 5:

  • Trước khi bạn 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 đã save trạng thái thì bạn cứ thoải mái sử dụng các phép biến đổi canvas.

Restore Canvas

  • Gọi canvas.Restore(); để đưa canvas về trạng thái nó đã save trước đó.

Lưu ý. canvas.Restore() chỉ có thể gọi được nếu bạn đã call canvas.Save();. Nếu bạn cố tình gọi canvas.Restore() mà chưa gọi canvas.Save() ở trước thì Android sẽ ném ra ngoại lệ.

Tài liệu tham khảo : https://developer.android.com/reference/android/graphics/Canvas.html Exampe tham khảo sử dụng canvas trong view: https://examples.javacodegeeks.com/android/core/graphics/canvas-graphics/android-canvas-example/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí