Butterknife full analysis

Butterknife full analysis

Overview

Butterknife is an open source library developed by Jake Wharton, who works at Square. Using this library and the Android ButterKnife Zelezny plugin in AS can greatly improve development efficiency, and you can get rid of the cumbersome findViewById(int id) and do not need to manually @bind(int id). You can directly use the plugin to generate it. This blog will analyze Butterknife in depth.

Project address: JakeWharton/butterknife

ButterKnife has the following advantages:

1. Powerful View binding and Click event processing functions to simplify code and improve development efficiency

2. Conveniently handle the ViewHolder binding problem in Adapter

3. It will not affect the efficiency of APP during operation and is easy to use and configure

4. The code is clear and readable

How to import ButterKnife

Configure the following in the project's build.grade file:

  1. buildscript {
  2. repositories {
  3. jcenter()
  4. mavenCentral()
  5. Maven {
  6. url "https://plugins.gradle.org/m2/"  
  7. }
  8. }
  9. dependencies {
  10. classpath 'com.android.tools.build:gradle:2.2.0'  
  11. //Configure apt here for butterknife to use
  12. classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'  
  13.  
  14. }
  15. }

For example:

  1. buildscript {
  2. repositories {
  3. jcenter()
  4. mavenCentral()
  5. Maven {
  6. url "https://plugins.gradle.org/m2/"  
  7. }
  8.  
  9. }
  10.  
  11. dependencies {
  12. classpath 'com.android.tools.build:gradle:2.2.2'  
  13. classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'  
  14. }
  15. }
  16.  
  17. allprojects {
  18. repositories {
  19. jcenter()
  20. }
  21. }
  22.  
  23. task clean(type: Delete ) {
  24. delete rootProject.buildDir
  25. }

Configure the following in the app's build.grade file:

  1. apply plugin: 'com.android.application'  
  2. apply plugin: 'com.neenbedankt.android-apt'  
  3.  
  4. android{...}
  5.  
  6. dependencies {
  7. //View binding butterknife
  8. compile 'com.jakewharton:butterknife:8.4.0'  
  9. apt 'com.jakewharton:butterknife-compiler:8.4.0'  
  10. }

For example:

  1. apply plugin: 'com.android.application'  
  2. apply plugin: 'android-apt'  
  3.  
  4. android {
  5. compileSdkVersion 24
  6. buildToolsVersion "24.0.3"  
  7.  
  8. defaultConfig {
  9.  
  10. minSdkVersion 14
  11. targetSdkVersion 24
  12. versionCode 1
  13. versionName "1.0"  
  14. }
  15. buildTypes {
  16. release {
  17. minifyEnabled false  
  18. proguardFiles getDefaultProguardFile( 'proguard-android.txt' ), 'proguard-rules.pro'  
  19. }
  20. }
  21. }
  22.  
  23. dependencies {
  24. compile fileTree(dir: 'libs' , include: [ '*.jar' ])
  25.  
  26. compile 'com.jakewharton:butterknife:8.4.0'  
  27. apt 'com.jakewharton:butterknife-compiler:8.4.0'  
  28. }

How to use ButterKnife

1) Since you need to bind the Activity in onCreate every time, I suggest writing a BaseActivity to complete the binding, and then the subclass can inherit it.

Note: ButterKnife.bind(this); must be done after settingContentView when binding Activity:

The implementation is as follows (the same as FragmentActivity):

  1. public abstract class BaseActivity extends Activity {
  2. public abstract int getContentViewId();
  3.  
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(getContentViewId());
  8. ButterKnife.bind(this);
  9. initAllMembersView(savedInstanceState);
  10. }
  11.  
  12. protected abstract void initAllMembersView(Bundle savedInstanceState);
  13.  
  14. @Override
  15. protected void onDestroy() {
  16. super.onDestroy();
  17. ButterKnife.unbind(this); //Unbind, the official document only unbinds fragments
  18. }
  19. }

2) Bind fragment

  1. public abstract class BaseFragment extends Fragment {
  2. public abstract int getContentViewId();
  3. protected Context context;
  4. protected View mRootView;
  5.  
  6. @Nullable
  7. @Override
  8. public   View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  9. mRootView =inflater.inflate(getContentViewId(),container, false );
  10. ButterKnife.bind(this,mRootView);//Bind framgent
  11. this.context = getActivity();
  12. initAllMembersView(savedInstanceState);
  13. return mRootView;
  14. }
  15.  
  16. protected abstract void initAllMembersView(Bundle savedInstanceState);
  17.  
  18. @Override
  19. public void onDestroyView() {
  20. super.onDestroyView();
  21. ButterKnife.unbind(this); //Unbind
  22. }
  23. }

3) Control id annotation: @BindView()

  1. package com.myl.test;
  2.  
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.widget.Button;
  6.  
  7. import butterknife.BindView;
  8. import butterknife.ButterKnife;
  9.  
  10. public class ButterknifeActivity extends AppCompatActivity {
  11.  
  12. @BindView( R.id.button1 )
  13. public Button button1;
  14.  
  15. // Note: The button's modification type cannot be: private or static . Otherwise, an error will be reported: Error: @BindView fields must not be private or static   static . (com.myl.test.ButterknifeActivity.button1)
  16.  
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_butterknife);
  21. //Bind activity
  22. ButterKnife.bind( this );
  23.  
  24. button1.setText( "I am a button " );
  25. }
  26. }

4) Multiple control ID annotations: @BindViews()

  1. package com.myl.test;
  2.  
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.widget.Button;
  6. import java.util.List;
  7. import butterknife.BindViews;
  8. import butterknife.ButterKnife;
  9.  
  10. public class Main2Activity extends AppCompatActivity {
  11.  
  12. @BindViews({ R.id.button1 , R.id.button2 , R.id.button3 })
  13. public List<Button> buttonList;
  14.  
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_main2);
  19.  
  20. ButterKnife.bind(this);
  21.  
  22. buttonList.get( 0 ).setText( "hello 1 " );
  23. buttonList.get( 1 ).setText( "hello 2 " );
  24. buttonList.get( 2 ).setText( "hello 3 " );
  25. }
  26. }

5) @BindString(): Bind string

  1. package com.myl.test;
  2.  
  3. import android.os.Bundle;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.widget.Button;
  6.  
  7. import butterknife.BindString;
  8. import butterknife.BindView;
  9. import butterknife.ButterKnife;
  10.  
  11. public class ButterknifeActivity extends AppCompatActivity {
  12.  
  13. @BindView( R.id.button1 ) //Bind button control
  14. public Button button1;
  15.  
  16. @BindString( R.string.app_name ) // Bind string string
  17. String meg;
  18.  
  19. @Override
  20. protected void onCreate(Bundle savedInstanceState) {
  21. super.onCreate(savedInstanceState);
  22. setContentView(R.layout.activity_butterknife);
  23.  
  24. //Bind activity
  25. ButterKnife.bind( this );
  26.  
  27. button1.setText(meg);
  28. }
  29. }

6) @BindArray(): Bind the array in the string

  1. <resources>
  2. <string name = "app_name" >Campus Assistant</string>
  3.  
  4. <string-array name = "city" >
  5. <item>Dongguan City</item>
  6. <item>Guangzhou</item>
  7. <item>Zhuhai City</item>
  8. <item>Zhaoqing City</item>
  9. <item>Shenzhen City</item>
  10. </string-array>
  11.  
  12. </resources>
  13. ------------------------------------------------------------------  
  14. package com.myl.test;
  15.  
  16. import android.os.Bundle;
  17. import android.support.v7.app.AppCompatActivity;
  18. import android.widget.Button;
  19.  
  20. import butterknife.BindArray;
  21. import butterknife.BindView;
  22. import butterknife.ButterKnife;
  23.  
  24. public class ButterknifeActivity extends AppCompatActivity {
  25.  
  26. @BindView( R.id.button1 ) //Bind button control
  27. public Button button1;
  28.  
  29. @BindArray(R.array.city) //Bind the array in string
  30. String [] cities;
  31.  
  32. @Override
  33. protected void onCreate(Bundle savedInstanceState) {
  34. super.onCreate(savedInstanceState);
  35. setContentView(R.layout.activity_butterknife);
  36.  
  37. //Bind activity
  38. ButterKnife.bind( this );
  39.  
  40. button1.setText( cities[0] );
  41. }
  42. }

7) @BindBitmap(): Bind Bitmap resource

  1. package com.myl.test;
  2.  
  3. import android.graphics.Bitmap;
  4. import android.os.Bundle;
  5. import android.support.v7.app.AppCompatActivity;
  6. import android.widget.ImageView;
  7.  
  8. import butterknife.BindBitmap;
  9. import butterknife.BindView;
  10. import butterknife.ButterKnife;
  11.  
  12. public class ButterknifeActivity extends AppCompatActivity {
  13.  
  14. @BindView( R.id.imageView ) //Bind the ImageView control
  15. public ImageView imageView ;
  16.  
  17. @BindBitmap( R.mipmap.wifi ) //Bind Bitmap resource
  18. public Bitmap wifi_bitmap;
  19.  
  20. @Override
  21. protected void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_butterknife);
  24.  
  25. //Bind activity
  26. ButterKnife.bind( this );
  27.  
  28. imageView.setImageBitmap(wifi_bitmap);
  29. }
  30. }

8) @BindColor(): Bind a color value

  1. package com.myl.test;
  2.  
  3. import android.os.Bundle;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.widget.Button;
  6.  
  7. import butterknife.BindColor;
  8. import butterknife.BindView;
  9. import butterknife.ButterKnife;
  10.  
  11. public class ButterknifeActivity extends AppCompatActivity {
  12.  
  13. @BindView( R.id.button1 ) //Bind a control
  14. public Button button1;
  15.  
  16. @BindColor( R.color.colorAccent ) int black ; //Bind a color value
  17.  
  18. @Override
  19. protected void onCreate(Bundle savedInstanceState) {
  20. super.onCreate(savedInstanceState);
  21. setContentView(R.layout.activity_butterknife);
  22.  
  23. //Bind activity
  24. ButterKnife.bind( this );
  25.  
  26. button1.setTextColor( black );
  27.  
  28. }
  29. }

9) Adapter ViewHolder Binding

  1. public class TestAdapter extends BaseAdapter {
  2. private List<String> list;
  3. private Context context;
  4.  
  5. public TestAdapter(Context context, List<String> list) {
  6. this.list = list;
  7. this.context = context;
  8. }
  9.  
  10. @Override
  11. public   int getCount() {
  12. return list== null ? 0 : list. size ();
  13. }
  14.  
  15. @Override
  16. public Object getItem( int position) {
  17. return list.get(position);
  18. }
  19.  
  20. @Override
  21. public long getItemId( int position) {
  22. return position;
  23. }
  24.  
  25. @Override
  26. public   View getView( int position, View convertView, ViewGroup parent) {
  27. ViewHolder holder;
  28. if (convertView == null ) {
  29. convertView = LayoutInflater. from (context).inflate(R.layout.layout_list_item, null );
  30. holder = new ViewHolder(convertView);
  31. convertView.setTag(holder);
  32. } else {
  33. holder = (ViewHolder) convertView.getTag();
  34. }
  35. holder.textview.setText( "item=====" + position);
  36. return convertView;
  37. }
  38.  
  39. static class ViewHolder {
  40. @Bind(R.id.hello_world)
  41. TextView textview;
  42.  
  43. public ViewHolder( View   view ) {
  44. ButterKnife.bind(this, view );
  45. }
  46. }
  47. }

10) Click event binding: No need to declare view, no need to setOnClickLisener() to bind click event

a. Bind a method directly

  1. @OnClick(R.id.submit)
  2. public void submit( View   view ) {
  3. // TODO submit data to server...
  4. }

b. All monitoring method parameters are optional

  1. @OnClick(R.id.submit)
  2. public void submit() {
  3. // TODO submit data to server...
  4. }

c. Define a specific type, which will be automatically converted

  1. @OnClick(R.id.submit)
  2. public void sayHi(Button button) {
  3. button.setText( "Hello!" );
  4. }

d. Multiple views can handle the same click event uniformly, which is very convenient and avoids the trouble of repeated method calls

  1. @OnClick(R.id.submit)
  2. public void sayHi(Button button) {
  3. button.setText( "Hello!" );
  4. }

e. Custom views can bind their own listeners without specifying an id

  1. public class FancyButton extends Button {
  2. @OnClick
  3. public void onClick() {
  4. // TODO do something!
  5. }
  6. }

f. Add addTextChangedListener to EditText (that is, the method of adding multiple callback methods to listen), use the specified callback to implement the method you want to callback. If you don't use any annotations, click to read the comments on the source code.

  1. @OnTextChanged(value = R.id.mobileEditText, callback = OnTextChanged.Callback.BEFORE_TEXT_CHANGED)
  2. void beforeTextChanged(CharSequence s, int start, int   count , int   after ) {
  3.  
  4. }
  5. @OnTextChanged(value = R.id.mobileEditText, callback = OnTextChanged.Callback.TEXT_CHANGED)
  6. void onTextChanged(CharSequence s, int start, int before, int   count ) {
  7.  
  8. }
  9. @OnTextChanged(value = R.id.mobileEditText, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
  10. void afterTextChanged(Editable s) {
  11.  
  12. }

Code Obfuscation

  1. -keep class butterknife.** { *; }
  2. -dontwarn butterknife.internal.**
  3. -keep class **$$ViewBinder { *; }
  4.  
  5. -keepclasseswithmembernames class * {
  6. @butterknife.* <fields>;
  7. }
  8.  
  9. -keepclasseswithmembernames class * {
  10. @butterknife.* <methods>;
  11. }

Use of Zelezny plugin

In AndroidStudio->File->Settings->Plugins->Search Zelezny, download and add it, you can quickly generate instance objects of the corresponding components without writing them manually. When using it, right-click on the layout resource code of the Activity, Fragment or ViewHolder where you want to import annotations, ->Generate -> Generate ButterKnife Injections, and then a selection box as shown in the figure will appear.

ButterKnife implementation principle

For those who have some knowledge of ButterKnife, the way to inject fields is to use the annotation @BindView(R.id.tv_account_name), but first we need to inject ButterKnife.bind(Activity activity) in the Activity declaration. We know that annotations are divided into several categories, some are effective in the source code, some are effective when the class file is generated, and some are effective at runtime. They are RetentionPolicy.SOURCE, RetentionPolicy.CLASS, and RetentionPolicy.RUNTIME, among which RetentionPolicy.RUNTIME consumes the most performance. ButterKnife uses compiler-time injection. When using it, you need to configure classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'. This configuration indicates that annotation processing is performed during compilation. To process annotations, you need to inherit AbstractProcessor and in boolean process(Set

ButterKnife Implementation

Knowing that annotations can be processed during compilation, we can get the field attributes and the class of the annotation, and then generate an injection file, generate an inner class of the injection class, and then process the field. After compilation, it will be merged into the injection class to achieve the purpose of implanting new code segments. For example: we inject @VInjector(R.id.tv_show) TextView tvShow; we can get the value of the tvShow variable and the id R.id.tv_show, and then perform pattern processing injectObject.tvShow = injectObject.findViewById(R.id.tv_show);, and then add the code to the class where the component is located as an inner class, completing a DI (injection).

a) First create a view annotation

b) Create an annotation processor to get the attributes of the annotation and the class to which it belongs

c) Parse annotations and separate and combine classes and attributes

d) Combine Class and attributes to generate a new Java File

APT generated Java File, and pattern code

Use Javac to generate subclasses of the injection class at compile time

Project UML diagram

Brief description:

Main categories:

VInjectProcessor —-> Annotation processor, you need to configure the annotation processor

  1. resources
  2. -META-INF
  3. - services
  4. - javax.annotation.processing.Processor

Processor content:

  1. com.myl.viewinject.apt.VInjectProcessor #Specify the full class name of the processor

VInjectHandler —-> Annotation processing class, mainly performs injection class and annotation field parsing and encapsulation, and maps the same type of fields using map collection. exp: Map

Custom ButterKnife Implementation

Due to WeChat word limit, please click the original link in the lower left corner to view!~

<<:  Android Wear 2.0 preview: new single sign-on feature

>>:  6 reasons for Android memory leaks

Recommend

How do Internet celebrity stores create hit products?

This article mainly analyzes from four aspects: c...

iOS 14.7 official version released, iOS 14.7 update notes

iOS 14.7 mainly optimizes general functions, incl...

How long will the NBA suspension last? NBA lockout costs more than $1 billion

The new coronavirus is spreading around the world...

Mercedes-Benz new E-class starts at RMB 422,800

The 2016 Guangzhou Auto Show has kicked off. Merc...

12-inch iPad Pro is coming soon!

A foreign Apple device hacker named Steve TS disc...

「User acquisition」The core of new product user growth

When it comes to growth, the first thing that com...

Apple Pencil vs. Surface Pen: Do you really need a pen for your screen?

Obviously, Apple Pencil is designed for iPad Pro,...

Quantum computers: a three-step leap from the laboratory to changing the world

Quantum computers have been one of the hottest st...