在前面的一步一步,實現自己的ButterKnife(一)中,我們已經知道了如何實現在Activity中用註解@BindView來實現findViewById的功能。
文章釋出後,有朋友問能不能將setContentView()這個方法也給用註解來Bind,這個目前ButterKnife中並沒有做到。如果實現了的話,那麼只需要在BaseActivity中的onCreate方法中加入ButterKnife.bind(this);那麼所以BaseActivity的子類就可以連onCreate方法都不用寫了,減輕了多少程式碼量啊!不懂得偷懶的程式設計師不是好程式設計師(^__^) 。
那麼,我們來實現,用註解來生成setContentView程式碼吧。
本文實現的功能
使用@BindLayout來註解一個Activity,為Activity自動在onCreate方法中新增setContentView語句,進而免去onCreate方法的手工程式碼實現。專案連結不變:點選前往Github:ButterFork.
改進細節
首先當然是在annotation module中增加註解:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface BindLayout {
int value();
}複製程式碼
我們是用來給類進行註解的,所以這裡的Target是ElementType.TYPE了,而不是FIELD。
然後,在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);
}
}複製程式碼
看過文章(一)的朋友應該記得,ButterFork.bind()實質是通過反射去呼叫了自動生成的BindMainActivity.bind()方法,這裡邊有@BindView自動生成的findViewById語句,這次,我們把setContentView語句也寫進去就可以了。
要自動生成語句,當然要改動的就是compiler module裡的BindProcessor了,新增的程式碼只5行,見註釋。
private void generateJavaClass() {
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);
//新增程式碼 start
BindLayout bindLayoutAnno = enclosedElem.getAnnotation(BindLayout.class);
if (bindLayoutAnno != null){
methodSpecBuilder.addStatement(String.format(Locale.US,"activity.setContentView(%d)",bindLayoutAnno.value()));
}
//新增程式碼 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();
}
}複製程式碼
很簡單,遍歷到當前類,如果有被BindLayout進行註解,就把該註解的value,也就是layoutId拿來生成setContentView語句,最後自動生成的類如下:
public final class BindMainActivity extends MainActivity {
public static void bind(MainActivity activity) {
activity.setContentView(2130968604); //這句是本次改動中新生成的
activity.mBtn = (android.widget.Button)activity.findViewById(2131427422);
activity.mTextView = (android.widget.TextView)activity.findViewById(2131427423);
}
}複製程式碼
這樣,就實現了利用@BindLayout來生成setContentView語句的想法。
Tips
Activity的onCreate方法中,現在只有ButterFork.bind(this);這一句程式碼了,我們可以建一個BaseActivity
public abstract class BaseActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ButterFork.bind(this);
}
}複製程式碼
這樣一來,子類Activity連onCreate方法都不用寫了,通過BindLayout來指定layoutId,通過BindView來指定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;
}複製程式碼