Author: Ninh食べます

  • Giúp background của View co giãn tối ưu với 9-patch image trong Android

    Giúp background của View co giãn tối ưu với 9-patch image trong Android

    Đã bao giờ bạn phải xử lý một Dialog, Button hoặc 1 cái CardView design đặc biệt tràn đầy graphical art các kiểu con đà điểu chưa? Có thể bạn nhìn file design một lúc và export nguyên cái ảnh ra để rồi nhận ra nếu nhiều chữ hơn hoặc màn hình bé hơn to hơn -> ảnh vỡ, code của bạn tèo ?

    pain...
    không chỉ ở làng Mưa, mà trong file design cũng có pain…

    Và rồi bạn đành phải work around, tự cắt các thành phần của một design ra nhiều phần nhỏ và dùng ImageView ghép lại theo định nghĩa phần có thể giãn và phần không cần giãn. Well, code bạn vẫn chạy ổn, nhưng chưa phải tối ưu.

    Để tạo background cho những component mang tính graphical như vậy, chúng ta đã 9-patch image hỗ trợ.

    9-patch Image là gì cơ?

    9-patch image là drawable dạng ảnh png đặc biệt có thể co giãn theo một định nghĩa nằm trong file này. Loại drawable này thường được sử dụng để làm background cho các View, Android OS sẽ xử lý background dạng 9-patch co giãn theo định nghĩa của file thay vì giãn đều như drawable bình thường. File 9-patch sẽ có đuôi 9.png.
    Btw, 9-patch là 9 mảnh vá đó ? (trích của anh Nguyen Van Toan)

    Cấu trúc của ảnh 9-patch

    Như ảnh bạn có thể thấy 9-patch image được chia làm 2 thành phần:

    • Stretchable area
    • Fill area (Padding box)

    Stretchable area là phần mà ảnh có thể được giãn nếu cần thiết.
    Fill area là phần mà content của bạn được phép nằm trong đó (Vì thế nên còn gọi là Padding box đó hehe).

    Ngoài ra thì 9-patch image còn hỗ trợ Optical Bounds. Optical bounds trên 9-patch image sẽ hiển thị bằng đường kẻ màu đỏ.

    Như bạn thấy, khi định nghĩa chuẩn, View của bạn sẽ được co giãn một cách tối ưu.

    Nghe hay phết, vậy tạo 9-patch như thế nào z bro?

    Cách tạo 9-patch image

    Để tạo ra 9-patch image, Android Studio đã có sẵn tool support đầy đủ, bạn không cần tải thêm bất cứ thứ gì khác. Ngoài ra thì cũng có cách khác là sử dụng Simple nine-patch generator trong bộ tools online của Roman Nurik.

    Tạo bằng Android Studio

    Bước 1: Chúng ta click chuột phải vào ảnh PNG gốc, chọn Create 9-Patch file…

    Bước 2: bắt đầu tuỳ chỉnh các phẩn Stretchable area và Fill area cho hợp lý

    Bước 3: Sử dụng file 9-patch như một drawable bình thường (không cần ghi đuôi .9)

    Tạo bằng Simple nine-patch generator

    Để hỗ trợ các density khác nhau của các device Android khác nhau, chúng ta cần tạo đủ các resources như drawable-ldpi, drawable-mdpi, drawable-hdpi, drawable-xhdpi… nên nếu tạo bằng tay từng cái một có thể sẽ là 1 đống việc. Nên chúng ta dùng Tool!

    Bước 1: Mở trang web Simple nine-patch generator chọn Select image và chọn đúng loại desity của source ảnh bạn chọn.

    Bước 2: Chỉnh Stretchable area và Fill area cho hợp lý. Nếu bạn sử dụng Optical bounds thì nó sẽ là viền đỏ trên file 9-Patch.

    Bước 3: Tải ảnh về khi đã edit xong, chúng ta sẽ click vào icon download ở góc phải trên cùng để tải về.

    Bước 4: Sử dụng file 9-patch như một drawable bình thường (không cần ghi đuôi .9)

    Việc tạo bằng tool sẽ giúp chúng ta tinh chỉnh dễ dàng hơn, đỡ phải take time tạo từng 9-patch image khác nhau cho các density mà chưa chắc đã có độ nhất quán tuyệt đối. Nhưng bên cạnh đó lại có một nhược điểm là bạn chỉ có thể tạo một Stretchable area trong một drawable. Android studio thì lại giúp bạn tuỳ ý tạo các Stretchable area khác nhau.

    Summary

    Để styling trong Android thì muôn vàn cách, 9-patch chỉ là một trong số cách đó. Trên đây chỉ là một vài ví dụ cơ bản về việc sử dụng 9-patch. Mong rằng bài viết đã giúp bạn có thêm một kiến thức mới – phép thuật mới cho chặng đường của một phù thủy Android ??

  • Delegate trong Kotlin

    Delegate trong Kotlin

    Delegate là gì nhỉ? Tại sao lại là Delegate? Đúng như cái tên, Delegate là một design pattern mà bạn ủy quyền xử lý logic của Class hiện tại cho một Object/Class khác. Delegate thường được sử dụng để tách logic code theo việc của nó (separate concerns) hoặc common hóa một đoạn logic.
    Bài viết này sẽ giúp bạn tìm hiểu cơ bản về Delegate trong Kotlin và cách sử dụng khái niệm này. Trong Kotlin, có 2 cách để sử dụng Delegate
    – Interface/Class Delegation
    – Delegate Properties

    Delegate là cách bạn cho phép một object khác xử lý một logic cho object hiện tại

    Interface/Class Delegation

    Với cách thứ nhất, chúng ta sẽ sử dụng một interface làm abstract cho một object và truyền object đó vào phần khai báo implement thông qua keyword by. Bằng cách này, các abstract methods (method của interface) sẽ chạy code của delegating object!

    interface CameraOptimization {
        fun optimize()
    }
    
    object XiaomiDevicesOptimization : CameraOptimization {
        override fun optimize() {
            TODO("do something for Xiaomi devices")
        }
    
    }
    
    object DefaultDevicesOptimization : CameraOptimization {
        override fun optimize() {
            TODO("do something for others devices")
        }
    }
    
    class CameraManager(optimization: CameraOptimization) : CameraOptimization by optimization {
        fun cameraFocus() {
            //todo: focus camera
        }
    }

    Đây là cách setup cơ bản của Interface/Class Delegation. Như các bạn thấy thì implementation của method optimize() không trực tiếp xuất hiện ở trong class CameraManager mà sẽ được delegate đến object truyền vào bằng keyword by.

    class CameraActivity : BaseActivity() {
        private lateinit var _cameraManager: CameraManager
    
        private fun initView() {
            val vendor = android.os.Build.MANUFACTURER
            val config = when {
                vendor.equals("Xiaomi", ignoreCase = true) -> XiaomiDevicesOptimization
                else -> DefaultDevicesOptimization
            }
            _cameraManager = CameraManager(config)
            _cameraManager.optimize()
            _cameraManager.cameraFocus()
        }
    }


    Khi method optimize() được gọi, nó sẽ delegate đến Config của XiaomiDevicesOptimization hoặc DefaultDevicesOptimization tùy theo device đó là gì. Với cách tiếp cận này logic của CameraManager vẫn có khả năng tối ưu mà không cần phải quan tâm rằng nó sinh ra cho vendor cụ thể nào cả. Đồng thời cũng tăng khả năng mở rộng của Class này hơn. Nếu app của bạn quyết định support optimize thêm cả anh zai Samsung cũng oke luôn, code thêm 1 class và 1 dòng duy nhất.

    Delegate Properties

    Chắc bạn đã từng sử dụng rất nhiều lần lazy trong kotlin rồi đúng không? Delegate đó :v Delegate Properties là việc bạn implement operator getValue (có thể thêm cả setValue luôn nếu bạn muốn nó set được cả value) của một class. Hoặc một cách khác tường mình hơn là implement interface ReadWriteProperty/ReadOnlyProperty. Class đó sẽ trở thành Delegate. Vẫn là keyword by, chúng ta khai báo một biến với Delegate thông qua by.

    class IntNaturalSet : ReadWriteProperty<Any, Int> {
        private var _value: Int = 0
        override fun getValue(thisRef: Any, property: KProperty<*>): Int {
            return _value
        }
    
        override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
            _value = if(value < 0) 0 else value
        }
    }

    Trong ví dụ đơn giản này chúng ta đã ủy quyền getter setter của biến cho class IntNaturalSet can thiệp và xử lý logic. Bên cạnh việc ủy quyển xử lý logic getter (setter) thì Delegate cũng có thể access đến Class chứa biến được delegate thông qua param thisRef(Chính là generic T trong ReadWriteProperty/ReadOnlyProperty). Có thể là built-in delegate cho một Type hoặc ép kiểu thisRef trong logic của getter setter để có thể sử dụng param này.

    Ứng dụng của Delegate Properties rất rộng, chúng ta có thể custom cho logic in/out data như shared preferences, cache…
    Trong Androidx/Kotlin cơ bản cũng có vài delegate như lazy, viewModels, activityViewModels…

    Summary

    Delegate là một phương pháp khá hay trong lập trình giúp chúng ta tối ưu logic source code. Một source base có thể sẽ clean hơn nếu rút gọn các common logic hay boilerplate code. Một class có thể tăng tính mở rộng trong tương lai. Một project có thể sẽ triển khai nhanh hơn nhờ những common và khả năng scalable tốt. Lần tới nếu như bạn gặp phải một vấn đề có thể xử lý bằng Delegate, cứ thử xem sao nhé!
    Hi vọng bài viết giúp bạn có thêm chút kiến thức trong chặng đường của một Engineer :3!