В этом уроке мы рассмотрим преимущества использования популярной Android библиотеки ButterKnife. Узнаем зачем она нужна и когда ее лучше использовать.
Краткий обзор библиотеки ButterKnife в Android
ButterKnife — инструмент байндинга (с англ. «связывание»), который использует аннотации для генерации шаблонного кода. Основная задача библиотеки состоит в том, чтобы избавить нас от избыточного кода, множественного использования findViewById(R.id.some_view)
при работе с View. Вместо этого используются лаконичные аннотации и код выглядит намного чище и элегантнее.
Чтобы начать пользоваться библиотекой ButterKnife, добавьте в секцию зависимостей (dependencies) следующие строчки:
1 2 |
compile 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' |
Обратите внимание:
- Последние 3 цифры (8.4.0 -версия библиотеки) наверняка изменятся, поэтому когда будете использовать этот код в своей приложении, не забудьте посмотреть актуальную версию в репозитории на Github.
- Не забудьте вставить в зависимости
annotationProcessor
для корректной работы с аннотациями.
Также Android Studio может потребовать от Вас более актуальную версию Android Support Repository и сама предложит установить обновление — соглашайтесь.
В результате Ваш скрипт build.gradle
должен выглядеть примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
apply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "24.0.0" defaultConfig { applicationId "javadevblog.com.butterknifeapp" minSdkVersion 16 targetSdkVersion 24 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.0.0' compile 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' testCompile 'junit:junit:4.12' } |
Обзор аннотаций ButterKnife
Ниже представлены самые популярные и полезные аннотации библиотеки ButterKnife. Они затрагивают работу с виджетами внутри Activity, во фрагментах, работу с ресурсами, обработкой событий и много другое.
У нас есть activity_main.xml, в котором описанны какие-то виджеты.
Задача: нам нужно их инициализировать в коде и как-то использовать в дальнейшем. Ниже представлены решения, которые представляет ButterKnife.
Аннотация @BindView
Наиболее популярной является аннотация @BindView
, которая отвечает за автоматическое связывание описанной View с ее xml-представлением в файле компоновке по указанному идентификатору.
Подробнее об аннотациях в Java.
1.1 Пример использования @BindView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package javadevblog.com.butterknifeapp; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.ImageView; import android.widget.TextView; import butterknife.BindView; import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity { @BindView(R.id.tv_title) TextView mTextViewTitle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mTextViewTitle.setText("Работаем с TextView как обычно"); } } |
Сгенерированный код для приведенного выше примера с ButterKnife.bind(this)
примерно эквивалентен следующему:
1 2 3 |
public void bind(MainActivity activity) { activity.title = (android.widget.TextView) activity.findViewById(2021857466); } |
1.2 Связывание ресурсов в ButterKnife
В библиотеке уже предопределенные аннотации для работы с ресурсами: @BindInt
, @BindString
, @BindDimen
, @BindDrawable
, @BindBool
, @BindColor
, который связывает идентификатор с объявленным виджетом. Пример инициализации некоторых ресурсов с помощью ButterKnife:
1 2 3 4 5 6 7 8 9 10 11 12 |
class MainActivity extends AppCompatActivity { @BindColor(R.color.color_red) int mColorRed; @BindString(R.string.title_res) String mStringTitle; @BindDrawable(R.drawable.graphic_res) Drawable mDrawableGraphic; // int (для указания пикселей) или float (для точного значения) @BindDimen(R.dimen.space_x) Float mSpaceX; // ...метод OnCreate() и остальная часть класса // опускается для акцентирования внимания на ButterKnife } |
2. Пример использования @BindView
вне активности
Если мы хотим использовать ButterKnife в каком-то фрагменте или адаптере, то нам нужно явно указать View, который нужно использовать для связывания виджетов.
1 2 3 4 5 6 7 8 9 10 11 12 |
public class FragmentExample extends Fragment { @BindView(R.id.btn_ok) Button mButtonOk; @BindView(R.id.btn_cancel) Button mButtonCancel; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_example, container, false); ButterKnife.bind(this, view); // теперь можно использовать mButtonOk и mButtonCancel return view; } } |
Этот подход также работает и в адаптере, например, когда мы работаем с ListView. Смотрим пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class SimpleAdapter extends BaseAdapter { @Override public View getView(int position, View view, ViewGroup parent) { ViewHolder viewHolder; if (view != null) { viewHolder = (ViewHolder) view.getTag(); } else { view = inflater.inflate(R.layout.simple_view, parent, false); viewHolder = new ViewHolder(view); view.setTag(viewHolder); } viewHolder.name.setText("Андрей"); // остальные поля return view; } static class ViewHolder { @BindView(R.id.tv_name) TextView mTextViewName; @BindView(R.id.tv_job) TextView mTextViewJob; public ViewHolder(View view) { ButterKnife.bind(this, view); } } } |
3. Аннотация @BindViews — Работа с несколькими View
ButterKnife предоставляет аннотацию @BindViews
для удобной работы с множеством объектов View. Например, мы определяем массив или список идентификаторов виджетов и применяем метод ButterKnife.
apply()
с каким-то действием. Действия предопределяет программист с помощью интерфейсов Action
и Setter
. Пример ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// создаем action с названием DISABLE static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() { @Override public void apply(View view, int index) { view.setEnabled(false); } }; // задаем сеттер с каким-то действием - необходимое значение передаем на вход static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() { @Override public void set(View view, Boolean value, int index) { view.setEnabled(value); } }; |
Задаем список:
1 2 3 |
// есть список id каких-то View @BindViews({R.id.tv_first, R.id.tv_second, R.id.tv_third}) List<TextView> mViewList; |
Теперь применяем какое-то действие к списку:
1 2 |
ButterKnife.apply(mViewList, DISABLE); ButterKnife.apply(mViewList, ENABLED, false); |
Теперь указанное действие будет применено к множеству View всего одним вызовом метода apply()
.
Аннотация @OnClick
С помощью ButterKnife в Android можно задавать слушатели всего одной аннотацией:
1 2 3 4 |
@OnClick(R.id.send_action) public void performSend(View view) { // что-то сделать по нажатию } |
Обратите внимание, что аргумент метода performSend()
не обязательный, то есть мы могли просто написать public void performSend() {...}
.
Также мы могли бы передать в параметры метода какой-то View (например, Button) — произойдет автоматическое приведение типов:
1 2 3 4 |
@OnClick(R.id.do_stuff) public void doStuff(Button button) { button.setText("Привет"); } |
Также можно указывать множество идентификаторов — для них будет установлено выбранное действие:
1 2 3 4 |
@OnClick({ R.id.view_first, R.id.view_second, R.id.view_third }) public void someMethod(CustomView view) { //... } |
Метод findById
Также ButterKnife предоставляет статический метод findById()
, который упрощает работу внутри компонентов типа Dialog или View:
1 2 3 |
View view = LayoutInflater.from(mContext).inflate(R.layout.simple_layout, null); TextView mTextViewTitle = ButterKnife.findById(view, R.id.tv_title); ImageView mImageViewPhoto = ButterKnife.findById(view, R.id.iv_photo); |
На этом наше знакомство с библиотекой Butter Knife подошло к концу, поэтому давайте подобьем итоги:
- работа с ресурсами через аннотации;
- заменяет громоздкие внутренние анонимные классы (например, слушатели нажатий) одной аннотацией
@OnClick
; - позволяет формировать списки или массивы из View и применять к ним какие-то действия;
- уменьшает количество обязательного кода, связанного с
findViewById()
всего-лишь одной аннотацией@BindView
.
Мое мнение: библиотека очень интересная и удобная, но в небольших проектах преимущества ее использования кажутся сомнительными — я бы все-таки использовал ее в средних или даже больших проектах. Мне кажется, там она сможет проявить себя во всей красе: заметно уменьшит количество кода, сделает большие классы более читабельными. Однако, как и любая другая библиотека, она имеет свой порог вхождения и для начинающих Android разработчиков возможно будет сложно на первых порах.
Следите за обновлениями!