Giới thiệu Butter Knife, thư viện hỗ trợ gán view và callback vào các trường và hàm dành cho Android

Lập trình ứng dụng Android đôi khi là một công việc nhàm chán khi bạn phải viết đi viết lại 1 đoạn code dài đến vài dòng chỉ để làm những tác vụ đơn giản. Java là 1 ngôn ngữ được coi là khá "rườm rà" so với những ngôn ngữ mới hiện nay, và điều đó làm cho bạn không thể hoàn toàn tập trung vào việc xử lý phần logic khi mà code của bạn bị sắp xếp một cách lộn xộn và khó theo dõi. Hãy cùng xem 1 ví dụ nhỏ sau:

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private EditText mEditText;
    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text_view);
        mEditText = (EditText) findViewById(R.id.edit_text);
        mButton = (Button) findViewById(R.id.button);
    }
}

Đoạn code trên là 1 trong những ví dụ điển hình nhất về việc lặp code 1 cách không cần thiết khi lập trình Android mà bất cứ 1 ai cũng đã từng trải qua. Mặt tốt duy nhất của nó là cho chúng ta biết được nó sẽ làm gì (findViewById() - Tìm view trong layout bằng id này và gán với trường tương ứng) tuy nhiên với những người đã code Android dù sẽ chỉ 1 thời gian ngắn cũng sẽ rất quen với hàm này và dần dần việc gọi nó quá nhiều sẽ làm cho hàm chứa nó (ở đây là onCreate) nhìn rất lộn xộn. 1 trong những cách để xử lý vấn đề này là cho tất cả những call đến findViewById vào trong 1 hàm và gọi hàm đó ở onCreate, tuy nhiên cách này cũng không thật sự giải quyết được bản chất của vấn đề mà chỉ làm code trông "gọn" hơn 1 chút mà thôi. Thử tưởng tượng khi bạn có 1 layout khá phức tạp với trên 10 view xem, viết cái hàm gán hết nó đã rất là mất thời gian rồi.

Ở bài này mình sẽ giới thiệu với các bạn 1 thư viện có thể phần nào giải quyết cái sự rườm rà của việc lập trình ứng dụng cho Android, đó là Butter Knife. Butter Knife được phát triển bởi Jake Wharton, lập trình viên chủ đạo của Square, bạn nào hay theo dõi những thư viện mã nguồn mở thì chắc chắn đã ít nhất 1 lần nghe đến tên công ty này, bởi Square sở hữu các thư viện nổi tiếng bậc nhất dành cho Android như Picasso, Retrofit hay Okhttp.

logo.png

Thêm vào build.gradle trong project của bạn:

compile 'com.jakewharton:butterknife:7.0.1'

Đừng quên thêm cả dòng này để chặn lint warning:

lintOptions { disable 'InvalidPackage' }

Vậy thì Butter Knife dùng để làm gì? Use case phổ biến nhất của Butter Knife là để gán view vào trường trong 1 Activity hay Fragment. Như đoạn code ở trên có thể được viết lại như sau:

public class MainActivity extends AppCompatActivity {
    @Bind(R.id.text_view) TextView mTextView;
    @Bind(R.id.edit_text) EditText mEditText;
    @Bind(R.id.button) Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
}

Ngắn và dễ nhìn hơn rất nhiều! Vậy Butter Knife đã xử lý việc gán view như thế nào? Khi bạn sử dụng Annotation Bind cùng với 1 id, Butter Knife sẽ tự động tìm đến id đó trong layout và cast nó sang view tương ứng. Thay vì sử dụng reflection, Butter Knife tạo ra 1 class trong quá trình compile để thực hiện việc tìm kiếm. Class đó có chứa 1 method nhìn như sau:

public void bind(MainActivity activity) {
    activity.mTextView = (android.widget.TextView) activity.findViewById(2130968578);
    activity.mEditText = (android.widget.EditText) activity.findViewById(2130968579);
    activity.mButton = (android.widget.Button) activity.findViewById(2130968577);
}

Khi MainActivity được chạy và chúng ta gọi đến hàm ButterKnife.bind(this) thì đoạn code trên sẽ được chạy và view sẽ được tìm, cast và gán vào trường tương ứng. Lưu ý nhỏ là bạn phải gọi ButterKnife.bind(this) sau hàm setContentView(R.layout.activity_main) thì Butter Knife mới hoạt động nhé! Và 1 điều nữa là các trường dùng để gán view không được có modifier là private, do Butter Knife sẽ gọi đến các trường ấy ở 1 class khác chứ không phải trong class MainActivity.

Nhưng tác dụng của Butter Knife không chỉ có vậy. Nó còn rất hữu ích trong nhiều trường hợp khác như:

1. Gán resource

Butter Knife cho phép chúng ta gán resource một cách trực tiếp sử dụng các annotation như @BindString, @BindDimen hay @BindDrawable,... Bạn chỉ cần có id đúng là xong:

@BindString(R.string.some_string) String mString;
@BindDrawable(R.drawable.button_drawable) Drawable mDrawable;

2. Sử dụng với ViewHolder

Với sự ra đời của RecyclerView, có lẽ việc sử dụng ViewHolder pattern đã trở nên quen thuộc với tất cả các lập trình viên Android. Giống như đối với Activity hay Fragment, Butter Knife cũng giúp chúng ta tránh việc phải viết các hàm tìm kiếm view như sau:

class CustomViewHolder extends RecyclerView.ViewHolder {
    @Bind(R.id.title) TextView mTitle;
    @Bind(R.id.description) TextView mDescription;

    public CustomViewHolder(View view) {
      super(view);
      ButterKnife.bind(this, view);
    }
}

3. Gán các listener

Butter Knife cung cấp cho chúng ta các annotation cần thiết để bind những listener phổ biến nhất trong Android. Ví dụ cách sử dụng:

@OnClick(R.id.submit)
public void submit(View view) {
  // TODO viết code chạy khi ấn vào view có id `R.id.submit`
}

Ngoài @OnClick chúng ta còn có thể dùng

4. Gộp View vào 1 List hoặc Array

@Bind({R.id.text1, R.id.text2, R.id.text3})
List<TextView> mTexViewList;

5. Apply thuộc tính vào tất cả các View trong List

Bạn có thể set 1 thuộc tính (ví dụ như alpha) vào 1 List các View như sau:

ButterKnife.apply(mTexViewList, View.ALPHA, 0.0f);

6. Tìm đến 1 view cụ thể

Butter Knife cung cấp hàm findById() để bạn tìm 1 view cụ thể nào đó. Tuy nhìn hơi giống findViewById() nhưng bạn lại không cần phải cast khi sử dụng hàm này

View view = LayoutInflater.from(context).inflate(R.layout.main_layout, null);
TextView firstName = ButterKnife.findById(view, R.id.text_first_name);
TextView lastName = ButterKnife.findById(view, R.id.text_last_name);
ImageView photo = ButterKnife.findById(view, R.id.image_photo);

Trên đây chỉ là 1 trong số những tính năng mà Butter Knife đem lại. Đây là 1 thư viện đơn giản nhưng lợi ích mà nó đem lại là không phải bàn cãi, và nó đủ độ ổn định để có thể sử dụng ngay cả trong những ứng dụng lớn. Các bạn có thể tìm hiểu thêm về Butter Knife tại http://jakewharton.github.io/butterknife/

Cảm ơn các bạn đã theo dõi!