public void bindView(Object obj,Activity activity, View.OnClickListener clickListener){
bindView(obj,null,clickListener,activity);
}
public void bindView(Object obj,View v, View.OnClickListener clickListener,Activity activity){
Field[] field = obj.getClass().getDeclaredFields();
for (Field f:field){
try {
f.setAccessible(true);
Class clz = f.getType();
//若是是基本數據類型,不處理,直接跳過
if (clz.isPrimitive()){
continue;
}
//判斷實例是否是view的子類,不是直接跳過,不處理
if (!View.class.isAssignableFrom(f.getType())){
continue;
}
//獲取屬性名稱
String name = f.toString().substring(f.toString().lastIndexOf(".")+1);
//經過屬性名稱獲取view的id
int id = activity.getResources().getIdentifier(name,"id",activity.getPackageName());
//實例化view
View view;
if (v == null){
view = activity.findViewById(id);
}else {
view = v.findViewById(id);
}
//設置點擊事件
if (clickListener != null && view != null){
view.setOnClickListener(clickListener);
}
//設置view
f.set(obj,view);
} catch (IllegalAccessException e) {
Log.e("FindViewUtilsError","reason:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
}
複製代碼
上面的代碼其實已經可使用一句話代替findViewById了。固然,調用上面的代碼位置必須是頁面裝載完成後,不然view都是空的。上面雖然實現了不用頻繁的findViewById和略過了設置點擊監聽器的代碼,可是咱們仍然要手動聲明控件的屬性名稱,同時屬性名稱必須和layout中的id一致,這不只容易出錯,還有點麻煩,畢竟屬性名稱一寫錯,view就爲空了,這是不能忍的。那麼怎麼實現連同代碼聲明這個步驟一併去掉呢,這時候咱們就能夠開發一個插件來實現了。
java
public class Test extends AnAction {
private HashMap<String,String> map;
@Override
public void actionPerformed(AnActionEvent e) {
//獲取編輯器
Editor data = e.getData(PlatformDataKeys.EDITOR);
//獲取鼠標選中區域
SelectionModel selectionModel = data.getSelectionModel();
if (selectionModel == null){
return;
}
// TODO: insert action logic here
//這個map用於保存要寫入的聲明對象的類和類名。
map = new HashMap<>();
//讀取xml文件。selectionModel.getSelectedText()這句話爲獲取鼠標點擊高亮區部分的文字內容。下面整句話是在工程中尋找名字爲xxx.xml的文件。xxx值就是鼠標選中的高亮區的文字。
PsiFileSystemItem[] timeUtils = FilenameIndex.getFilesByName(e.getProject(), selectionModel.getSelectedText()+".xml", GlobalSearchScope.allScope(e.getProject()), false);
//若是沒找到直接結束掉操做。
if (timeUtils != null && timeUtils.length != 0){
//理論上來講,xml文件大多數狀況下名字是惟一的,因此這裏直接取了第一個xml文件來解析
VirtualFile virtualFile = timeUtils[0].getVirtualFile();
//獲取對應的文件
PsiFile manifestFile = PsiManager.getInstance(e.getProject()).findFile( virtualFile);
// XmlDocument xml = (XmlDocument) manifestFile.getOriginalFile();
//將文件解析爲xml文件
XmlFile xmlFile = (XmlFile) PsiManager.getInstance(e.getProject()).findFile(virtualFile);
XmlDocument document = xmlFile.getDocument();
if (document != null) {
//獲取頂層的xml內容
XmlTag rootTag = document.getRootTag();
if (rootTag != null) {
//獲取頂層後的下一層內容
XmlTag[] subTags = rootTag.getSubTags();
for (XmlTag tag : subTags) {
//獲取個各標籤的內容,裏面的方法使用了遞歸。
getTag(tag);
}
}
}
}else {
return;
}
//======================下面的代碼是將聲明和引用寫入到java文件中去==================================
Document document = data.getDocument();
Runnable runnable = new Runnable() {
@Override
public void run() {
//這個stringBuffer爲最終須要寫入的內容
StringBuffer stringBuffer = new StringBuffer();
//這個map用於裝載要引入的包,之因此使用hashmap,由於hashmap鍵不可重複,避免重複引入包名。
HashMap<String,String> importPackage = new HashMap<>();
for (String key:map.keySet()){
if (!map.get(key).contains(".")){
//應爲沒有包含「.」的通常是一些基本控件,基本控件基本都是出自android.widget裏面,因此這裏就固定引入android.widget.+控件類名
importPackage.put(map.get(key),"import android.widget."+map.get(key)+";\n");
stringBuffer.append("\tprivate "+map.get(key)+" "+key+";\n");
}else {
//若是包含「.」的,多是自定義佈局或谷歌後面加入的佈局,這些佈局通常前面都帶有一串包名加上控件類名,這種咱們直接引入便可。
importPackage.put(map.get(key),"import "+map.get(key)+";\n");
stringBuffer.append("\tprivate "+map.get(key).substring(map.get(key).lastIndexOf(".")+1)+" "+key+";\n");
}
//System.out.println(key+","+map.get(key));
}
StringBuffer packageResult = new StringBuffer();
for (String key:importPackage.keySet()){
packageResult.append(importPackage.get(key));
}
System.out.println(stringBuffer.toString());
//在類中寫入屬性。寫入位置爲類文件中第一次出現「{」的地點的後一行
document.insertString(document.getText().indexOf("{")+1,"\n"+stringBuffer.toString());
//寫入導包的代碼。寫入位置爲類文件中第一次出現「;」的位置,即package xxx;的後一行
document.insertString(document.getText().indexOf(";")+1,"\n"+packageResult.toString());
}
};
WriteCommandAction.runWriteCommandAction(e.getProject(),runnable);
}
private void getTag(XmlTag tag) {
//獲取標籤下的全部屬性
XmlAttribute[] attributes = tag.getAttributes();
for (XmlAttribute attribute:attributes){
//咱們只須要獲取到id的內容和標籤的內容便可。這裏的id內容是指安卓xml裏面的id值,標籤內容是指id該id指代的對象的名稱,好比LinearLayout等。
if ("android:id".equals(attribute.getName())){
//將id和該id指代的對象以鍵值對的形式保存到hashmap中去。
map.put(attribute.getValue().split("/")[1],tag.getName());
//System.out.println(attribute.getValue().split("/")[1]+","+tag.getName());
}
}
//判斷該標籤下是否存在子標籤,若是存在則進行遞歸調用
XmlTag[] subTags = tag.getSubTags();
for (XmlTag t : subTags) {
getTag(t);
}
}
}
複製代碼
寫完上面的代碼後就能夠實現代碼注入的功能了。上面的註釋也寫得不少了,固然,有些接口是intellij idea裏面獨有的,可能以前沒見過,不過沒關係,記住就行。
android