前言

经过项目的初步编写和进一步改造,RemoveButterKnife插件终于也有模有样了,但是,功能上仅仅支持Activity/Fragment的BindView注解。

关于编写和优化的过程可以看下面两篇文章 项目构造RemoveButterKnife

项目改进-重构RemoveButterKnife

当然,这里也附上这个项目的github地址

为了让插件支持更加彻底,我们还要支持组合自定义view以及viewholder中使用butterknife的情况,当然,我们也要支持OnClick注解以及一些其他的使用场景。

要增加哪些功能?

首先要确定需要增加哪些功能点,功能点的更新如下

  1. 增加对多moudle时的R2.id.xxx的支持
  2. 增加对OnClick注解的支持
  3. 增加对viewholder和自定义组合view的支持

确定增加功能的先后顺序

审阅我们的功能,发现1号功能是比较容易的,所以我们把1号定为优先 再次思考发现,2,3号功能之间存在关联性,即,3号功能提及的支持种类中也要支持Onclick注解的形式。 所以确定开发顺序为1->3->2

功能拆分

在这里我们使用github提供的project功能,具体分解如下 可以看到,我们分为了todo,doing,done三个部分,而且把每个任务都细分为了几个步骤,这样我们就可以在开发某一功能时保持专注,而不需要东写一点西写一点了

具体功能开发

1.对R2.id.xxxx的支持

由于我们原来的代码中寻找匹配使用的是正则表达式,如下

   String pattern = "^@(BindView|InjectView|Bind)\\((R.id.*)|(R2.id.*)\\)$";Pattern r = Pattern.compile(pattern);
复制代码

可以看到,我们的代码已经添加了对R2.id.xxx的支持,只需要给正则表达式增加一个条件。 最后,我们给这个功能添加上unit test,就可以完成对功能1的开发

2.对自定义view和viewholder的支持

首先,我们要判别一个类到底是自定义view还是viewholder,对于activity和fragment很简单,因为初始函数是不同的,一个是oncreate,一个是oncreateview,但是由于增加了支持种类,老办法就行不通了,这时我们需要使用idea的sdk来进行判断,代码如下

   GenCodeContext codeContext = new GenCodeContext(mClass, mFactory);String type = mClass.getSuperClassType().toString();if (type.contains("Activity")){codeContext.setStrategy(new ActivityStrategy(code,clickMap));}else if (type.contains("Fragment")) {codeContext.setStrategy(new FragmentStrategy(code,clickMap));}else if (type.contains("ViewHolder")||type.contains("Adapter<ViewHolder>")) {codeContext.setStrategy(new AdapterStrategy(code,clickMap));}else {codeContext.setStrategy(new CustomViewStrategy(code,clickMap));}codeContext.executeStrategy();
复制代码

对于原来的代码,我们已经能够找到activity/fragment的特定位置插入代码,但是对于自定义view和viewholder,又该用什么特征来定位该在哪里插入呢? 对于这个问题我们分情况讨论

  1. 自定义view 我们这里讨论的自定义view仅仅针对组合view,自绘和扩展方式不做讨论,因为这两种方式一般不会使用ButterKnife. 组合自定义view的特征 对于这种自定view,最大的特征就是在构造的时候会使用inflate方法将xml文件进行压入,那么,找到inflate或者R.layout.xxx的语句,这里就是我们插入生成后代码的位置 代码
   private PsiStatement findInflateStatement(PsiClass mClass){PsiStatement result = null;PsiMethod[] methods = mClass.getAllMethods();for (PsiMethod method:methods) {for (PsiStatement statement : method.getBody().getStatements()) {String returnValue = statement.getText();if (returnValue.contains("R.layout") || returnValue.contains("LayoutInflater.from(context).inflate")) {result = statement;break;}}}return result;}
复制代码
  1. viewholder 这里说的viewholder特指recyclerview.viewholder. 这种viewholder都有一个构造函数,参数为(View xxx)第一句是super(xxx); 我们可以基于这两个特征进行定位。
  private PsiStatement findSuperStatement(PsiMethod method,String viewName){PsiStatement result = null;for (PsiStatement statement : method.getBody().getStatements()) {String returnValue = statement.getText();if (returnValue.contains("super(" + viewName + ")")) {result = statement;break;}}return result;}
复制代码

那么,既然能够识别和找到哪里插入代码了,我们的类型支持也就水到渠成了。 在类型支持的时候,我们使用了策略模式,这样根据类型不同,设置不同的策略就可以方便的进行处理。 目录结构如下

3.对onclick注解的支持

我们对onclick的处理分以下几步

  1. 寻找注解
  2. 分析注解信息并保持
  3. 根据保存信息生成代码并插入

1.寻找注解

使用正则表达式很容易找到,这里不再重复贴代码

2.分析注解信息并保存

onclick注解有几种情况

  1. 单id/多id的绑定
  2. 点击函数是否有参数的情况 我们要获取的信息有以下几个
  3. 绑定的id列表
  4. 点击函数的名称,是否有参数,参数的类型 针对第二点,我们使用一个对象将其封装起来 保存获取信息我们使用一个Map<>来进行 代码:
 @Overridepublic void process() {String pattern = "^@OnClick\\(\\{*(R.id.*,|R.id.*|R2.id.*|R2.id.*,)+\\}*\\)$";Pattern r = Pattern.compile(pattern);for (int i = 0;i < currentDoc.length;i++){Matcher m = r.matcher(currentDoc[i].trim());currentDoc[i] = currentDoc[i].trim();if (m.find()) {method = detectMethod(currentDoc[i+1]);ids = detectID(currentDoc[i], method);methodAndIDMap.put(method,ids);deleteLineNumbers.add(i);}}}
复制代码

3.根据保存信息生成代码并插入

这步我们需要根据保存的信息进行代码生成和插入,我们主要讨论生成,插入部分和findviewbyid代码大同小异 我们已经知道了注解的id和点击对应的方法,那么我们复原的结果就应该是 findViewById(R.id.xxx).setOnclickListener(new OnclickListener(.... 我们需要注意的地方就是点击函数是否有参数,这会影响到我们生成的代码 看具体代码:

  protected StringBuilder getMethodInvokeString(ClickMehtod method) {StringBuilder methodString = new StringBuilder();if (method.isHaveArg()){methodString.append(method.getName()+"(("+method.getArgType()+")"+"v);");}else{methodString.append(method.getName()+"();");}return methodString;}protected String getOnClickCode(StringBuilder methodString, String id) {return "findViewById("+id+").setOnClickListener(new View.OnClickListener() {\n" +" @Override\n" +" public void onClick(View v){\n"+methodString.toString()+"}"+"});";}
复制代码

到了这里,我们的Onclick注解支持也完成了。

总结

通过对这个小小的插件的开发和重构以及功能添加,虽然项目很小,但是工程和面向对象的思想的重要性已经体现了出来,在一个拥有良好项目结构的工程下增加新功能是非常简答而明快的,如果像最初版本那样把所有的代码写在一个文件中而没有进行逻辑拆分的话,新增功能基本等于重写项目,这肯定是痛苦的。

还有一点值得一提,在做项目的时候第一步永远是总体构思,第二部是具体拆分,写代码这件事的优先级并没有那么高,容易犯的一个问题就是一提到某个功能马上就开始写具体代码,这样的结果往往费力不讨好,有一个明确的功能拆分和行进步骤会极大的增强开发体验。

至此,RemoveButterKnife系列文章就告一段落了,这几篇文章的目的不仅仅是记录开发RemoveButterKnife插件中的思路和遇到的问题,更重要的是总结了作者我开发软件项目的一个历程,而把这些写下来的过程,也是巩固这段历程的重要步骤。

转载于:https://juejin.im/post/5c21e86b51882561431a3cec

项目的升级-给RemoveButterKnife插件增加新功能相关推荐

  1. ET钱包1月21日早报|EOS钱包插件Scatter正尝试增加新功能

    在目前EOS生态发展出现了大量的DApp,由于其采用权益委托证明(Delegated Proof Of Stake,DPoS)的共识算法,通过21个超级节点(BP)来处理交易使其区块链交易速度明显提升 ...

  2. grep 模糊匹配_vim 的模糊查找插件 LeaderF 新功能介绍(二)

    前言 本文介绍自<vim 的模糊查找插件 LeaderF 新功能介绍>以后,LeaderF增加的一些新的功能. 异步grep Leaderf rg 此功能已经在<vim的grep插件 ...

  3. python的继承模式_Python之继承--增加新功能,不更改以前的代码

    在实际工作中,设计模式的时候,要增加新功能,不要更改以前的代码,这是封装和继承的高度总结. isinstance('对象',"类") 返回Ture or False issubcl ...

  4. 牛!上半年跨境电商进出口增长28.6%;亚马逊日本站自动定价工具增加新功能;TikTok试点视频简历项目…|洞悉跨境

    "上半年,我国跨境电商进出口增长28.6%,市场采购出口增长49.1%. " 每晚八点·洞悉跨境 [海关总署:上半年跨境电商进出口增长28.6%]今日,海关总署新闻发言人.统计分析 ...

  5. 整活插件 炉石传说_炉石传说:国服再搞大动作?官方插件神秘新功能压力测试开启...

    一入酒馆深似海,从此萌新变大神.大家好,我是酒馆店小二!据说点了关注的炉友,天天都能开橙卡,月月都能上传说哦~ 今天小二从营地看到了一则比较新奇的新闻,是关于炉石传说官方插件的.据悉,<炉石传说 ...

  6. Visual Studio 2017 15.6版本预览,增加新功能

    上周Visual Studio 2017 15.5 版本已正式发布,同时发布的还有 Visual Studio for Mac 7.3 . Visual Studio 2017 15.6 版本预览,这 ...

  7. 小说朗读器又增加新功能

    2021.7.3 1.增加打开word.rtf格式文件功能 2.增加拖放word.rtf格式文件功能 3.增加"关于"功能 2021.7.1 1.增加文本文件拖放功能 拖放在朗读的 ...

  8. rowid会变化么_【生活】微信对话框上线搜一搜功能 | 饿了么将增加新功能引吐槽...

    首先是微信的一个小变化,今天下午,微信对话框上线了一个搜一搜功能.本来还以为有多好用,结果...小白点了之后发现竟然是直接跳转到了微信里面那个搜一搜,感觉没多大用? 不过微信团队在介绍时表示用户可以直 ...

  9. 苹果应用商店增加新功能 可帮助分发“不适合公开发布应用”

    1月29日消息,据外媒报道,美国当地时间周五,苹果公司通过其开发者网站宣布,应用商店增加了一项新功能,"非上市应用"也可被发现和分发. 所谓"非上市应用"是指开 ...

  10. graphpad prism怎么添加图例_Graphpad官网刚刚升级了!听说,新功能吊打R语言...........

    解螺旋公众号·陪伴你科研的第2376天 科研之友,Graphpad Prism 9全新功能重磅出场! GraphPad Prism是一款高效易用的科研绘图工具,集生物统计.曲线拟合和科技绘图于一体,还 ...

最新文章

  1. php 加载一个文件路径_PHP文件加载过程
  2. Pandas一些小技巧
  3. 力扣 验证二叉搜索树
  4. oc21--super
  5. 23 android多线程
  6. 【Webcam设计】x264编码实现
  7. android 编译api,Android逆向利器,直接将apk转换为可二次开发Android工程,提供So hook Api,......
  8. 十大热门编程语言入门难度排名
  9. 凤凰系统安装到移动硬盘教程
  10. 《那些年啊,那些事——一个程序员的奋斗史》——100
  11. JS padStart()方法和padEnd()方法(ES6新增方法)
  12. Spring Security OAuth2.0认证授权知识概括
  13. MINIX - 磁盘块和缓冲块
  14. 银行定期存三个月利息计算机公式,银行存款利息如何计算?如定期三个月,半年、一年、二年 爱问知识人...
  15. Sklearn官方文档中文整理6——交叉分解,朴素贝叶斯和决策树篇
  16. :hover的知识点
  17. 2021年中国尼龙66(聚己二酰己二胺)行业现状分析:产能逐年增长,需求量增高[图]
  18. 2022最新视频打赏系统+全开源版本/附教程
  19. CRect类基本介绍
  20. 【Pytorch with fastai】第 2 章:从模型到生产

热门文章

  1. Windows环境下log4cxx的编译及使用(转载)
  2. 【译】BMP格式与JPG格式之间的区别
  3. 可视化图布局算法简介
  4. 16位伪指令汇编程序查看内存
  5. 二维数组,字符串,字符数组
  6. Python学习总结(4)——运算符
  7. 微波遥感SNAP(一)——基于Sentinel-1雷达数据重建数字高程模型
  8. endnote x8安装办法
  9. 狂神说Redis笔记三
  10. java邻接表无向图的创建_无向图的邻接表创建以及图的深度和…