Day 9 - Butter Knife
สวัสดีครับ บทความนี้เป็นบทความที่ 9 แล้วนะครับ ที่ผมจะมาเขียน ในซีรีย์ Learn 30 Android Libraries in 30 days
สำหรับบทความทั้งหมด อ่านได้จากด้านล่างครับ
- Day 1 : AndroidStaggered Grid
- Day 2 : Paralloid
- Day 3 : Retrofit
- Day 4 : SwipeRefreshLayout
- Day 5 : Android GraphView
- Day 6 : Holo Color Picker
- Day 7 : Android Async Http
- Day 8 : Crashlytics
- Day 9 : Butter Knife
- Day 10 : Android Annotations
- Day 11 : DateTimePicker
- Day 12 : Circular Progress Button
- Day 13 : ViewPager
- Day 14 : ViewPagerIndicator
- Day 15 : FadingActionBar
- Day 16 : AutofitTextView
- Day 17 : SwipeListView
- Day 18 : ShowcaseView
- Day 19 : GreenDAO
- Day 20 : AndroidViewAnimation
- Day 21 : ActiveAndroid
- Day 22 : Twitter4J
- Day 23 : ListViewAnimations
- Day 24 : AndEngine
- Day 25 : EazeGraph
- Day 26 : Cardslib
- Day 27 : AdapterKit
- Day 28 : WeatherLib
- Day 29 : FlatUI
- Day 30 : Android Firebase
สำหรับวันนี้ขอนำเสนอเรื่อง Butter Knife ครับ
Butter Knife คืออะไร?
Butter Knife , View "injection" library for Android มันคือ Library สำหรับการ Inject View (Inject แปลตรงๆก็คือการฉีด) ให้เราอัตโนมัติ โดย Butter Knife จะทำหน้าที่หน้า View ให้เราเอง และทำการ cast View ให้เราเอง
สำหรับใครที่ไม่เข้าใจเรื่อง Injection แนะนำให้อ่าน Inversion of Control หรือ Dependencies Injection ประกอบครับ
ยอมรับเลยว่าผมไม่เคยใช้ Annotation บน Android มาก่อนเลย นอกจาก @Override
กับ @Deprecated
นะ :D เมื่อลองใช้ Butter Knife แล้ว ทำให้หวนนึกถึงอดีตที่เคยเขียน Java Spring ครับ คุ้นเคยกับการใช้ @Autowired
, @Qualifier
, @RequestMapping
, @Service
, @Controller
อุ้ย เยอะแยะ พูดแล้วก็คิดถึง ฮ่าๆ แต่ว่าหากใครไม่คุ้นเคยกับ Annotation เมื่อมาลองจับ Butter Knife ครั้งแรก คิดว่าน่าจะไม่ยากเท่าไหร่นะครับ ลองอ่านบทความนี้ดูครับ เผื่ออาจจะสนใจขึ้นมา
Installation
ขั้นตอนการติดตั้ง Library ตัวนี้ ทำได้ไม่ยากครับ ดาวน์โหลดไฟล์ .jar จากที่นี่ butterknife-5.1.1.jar หรือจะเป็น gradle ก็เปิดไฟล์ build.gradle
แล้วเพิ่ม dependencies นี้ลงไป
dependencies {
compile 'com.jakewharton:butterknife:5.1.1'
}
Getting Started
โปรเจ็คนี้ผมใช้ไฟล์ 2 ไฟล์ครับ คือ ButterKnifeActivity.java
และ activity_butter_knife.xml
สำหรับเลเอาท์
ไฟล์ activity_butter_knife.xml
มี TextView 2 อัน และ Button 1 อัน ดังนี้
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:id="@+id/greeting"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_below="@id/greeting"
android:id="@+id/name"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_click"
android:paddingLeft="32dp"
android:paddingRight="32dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:id="@+id/button_click"/>
</RelativeLayout>
ใส่ references ให้ String นิดนึง (ไม่ชอบ hard code)
ไฟล์ res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="button_click">Click</string>
</resources>
@InjectView
โดยปกติเวลาเราๆเขียนแอพ เวลาเราจะเชื่อม View ที่เราทำไว้ใน Layout ปกติเราจะใช้โค๊ดแบบนี้ใช่มั้ยครับ?
public class ButterKnifeActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.day9_activity_butter_knife);
TextView greeting = (TextView) findViewById(R.id.greeting);
TextView name = (TextView) findViewById(R.id.name);
Button buttonClick = (Button) findViewById(R.id.button_click);
buttonClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
}
ที่นี้ ถ้าเราใช้ Butter Knife เราใช้โค๊ดแค่นี้
public class ButterKnifeActivity extends ActionBarActivity {
@InjectView(R.id.greeting) TextView mGreeting;
@InjectView(R.id.name) TextView mName;
@InjectView(R.id.button_click) Button mButtonClick;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.day9_activity_butter_knife);
ButterKnife.inject(this);
}
อธิบายคือ Butter Knife จะใช้ annotation @InjectView(view_id)
โดยมี parameter เป็น id ของ View ที่เราต้องการหา ตามด้วย View และตัวแปรที่ต้องการครับ อย่างเช่น
@InjectView(R.id.greeting) TextView mGreeting;
มันก็คือการหา View ที่ชื่อ greeting
ในไฟล์ day9_activity_butter_knife
จากนั้นก็จะ cast มาใส่ TextView ของเราที่ชื่อ mGreeting
ฉะนั้น @InjectView()
: ก็คล้ายๆกับการ findViewById()
ส่วนการ cast มาใส่ TextView ก็เหมือนกับการใช้ (TextView) findViewById();
อ้อแล้วก็ ButterKnife.inject(this);
อันนี้จะขาดไม่ได้เลย มันคือการสั่งให้ Butter Knife inject View ในเลเอาท์นี้ให้เราหน่อยนะ เปรียบเสมือนเราลืมทำการ setContentView()
นั่นแหละ
@OnClick
หลายคนอาจจะสงสัย ถ้าเรา Inject View ได้แล้ว แล้วเราสามารถจะ setListener ให้กับ Button ได้มั้ย? คำตอบคือได้ครับ โดยใช้ @OnClick
อย่างเช่น ปกติโค๊ดเราเป็นแบบนี้
Button buttonClick = (Button) findViewById(R.id.button_click);
buttonClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// sayHello();
}
});
เมื่อเราใ้ช้ Butter Knife จะเหลือแค่นี้
@InjectView(R.id.button_click) Button mButtonClick;
@OnClick(R.id.button_click)
public void sayHello() {
}
อธิบายเพิ่มเติมคือ เราสร้างเมธอดขึ้นมาใหม่ สมมติเมธอดเอาไว้โชว์ Toast จากนั้นใช้ @OnClick()
แล้วส่ง parameter เป็นชื่อ view_id ไป ตัว Butter Knife มันก็จะไปทำการ setListerner()
ให้เราเอง
ลองเปรียบเทียบดูครับ อันบนกับอันล่าง อันไหนน่าใช้กว่ากัน :D
@InjectViews
เห็นหัวข้ออย่าสับสนนะครับ เขียนผิดหรือเปล่า ทำไมเขียน @InjectViews
ซ้ำ ที่จริงไม่ซ้ำกันนะครับ มี s กับ ไม่มี s สำหรับ @InjectViews
อันนี้ เราสามารถให้มัน inject ทีเดียว หลายๆ View ได้เลย อย่างเช่น กรณีที่เรามี TextView, Button ในหน้าเลเอาท์นั้นๆ เยอะมากๆ ต้องมา @InjectView()
ทุกๆอัน ก็ชักช้า เราสามารถทำแบบนี้ได้
@InjectViews({R.id.greeting, R.id.name})
List<TextView> mTextViews;
mTextViews.get(0).setText("Hello");
mTextViews.get(1).setText("Jane Doe");
โดยการส่ง parameter เป็น array ที่มีไอดีอยู่ จากนั้น ตามด้วย List<TextView>
เวลาเรียกใช้ก็แค่ระบุ index ของ List
findById
สุดท้ายครับ findById
ของ Butter Knife จะทำหน้าที่เหมือนตัว findViewById()
แบบปกติครับ แต่ว่าของ Butter Knife จะมีการ casting ให้เราอัตโนมัติครับ เช่น
TextView greeting = (TextView) findViewById(R.id.greeting);
Button buttonClick = (Button) findViewById(R.id.button_click);
แบบนี้ เราต้องมา cast greeting
จาก View ไปเป็น TextView และ buttonClick
ไปเป็น Button เอง แต่ถ้าใช้ Butter Knife จะเป็นแบบนี้
TextView greeting = ButterKnife.findById(this, R.id.greeting);
Button buttonClick = ButterKnife.findById(this, R.button_click);
โดย this หมายถึงหา View ใน Activity ของเรานะครับ
สรุป
จากที่เขียนมาทั้งหมดก็ยังเป็นแค่ Basic อยู่ ใครอยากรู้รายละเอียดแบบลึกขึ้น ก็แนะนำอ่านจากต้นฉบับเลยครับ Butter Knife
หลังจากได้ทดลองใช้ Butter Knife แล้วรู้สึกว่าก็โอเคนะครับ ใช้งานค่อนข้างง่าย ยอมรับเลยว่าไม่เคยใช้มาก่อนเลย นี้เพิ่งใช้ครั้งแรก แล้วก็เขียนบทความเลย อาจจะเพราะเคยใช้ annotation บน Spring มาก่อนรึเปล่าไม่แน่ใจนะครับ
รู้สึกว่า มันทำให้โค๊ดดูสะอาดตาขึ้นเยอะครับ แต่จริงๆ ผมก็ยังชอบใช้ findViewById()
นะ ก่อนที่จะมาลอง Butter Knife ผมได้เห็นอันที่คล้ายๆกันแล้วเช่น Dagger, Roboguice, Android Annotations เดี่ยวอาจจะลองเล่น แล้วเอามาเปรียบเทียบกันดูครับ ว่าอันไหนใช้ง่าย หรือว่ามีอันไหนมีฟังค์ชันที่พิเศษที่อันอื่นไม่มี ไว้คอยติดตามบทความถัดไปนะครับ ขอบคุณครับ
สุดท้าย Source Code อัพเดทล่าสุด
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit