Custom view trong Android
Bài đăng này đã không được cập nhật trong 6 năm
Giới thiệu
Mỗi ngày chúng ta đều sử dụng rất nhiều ứng dụng khác nhau, hầu hết chúng khá giống nhau thậm chí là giống nhau trong cả thiết kế. Đó là lý do mà tại sao nhiều khách hàng yêu cầu những ứng dụng cần có thiết kế giao diện riêng, tùy chỉnh làm sao cho ứng dụng Android của họ trở nên độc đáo và tương phản với những ứng dụng còn lại.
Nếu một tính năng cụ thể đòi hỏi một giao diện tùy chỉnh mà không được cung cấp sẵn bởi Android (gốc). Điều này dẫn đến chúng ta cần thiết kế lại giao diện sao cho bám sát với bản thiết kế, làm như vậy sẽ khá mất thời gian giành cho việc thiết kế giao diện của ứng dụng. Nhưng như thế không có nghĩa là chúng ta (các lập trình viên) sẽ không làm mà tôi khuyên bạn nên làm điều đó, làm như vậy mang lại tính độc đáo cho ứng dụng của khách hàng và hơn nữa việc tùy chỉnh lại giao diện (custom view) mang lại rất nhiều điều thú vị.
Gần đây mình đã gặp phải một tính huống tương tự như vậy, đó là tùy chỉnh 1 page indicator
cho Android ViewPager
sao cho khi chuyển trang thì sẽ hiển thị các chấm tròn ở dưới để người dùng biết là họ đang xem "page" nào. Không giống như IOS thì Android không cung cấp sẵn 1 View
để làm điều này, và việc tôi cần làm là tủy chỉnh lại Page indicator
sao cho giống với thiết kế của khách hàng.
Nếu bạn chưa thực hiện việc tùy chỉnh giao diện (Custom View) bao giờ thì bài viết này mình sẽ giới thiệu tới các bạn một cách chi tiết để làm thể nào có thể thực hiện 1 Custom View
được tối ưu nhất.
Đầu tiên mình sẽ giới thiệu với các bạn sơ qua về vòng đời của 1 View
. Vì 1 lý do nào đó mà Google
không cung cấp chính xác điều này, nó dẫn đến tương đối nhiều sự hiểu lầm giữa các lập trình viên và sainh ra nhiều lỗi khá là ngớ ngẩn. Vậy nên hãy nhớ kỹ về điều này.
Sau đây mình sẽ giỡi thiệu với các bạn về chi tiết tứng phần trong vòng đời 1 của View.
1. Constructor
Một vòng đời của mọi View
đều bắt đầu từ Constructor. Ở đây chúng ta có thể làm rất nhiều việc như chuẩn bị 1 bản vẽ 2D (Canvas), khỏi tạo các giá trị mặc định ban đầu, thực hiện 1 số điều kiện nhất định hoặc bất cứ điều gì bạn muốn...
Để cho View
dễ dàng thiết lập và sử dụng thì ở đây chúng ta nên quan tâm đến AttributeSet interface
. Nó giúp chúng ta lấy được các thông số mà chúng ta đã set cho View ở ngoài file .xml
Đầu tiên hãy tạo 1 file attrs.xml
nơi đây sẽ đinh nghĩ tất cả các thuộc tính cho toàn bộ Custom view
trong dự án của bạn. Như trong ví dụ trên mình cần tạo 1 Custom view tên là PageIndicatorView
và Customview này cần 1 thuộc tính tên là piv_count
để thiết lập số page count cho trước.
Sau đó trong Constructor của PageIndicatorView mình sẽ lấy giá trị của thuộc tính này từ file thiết kế layout.
public PageIndicatorView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.PageIndicatorView);
int count = typedArray.getInt(R.styleable.PageIndicatorView_piv_count,0);
typedArray.recycle();
}
Lưu ý:
- Khi tạo 1 thuộc tính cho
Custom View
thì bạn nên đặt 1 tiền tố cho nó để tránh nhầm lẫn giữa các thuộc tính của cácCustom View
khác nhau trong dự án. - Sau khi bạn hoàn thành việc lấy dữ liệu từ file thiết kế layout bạn bạn nên gọi hàm
recycle()
để giải gióng bộ nhớ.
2. onAttachedToWindow
Sau khi View Parent gọi hàm addView(View)
thì View sẽ được gắn vào 1 cửa sổ để hiển thị lên màn hình điện thoại. Ở giai đoạn này sau khi View được gắn lên 1 Windowns
là lúc chúng ta sẽ biết được vị trí của View nằm ở đâu trong file thiết kế giao diện layout.xml
, và thời điểm này là thời điểm tốt để bạn có thể lấy các Id của các View khác cùng nằm trong file layout.xml
và lưu lại nếu sau này cần để sử dụng.
3. onMeasure
Đây là lúc mà View sẽ tính toán chiều cao, độ rộng cần thiết để View hiển thị. Ở đây bạn có thể thiết lập các thuộc tính cao, rộng cho View làm sao cho phù hợp nhất với bốc cục của giao diện.
Khi ghi đề phương thức này thì sau khi tính toán lại width và height
cho View thì bạn cần gọi hàm setMeasuredDimension(int width, int height)
để thiết lập lại các thông số width, height
.
4. onLayout
Phương thức này để gán thêm các View con vào View hiện tại.
Như PageIndicatorView
mình cần thiết kế ở trên thì mình không cần thiết ghi đè phương thức này.
5. onDraw
Đây là nơi phép thuật xảy ra. Có cả đối tượng Canvas và Paint sẽ cho phép bạn vẽ bất kỳ thứ gì bạn cần.
Một đối tương Canvas
được cung cấp thông qua parameter
của hàm onDraw
nó là nền 2D cơ bản để bạn có thể vẽ các hình dạng khác nhau, trong khi đó đối tượng Paint
được dùng để xác định hình dạng và màu sắc của đối tượng cần vẽ. Về cơn bản thì Canvas
là tờ giấy trắng còn Paint
là cây bút, bạn muốn thể hiện hình gì thì chỉ cần cầm cây bút vẽ lên tờ giấy trắng là xong.
Trong khi View
được hiển thị lên màn hình thì hàm onDraw
sẽ được gọi nhiều lần, vì lý do đó mà bạn không nê khởi tạo đối tượng mới trong hàm onDraw
mà nên khởi tạo đối tượng toàn cục rồi sử dụng lại trong hàm này bằng cách thay đổi thuộc tính của đối tượng đó.
Lưu ý:
- Trong khi thực hiện luôn ghi nhớ là tái sử dụng các đối tượng toàn cục vì IDE không thể
recommend
bạn nếu bạn gọi 1 hàm trong hàmonDraw
. - Không nên
hard code
kích thước củaView
trong hàm onDraw. Cần xử lý tính toán các kích thước của
View` phụ thuộc vào đối tượng bạn cần vẽ.
6. View Update
Từ vòng đời của View bạn có thể nhận thấy View cung cấp cho chúng ta 2 hàm để có thể làm mới chính nó (View
) là invalidate() và requestLayout(). 2 phương thức này giúp bạn có thể thay đổi View trong khi người dùng tương tác theo thời gian thực. Nhưng tại sao lại cần đến 2 phương thức để thực hiện 1 công việc, đươi đây sẽ là lý do để bạn biết điều đó.
- invalidate() cung cấp để gọi lại hàm
onDraw()
nhằm cho bạn có thể quay lại và tùy chỉnh màu sắc, cập nhật văn bản cho View nhưng lưu ý mà các cập nhật không nên thay đổi kích thước của View vì kích thước của View đã được tính toán trước ở hàmonMeasure()
. - requestLayout() nói đến đây chắc các bạn biết tác dụng của hàm này rồi đúng không. Hàm
requestLayout()
giúp lập trình viên có thể quay lại gần đầu vòng đời của View để có thể thay đổi kích thước View theo ý muốn.
7. Animation
Sự thay đổi trạng thái trên View kết hợp với hiệu ứng sẽ tạo cho người dùng cảm giác mượt mà hơn.
ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
int newRadius = (int) animation.getAnimatedValue();
}
});
animator.start();
và đừng quên gọi invalidate() sau khi Animation kết thúc.
Bài viết trên đây mình đã giới thiệu cơ bản cách hoạt động của 1 View, và cách làm sao để bạn có thể Custom View theo ý muốn của mình. Hi vọng bài viết sẽ đem lại thêm hiểu biết về View trong Android cho các bạn.
All rights reserved