編輯:關於Android編程
ButterKnife 很多人都用過,能節省很多代碼,最多的就是省去了很多 findViewById 語句。接下來自己寫一個,就叫
BBKnife 吧。
分析
在使用 ButterKnife 時,需要書寫下面的類似代碼,以一個 Activity 為例
ExampleActivity.java
class ExampleActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
在編譯後,會自動生成一個和 Activity 同名的
ExampleActivity$ViewBinder 的輔助類文件,並且生成 findViewById 相關的代碼
ExampleActivity$ViewBinder.java
public void bind(ExampleActivity activity) {
activity.subtitle = (android.widget.TextView) activity.findViewById(2130968578);
activity.footer = (android.widget.TextView) activity.findViewById(2130968579);
activity.title = (android.widget.TextView) activity.findViewById(2130968577);
}
那麼
BBKnife 庫要做的事情就很清楚了,生成輔助類。
動手開寫
步驟:
創建注解 編譯期間處理注解 生成輔助類 調用輔助類
0x00 創建工程
首先創建一個新的工程
然後創建一個 module , 選擇 java Library。
0x01 創建注解
這個注解作用於類的屬性上面,包含一個整型的參數,類似於
@BindView(R.id.title)
/**
* Created by hanks on 2016/7/31.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
0x02 編譯期間處理注解 && 生成輔助類
/**
* 編譯期間處理注解
* Created by hanks on 2016/7/31.
*/
@SupportedAnnotationTypes("xyz.hanks.BindView")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class BindViewProcessor extends AbstractProcessor {
private Messager messager;
public static final String SUFFIX = "$ViewBinder";
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
}
@Override
public boolean process(Set annotations, RoundEnvironment roundEnv) {
Map> map = new HashMap<>(); // key 是類名,value 是該類的注解元素
// 遍歷 BindView 注解的所有元素
for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
if (element == null || !(element instanceof VariableElement)) {
continue;
}
// 給屬性添加的注解
VariableElement variableElement = (VariableElement) element;
// 獲取屬性所在的類名
String className = element.getEnclosingElement().getSimpleName().toString();
List variableElementList = map.get(className);
if (variableElementList == null) {
variableElementList = new ArrayList<>();
map.put(className, variableElementList);
}
variableElementList.add(variableElement);
}
// 生成輔助類
generate(map);
return true;
}
private void generate(Map> map) {
if (null == map || map.size() == 0) {
return;
}
for (String className : map.keySet()) {
List variableElementList = map.get(className);
if (variableElementList == null || variableElementList.size() <= 0) {
continue;
}
// 獲取包名
String packageName = variableElementList.get(0).getEnclosingElement().getEnclosingElement().toString();
StringBuilder builder = new StringBuilder()
.append("package ").append(packageName).append(";\n\n")
.append("public class ").append(className).append(SUFFIX).append("{\n") // open class
.append(" public void bind(Object target) {\n")
.append(" ").append(className).append(" activity = (").append(className).append(")target;\n");
for (VariableElement variableElement : variableElementList) {
BindView bindView = variableElement.getAnnotation(BindView.class);
log(bindView.toString());
builder.append(" activity.").append(variableElement.getSimpleName().toString()).append("=(").append(variableElement.asType()).append(")activity.findViewById(").append(bindView.value()).append(");\n");
}
builder.append(" }\n}\n");
// write the file
try {
String bindViewClassName = packageName + "." + className + SUFFIX;
JavaFileObject source = processingEnv.getFiler().createSourceFile(bindViewClassName);
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
log(e.getMessage());
}
}
}
private void log(String msg) {
messager.printMessage(Diagnostic.Kind.WARNING, msg);
}
}
注意點:
- 添加 @SupportedAnnotationTypes
@SupportedSourceVersion 注解, 原因: AbstractProcessor 中做了相關校驗(看 AbstractProcessor 源碼)。
- 打印消息是由 processingEnv.getMessager().printMessage 或者輸出日志文件,原因:編譯期間做的處理,不能使用 System.out 或者 Log.i
- 處理注解的時候需要獲取類名或者包名,需要注意獲取的是全路徑還是簡單名稱。
- 依照需要生成輔助類文件。
在 main 下新建 resources > META_INF > services 目錄,創建 javax.annotation.processing.Processor 文件,javac 會自動檢查和讀取
javax.annotation.processing.Processor 中的內容,並且注冊
BindViewProcessor 作為注解處理器。
0x03 調用輔助類
當執行
BBKnife.bind(activity) 的時候調用我們生成的輔助類,輔助類內部進行
findViewById 從而進行注入。
public class BBKnife {
// 調用我們生成的輔助類
public static void bind(Object view){
try {
String cla = view.getClass().getName()+BindViewProcessor.SUFFIX;
Class clazz = Class.forName(cla);
Object instance = clazz.newInstance();
Method bind = clazz.getMethod("bind",Object.class);
bind.invoke(instance,view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
導出 bbknife.jar
在 build/libs 目錄下有自動導出的 jar 文件,
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="使用">使用
復制到 app 下的 libs 進行引用。
app 下的 build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt' // 使用 apt
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // 添加引用
}
}
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "xyz.hanks.bbknifeproject"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// 設置java 版本
compileOptions{
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
}
在 MainActivity 使用
public class MainActivity extends AppCompatActivity {
@BindView(R.id.bb_button) Button mButton;
@BindView(R.id.bb_image) ImageView mImage;
@BindView(R.id.bb_text) TextView mText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BBKnife.bind(this);
mButton.setText("hanks.xyz");
mText.setText("hanks.xyz");
mImage.setImageResource(R.mipmap.ic_launcher);
}
}
看最後生成的輔助類
MainActivity$ViewBinder.java
public class MainActivity$ViewBinder{
public void bind(Object target) {
MainActivity activity = (MainActivity)target;
activity.mButton=(android.widget.Button)activity.findViewById(2131427412);
activity.mImage=(android.widget.ImageView)activity.findViewById(2131427414);
activity.mText=(android.widget.TextView)activity.findViewById(2131427413);
}
}
在命令行[CMD]使用emulator.exe啟動Android模擬器兩種方式:- emulator -avd (AVD名稱)- emulator -data (鏡像文件
吸引用戶的眼球,是我們至死不渝的追求;第一時間呈現最有價值的信息,簡明大方,告訴客戶,你的選擇是多麼的明智,這正是你尋覓已久的東西。分組的應用場合還是很多的,有數據集合的
需求分析: 通過ContentResolver操作內容提供者的數據,將姓名,電話顯示在listView中,並帶有添加和刪除按鈕進行相應操作 實現思路步驟: Adate
手機QQ同步助手可以備份短信和聯系人到網絡中,你只要下載並安裝QQ同步助手並用QQ號登錄,就可以將你的短信和聯系人備份到網絡中,當你換手機或者格機的時候就可