AndroidStudio编写插件超详细教程(二)
本教程从0开始,边探索边讲解思路,保证详细~~~写的时候发现有点长,准备分2-3次写完吧。
AndroidStudio编写插件超详细教程(一)
AndroidStudio编写插件超详细教程(二)
AndroidStudio编写插件超详细教程(三)
- 获取xml文件实体对象
- 获取类名和id一一对应的对象集合
获取xml文件实体对象
最近重构项目实在有点忙,两篇中间也是隔的时间有点久,尽量抽时间多写一下
吧!
我们先来整理一下我们手上有的“资源”。
上一篇文章,我们得到了PsiElement(光标选到的元素)
,Editor(光标等一写编辑上的操作)
,xxx.xml(资源文件的名字)
。
接下来,我们的任务是根据名字取到这个xml文件的实体。
上次我们通过官网,找到了一个方法
FilenameIndex.getFilesByName(project, name, scope);
很显然,这个方法可以通过文件名,得到PsiFile。不过,这个方法除了project和name之外,还需要一个scope。字面意思应该是个范围。我们用编辑器看一下这个方法,第三个参数需要一个GlobalSearchScope
。
官网搜了一下这个类,似乎并没有搜到介绍它的。我们先看一下这个类有没有什么静态方法可以得到它的实体。
看了一下,通过文件得到肯定是没办法了。看来看去,似乎也就module这个东西有点希望。通过编辑器一看,发现有个ModuleUtil,里面有一个findModuleForPsiElement()
方法,所需的参数正好是我们有的psiElement。先不管这个能不能行了,反正有参数了,先试试再说。
Module moduleForPsiElement = ModuleUtil.findModuleForPsiElement(psiElement);
GlobalSearchScope searchScope = GlobalSearchScope.moduleScope(moduleForPsiElement);
Project project = anActionEvent.getData(DataKeys.PROJECT);
//得到所有名字为name的文件
PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, name, searchScope);
for (PsiFile file : psiFiles) {
//得到的psiFiles长度为1,打印一下文件名name和内容text,发现名字为我们需要的xxxx.xml,内容也和文件里的内容一致。
System.out.println(file.getName());
System.out.println(file.getText());
}
通过这个方法,我们得到了我们要的xml文件实体类。
获取类名和id一一对应的对象集合
接下来就是遍历里面的id了。为了避免有bug,我们先多放几个控件,包括viewgroup的嵌套,还有include和自定义view。大概长这样。
<!-- 伪代码去除了无用代码,只保留了id -->
<!-- activity_main.xml -->
<RelativeLayout
android:id="@+id/rlVidwGroup">
<TextView
android:id="@+id/tvHelloWorld"/>
<ImageView
android:id="@+id/ivIcon"/>
<LinearLayout
android:id="@+id/llViewGroup"
android:orientation="vertical">
<TextView
android:id="@+id/tvInner"/>
<include
layout="@layout/include_plugin_test"/>
</LinearLayout>
<com.jarvis.myapplication.app.Custom
android:id="@+id/custom"/>
</RelativeLayout>
<!-- include_plugin_test.xml -->
<LinearLayout
android:id="@+id/llIncludeViewGroup"
android:orientation="vertical">
<TextView
android:id="@+id/tvInclude"/>
</LinearLayout>
不知道大家还记不记得,我们上一次再官网找到了一个能够递归遍历psiFile内元素的方法
其中参数PsiRecursiveElementWalkingVisitor有很多子类,其中就有XmlRecursiveElementVisitor,看名字正是我们需要的。(其实这里用PsiRecursiveElementWalkingVisitor也行,只不过需要把返回值手动强转成XML文件的元素)。我们调用一下这个方法,并且重写我们需要的方法。
很明显。我们可能用到的是visitXmlAttribute和visitXmlTag。但是由于我们得到id时还需要得到它对应的类,以便于我们生成参数类型,所以这里我们必须用visitXmlTag得到标签类。并且创建一个bean类,里面暂时存储我们一会得到的类名和id。
public class ResIdBean {
String name;
String id;
public ResIdBean(String name, String id) {
this.name = name;
this.id = id;
}
}
我们先考虑一般情况,也就是没有include的时候。这时候比较简单,类名就是标签名。自定义控件打出来的是完整的类名。
resFile.accept(new XmlRecursiveElementVisitor(true) {
@Override
public void visitXmlTag(XmlTag tag) {
super.visitXmlTag(tag);
String className = tag.getName();
}
});
接下来我们要得到控件的id。通过tag获取名为”android:id”的attribute属性。然后分割一下字符串就可以得到对应的id了。我们打印一下,确实是我们想要的值,我们把类名和id存在一个List集合里备用。
ArrayList<ResIdBean> resIdBeans = new ArrayList<>();
resFile.accept(new XmlRecursiveElementVisitor(true) {
@Override
public void visitXmlTag(XmlTag tag) {
super.visitXmlTag(tag);
XmlAttribute attribute = tag.getAttribute("android:id");
if (attribute != null) {
String idValue = attribute.getValue();
if (idValue != null && idValue.startsWith("@+id/")) {
String[] split = idValue.split("/");
String className = tag.getName();
String id = split[1];
System.out.println(className + "---" + id);
resIdBeans.add(new ResIdBean(className, id));
}
}
}
});
接下来就是获取include标签中的类名和id了。由于include中只有xml文件的名字,所以,和之前一样,我们需要先得到xml文件的名字,然后得到xml文件的实体类,在进行同样的操作得到类名和id,如果include中还有include,我们还需要进行这样的操作。显然这是一个递归。
我们完善一下代码,简单封装一下之前写的方法。如果tagName为include就继续通过文件名找到文件,然后遍历获得id,如果不是include就放入集合中。封装好的代码大概是这样。
//伪代码,需要根据前面讲的自行修改。
private void getResIdBeans(PsiFile psiFile, ArrayList<ResIdBean> container) {
psiFile.accept(new XmlRecursiveElementVisitor(true) {
super.visitXmlTag(tag);
if (tag.getName().equals("include")) {
String xmlName = String.format("%s.xml", name);
getResIdBeans(include, container);
PsiFile fileByName = getFileByName(psiFile, xmlName);
getResIdBeans(fileByName, container);
}else{
container.add(new ResIdBean(className, id));
}
}
}
最后我们往这个方法中传入的ArrayList<ResIdBean> container
里面就放好了我们存的ResIdBean了。
现在我们已经得到了我们选中xml文件中所有的id集合了。
文章写完后,代码会上传到github。需要的朋友可以去github下载。
代码github地址:https://github.com/GeniusLiu/FindViewById-Plugin
这个代码只是一个小demo,应该还会更新,看到的朋友给个star呗~~~