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