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