In the previous step, Implementing your own ButterKnife(1), we saw how to implement findViewById in an Activity using the @bindView annotation. After the article was published, I was asked if I could Bind setContentView() with annotations as well, which ButterKnife does not currently do. If you do, just add butterknife.bind (this) to the onCreate method in BaseActivity; So subclasses of BaseActivity don’t even need to write onCreate, which saves a lot of code. A programmer who is not lazy is not a good programmer. So, let’s implement, use annotations to generate the setContentView code.

This article realizes the function

Annotating an Activity with @bindLayout automatically adds a setContentView statement to the onCreate method, eliminating the need for manual code implementation of the onCreate method. Project link unchanged: Click to go to Github:ButterFork.

To improve the details

The first, of course, is to add annotations to the Annotation Module:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface BindLayout {
    int value(a);
}Copy the code

We are annotating the class, so the Target is elementType. TYPE instead of FIELD.

Then, use the annotations in the Sample Module:

@BindLayout(R.layout.activity_main)
public class MainActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ButterFork.bind(this); }}Copy the code

If you remember, butterfork.bind () essentially calls the auto-generated bindMainActivity.bind () method via reflection, along with @bindView’s auto-generated findViewById statement. Let’s just put the setContentView statement in there as well. In order to automatically generate the statement, the BindProcessor in the Compiler Module needs to be changed, and only 5 lines of code are added. See the comments.

private void generateJavaClass(a) {
        for (TypeElement enclosedElem : mBindViewElems.keySet()) {
            //generate bind method
            MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("bind")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .addParameter(ClassName.get(enclosedElem.asType()),"activity")
                    .returns(TypeName.VOID);
// Add code start
            BindLayout bindLayoutAnno = enclosedElem.getAnnotation(BindLayout.class);
            if(bindLayoutAnno ! =null){
                methodSpecBuilder.addStatement(String.format(Locale.US,"activity.setContentView(%d)",bindLayoutAnno.value()));
            }
// Add code end
            for (Element bindElem : mBindViewElems.get(enclosedElem)) {
                methodSpecBuilder.addStatement(String.format(Locale.US,"activity.%s = (%s)activity.findViewById(%d)",bindElem.getSimpleName(),bindElem.asType(),bindElem.getAnnotation(BindView.class).value()));
            }
            TypeSpec typeSpec = TypeSpec.classBuilder("Bind"+enclosedElem.getSimpleName())
                    .superclass(TypeName.get(enclosedElem.asType()))
                    .addModifiers(Modifier.FINAL,Modifier.PUBLIC)
                    .addMethod(methodSpecBuilder.build())
                    .build();
            JavaFile file = JavaFile.builder(getPackageName(enclosedElem),typeSpec).build();
            try {
                file.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
// e.printStackTrace();}}Copy the code

If BindLayout annotates the current class, use the value of the annotation (layoutId) to generate a setContentView statement.

public final class BindMainActivity extends MainActivity {
  public static void bind(MainActivity activity) {
    activity.setContentView(2130968604); // This sentence is newly generated in this change
    activity.mBtn = (android.widget.Button)activity.findViewById(2131427422);
    activity.mTextView = (android.widget.TextView)activity.findViewById(2131427423); }}Copy the code

This gives you the idea of using @bindLayout to generate the setContentView statement.

Tips

The Activity onCreate method now has only butterfork.bind (this); So with this line of code, we can create a BaseActivity

public abstract class BaseActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ButterFork.bind(this); }}Copy the code

So the subclass Activity doesn’t even need to write onCreate. It uses BindLayout to specify layoutId and BindView to specify viewId.


@BindLayout(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @BindView(R.id.btn)
    protected Button mBtn;

    @BindView(R.id.text)
    protected TextView mTextView;
}Copy the code