همه چیز درباره کتابخانه ButterKnife در اندروید ، کتابخانه ButterKnife در اندروید ناجی و فرشته ی نجات برنامه نویسان اندروید برای خلاص شدن از نوشتن متدهای طولانی findViewByID , onClick , onItemClick و مقداردهی یا initialize متفیر های مختلف و بسیاری از کدهای دیگر که تنها وظیفه پر کردن و کثیف کردن کلاس مارو داشت.
به عبارتی کتابخانه ButterKnife امکانات زیر را برای کاربر فراهم کرده
برای استفاده از کتابخانه ButterKnife مشخصا باید اول بریم سراغ گریدل برنامه و با ButterKnife آشناش کنیم پس دو خط زیر رو به dependencies اضافه میکنیم
// butter knife
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
بعد از sync کردن پروژه میتونیم از تمامی انوتیشن های کتابخانه ButterKnife استفاده کنیم اما نکته مهم این که بعد از استفاده از این انوتیشن ها باید در onCreate اکتیویتی یا فرگمنت حتما خط زیر رو اضافه کنیم تا bind ها به درستی اعمال بشن
// bind the view using butterknife
ButterKnife.bind(this);
پرکاربرد ترین انوتیشن این کتابخانه , انوتیشنی ست که بجای دستور ()findViewById استفاده میکنیم یعنی @BindView.
خب مشخصه که چکاری رو برای ما انجام میده ! حالا دیگه تنها کافیه به جای تعریف view و بعد مقدار دهی اون با استفاده از cast کردن به نوع view درخواستی و بعد هم اشاره کردن به id با استفاده از findViewById فقط بنویسیم
@BindView(R.id.nameTextView)
TextView mNameTextView;
@BindView(R.id.nameEditText)
EditText mNameEditText;
@BindView(R.id.button)
Button mbutton1;
یا هر view دیگه ای که در فایل xml خود دارید و در نهایت هم در متد onCreate برنامه دستور bind رو بدین صورت حتما ذکر کنید
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// bind the view using butterknife
ButterKnife.bind(this);
}
لیستی از view ها را bind میکند
@BindViews({R.id.lbl_name, R.id.lbl_email, R.id.lbl_address})
List<TextView> lblArray;
بخش دیگری از انوتیشن های ما داخل ButterKnife برای اختصاص دادن منبع یا مقداری به فیلدهاست که در ادامه باهاشون آشنا میشیم !
مشخصا برای اختصاص یک ارایه به فیلد بکار میرود برای مثال یک ارایه در فایل strings پروژه ایجاد کنید
<resources>
<string-array name="programmingLang">
<item>android</item>
<item>c</item>
<item>java</item>
<item>js</item>
<item>php</item>
<item>go</item>
</string-array>
</resources>
و در اکتیویتی خود اینطور عمل کنید
@BindArray(R.array.programmingLang) String[] progLang;
فراموش نکنید که حتما داخل متد onCreate هم دستور (ButterKnife.bind(this رو بنویسید .
این انوتیشن رو همچنین میتونید برای آرایه های دیگه ازجنس int ,TypedArray ,CharSequnce هم بکار ببرید.
برای اختصاص دادن یک فایل از شاخه drawable به یک فیلد به راحتی زیر
@BindBitmap(R.mipmap.ic_launcher) Bitmap launcherBitmap;
اما نکته قابل توجه درباره این انوتیشن این است که به شما تصویر با سایز اصلی را برمیگرداند بدون کوچک کردن یا تغییر سایز بنابراین شما خود باید تصویر را به صورتی دستی و یا با استفاده از کتابخانه های glide یا پیکاسو تغییر سایز دهید و ا ستفاده کنید تا با اخطار هایی مثل OutOfMemoryException مواجه نشوید.
برای bind کردن المان های drawable
@BindDrawable(R.mipmap.ic_launcher)
Drawable drawableLogo;
برای bind کردن یک مقدار string
@BindString(R.string.app_name)
String appName;
برای bindکردن مقدار color
@BindColor(R.color.colorPrimaryDark)
int colorTitle;
برای bind کردن مقدار dimen یا اندازه
@BindDimen(R.id.padding_hori)
float paddingHorizontal;
مقدار animation را bind میکند
;BindAnim(R.anim.move_up)
Animation animMoveUp;
مقدار font را اختصاص میدهدمقدار font را اختصاص میدهد
@BindFont(value = "test.ttf", bold = true)
@BindView(R.id.label) TextView label;
مقدار float را bind میکند
@BindFloat(R.dimen.radius)
float radius;
مقدار int را bind میکند
@BindInt(R.integer.distance)
int distance;
قطعا با listener ها آشنا هستید ! قبلا برای انجام عملیاتی خاص در حالتی خاص از view باید از listener ها به صورت زیر استفاده میکردیم
Button mbutton2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mbutton2=(Button) findViewById(R.id.button2);
mbutton2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"button has clicked");
}
});
}
خب میبینید که ما ۶ خط کد صرفا برای onClick برنامه نوشتیم خب فکر کنید بخوایم برای چندین view این listener ها رو صدا بزنیم !!!
اینجا است که ButterKnife قدرت خودشو نشون میده
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// bind the view using butterknife
ButterKnife.bind(this);
}
@OnClick(R.id.button)
void onButtonClick(){
Log.e(TAG,"button has clicked");
}
خب می بینید که:
ما در اندروید به کررات با این صحنه مواجه شده ایم که یک listener را برای چندین view درظر بگیریم ازجمله کلیک برروی buttonها
خبر خوب این است که butterKnife این کاررو هم برای ما راحت کرده بدین صورت
@OnClick({
R.id.redImageButton,
R.id.amberImageButton,
R.id.greenImageButton
})
void onLightClicked(ImageButton lightButton) {
switch(lightButton.getId()) {
case R.id.redImageButton:
// Turn on red light
break;
case R.id.amberImageButton:
// Turn on amber light
break;
case R.id.greenImageButton:
// Turn on green light
break;
}
}
خب نکات طلایی این قسمت:
ما در اندروید listener هایی هم داریم که interface آنها بش از یک متد دارد مثل TextWatcher
textView.addTextChangedListener(
new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// No-op
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// No-op
}
@Override
public void afterTextChanged(Editable s) {
validateEmail(s);
}
});
این درحالی است که بیشتر اوقات ما با تمامی متدهای interface کاری نداریم و عملیات مدنظر ما در یکی از متدها هم قابل اعمال است.
پس باز هم میریم سراغ Butter Knife و معجزه های که میکنه!
@OnTextChanged(value = R.id.emailEditText,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterEmailInput(Editable editable) {
validateEmail(editable);
}
انوتیشن onTextChanged مثل بقیه انوتیشن هاست و میتواند آرایه ای از view هارا با استفاده از شناسه آنها دریافت کند اما اینجا ما تنها یک شناسه به عنوان value و یک Callback که درواقع همان متد interfaceی است که مدنظر ماست که میخواهیم این انوتیشن برروی آن اعمال شود.
Butter Knife دارای ۳ انوتیشن multi-method می باشد:
اگر شما انوتیشن listener ی را bind یا به اصطلاح الحاق کردید که ممکن است گاهی وجود نداشته باشد میتوانید از انوتیشن @Optional استفاده کنید
@OnClick(R.id.proposeIfSingleButton)
@Optional
void proposeIfSingle() {
// On your knees pal (wink)
}
این دقیقا مشابه روشی است که ما از انوتیشن @Nullable برای viewهایی استفاده میکنیم که ممکن است گاهی موجود نباشند.
Custom View Listeners
همچنین در CustomView هایی که میسازید میتونید از listenerهای ButterKnife استفاده کنید اما دیگر نیاز به دادن ID نیست
public class TwitterFavoriteButton
extends Button {
@OnClick
void favoriteTweet() {
// Favorite the damn tweet...
}
}
ممکن است زمانی بخواهیم عملیاتی برروی دسته ای از View ها انجام دهیم همانطور که قبلا هم گفتیم ما در تمامی انوتیشن های Butter Knife میتوانیم آرایه
یا لیستی از view هارا داشته باشیم اما برای بدست آوردن لیستی از view ها در یک فیلد باید از @BindViews استفاده کنیم.
public class PickAvengersActivity extends Activity {
@BindViews({
R.id.ironMan, R.id.captainAmerica,
R.id.thor, R.id.blackWidow,
R.id.hawkeye, R.id.hulk
})
CheckBox[] avengers;
// ...
}
خب حالا ما لیستی از view ها رو داریم حالا باید راهی برای اعمال عملیاتی برروی همه آنها به صورت یکجا پیدا کنیم که Butter Knife فکر اینجا هم کرده در ادامه با actions , setters ,properties که تمامی کارهارو برای ما میکنند آشنا میشیم.
ما در بالا لیستی از چک باکس هارو درون فیلدی به عنوان avengers قرار دادیم حالا میخواهیم با استفاده از action تمامی view های داخل این لیست رو تیک بزنیم پس
public class PickAvengersActivity
extends Activity {
@BindViews({
R.id.ironMan, R.id.captainAmerica,
R.id.thor, R.id.blackWidow,
R.id.hawkeye, R.id.hulk
})
CheckBox[] avengers;
final Action<CheckBox> SELECT_ALL = new Action<>() {
@Override
public void apply(@NonNull CheckBox checkBox, int index) {
checkBox.setChecked(true);
}
};
// ...
@OnClick(R.id.selectAllButton)
void selectAll() {
ButterKnife.apply(avengers, SELECT_ALL);
}
}
خب مشخصا ما یک instance یا نمونه جدید از Action ایجاد کردیم که دارای متد apply برای اعمال عملیات مدنظر ما برروی تمامی اجزا لیست با استفاده از index است.
و با استفاده از انوتیشن @onClick اشاره شده که با فشردن یک کلید لیستی از ویو هارا به Action ی تحت عنوان SELECT_ALL برای اعمال عملیاتی خاص ارسال کند.
خب حال اگر بخواهیم برای مثال یک کلید دیگر تحت عنوان Clear برای unCheck کردن تمامی چک باکس ها در فایل xml خود قرار دهیم تا چک باکس هارا از حالت انتخاب خارج کند خب میتوانیم Action دیگری بنویسیم برای این عملیات اما راه حل دیگری نیز وجود دارد و آن استفاده از setter است.
حال setter چیست؟ خب به ساده ترین حالت ممکن میتون گفت setter ها دقیقا مشابه Action ها هستند با این تفاوت که اجازه ارسال یک مقدار اضافه بر پارامتر های قبلی را به شما میدهد تا بتوانید عملیات منعطف تری اعمال کرد برای مثال میتوان مثال فوق را بدین صورت تغییر داد
public class PickAvengersActivity
extends Activity {
@BindViews({
R.id.ironMan, R.id.captainAmerica,
R.id.thor, R.id.blackWidow,
R.id.hawkeye, R.id.hulk
})
CheckBox[] avengers;
final Setter<CheckBox, Boolean> CHECKED = new Setter<>() {
@Override
public void set(@NonNull CheckBox view, Boolean value, int index) {
checkBox.setChecked(value != null ? value : false);
}
};
// ...
@OnClick(R.id.selectAllButton)
void selectAll() {
ButterKnife.apply(avengers, CHECKED, true);
}
@OnClick(R.id.clearButton)
void clear() {
ButterKnife.apply(avengers, CHECKED, false);
}
}
خب همانطور که مشاهده میکنید ساختار setter شبیه به action است تنها تفاوت آن پارامترهای متد apply است و متدهای onClick کلید ها که براساس کلید انتخابی مقدار value مناسبی ارسال میشود.
آخرین آپشن نیز property ها هستند که میتوان آنهارا بروی view ها اعمال کرد
طریقه اعمال آنها نیز با متد apply به صورت زیر است
@OnClick(R.id.fade50Button)
void fade50() {
ButterKnife.apply(translucentViews, View.ALPHA, 0.5f);
}
برای نصب پلاگین کتابخانه ButterKnife به پست بعدی ما مراجعه کنید.