系列文章Github开源地址(源码及各项资料不间断进行更新):
https://github.com/AbnerMing888/AndroidShortcutTools
上半部分,我们已经实现了可视化的基本信息配置,也就是可视化的第一个页面,这仅仅是一个前奏,在第一章里,我已经罗列了很多的功能,所以啊老铁们,抽个时间一定要去前边看一看,因为很多人的问题,我基本上前边都已经给出了解答,比如为什么不采用Android studio里的插件实现,工具在哪下载等等,都诉说的很清楚,这里就不一一赘述了,我们继续上半部分陈述。
针对Shape的实现,是可视化脚手架里最简单的,因为没有什么逻辑可言,都是现成的模板,无非就是改里面的属性而已,老铁们,记住啊,可视化说到底,确实没有什么技术含量,大多都是动态的更改,比如这个Shape,在正常的开发中,无非就是,实心的,空心的,渐变的,左上右下带角度的,那么针对常见的几种方式,我们做好模板,然后根据你在可视化工具的选择,动态的进行改变即可。
细节的源码,大家可以直接去GitHub上下载浏览,文章里就不过多的赘述,我们主要说下具体的实现方式。
一、页面绘制及保存到drawable文件夹下。
可视化的工具,之前说过,采用的是Web开发,相对于Android而言,还是比较简单的,无非就是各个标签的罗列,针对这个最终效果,毕竟我也不是搞Web开发的,也没有一个UI设计,所有的效果都是自己根据实际业务而来的,丑是丑了点,大家可以将就着看哈。
具体的页面,大家可以看源码中的shape.html这个文件,都是div标签,就不贴了,没啥好说的。
选择对应生成文件夹,这个功能是在你选择项目或者一个文件夹的时候,下面有很多的子目录,或者说直接是Module,这种情况下,你就可以进行选择生成到哪个Module下,这个选择是根据基础配置里的选择路径而来的,所以啊,基础配置选择项目生成路径,大家尽量选择一个Android项目。选择生成到哪个Module下之后,就会自动在保存在对应的res下的drawable文件夹下。
如何根据选择的Android项目,保存到对应的drawable下呢?这个就很简单了,项目路径(基础配置页面选择的路径)加上当前页面选择的生成文件夹路径,就可以很好的知道对应的drawable路径了。
每个module下的drawable是固定的,无非在上边的两个路径下,拼接后边的drawable路径即可。
如下图代码,selectPath是基础配置页面里选择的项目路径,selectFile是你选择的路径,后边拼接固定的 /src/main/res即可,再后面就是shape文件资源的名字了。
有一个需要注意的点是,drawable文件夹可能不在,那么你就要判断drawable是否存在了,存在就执行写入,不存在,就先创建drawable文件夹,然后在执行写入,这个写入就是,写入生成shape的代码,后面会说到。
//先判断drawable文件是否存在,不存在去创建fs.readdir(endPathFile, function (err, files) { if (err) { return } varbooDrawable=false; files.forEach(function (item) { if ("drawable"===item) { booDrawable=true; } }); //存在if (booDrawable) { writeDrawable(endPath, endShapeText); } else { //不存在,创建fs.mkdir(endPathFile+"/drawable", function (err) { if (err) { return; } writeDrawable(endPath, endShapeText); }); } });
还有一个问题需要注意,虽然我们说了,一定要选择Android项目,我相信,肯定有很多小老弟,就不选择Android项目,那么在不是Android项目的前提下,就不能执行同样的逻辑了,需要特殊处理,毕竟不是Android项目的路径,src,res的文件夹可能都不存在,所以,针对这个特殊情况,我们要提前的判断了。
我是这样判断的,当然大家如果有好的判断方式,也可以积极的共享哈,首先,拿到用户选择的项目路径,也就是基础配置页面选择的路径,然后进行遍历,遍历的时候当出现Android标识性文件的时候,我用个变量做个累加,比如出现,app文件夹,gradle.properties文件,build.gradle文件等,当定义的变量累计到定义的标识后,就认为选择的是一个Android项目。
//检测选择的是否是一个Android项目,通过是否包含app,gradle,settings.gradle,当然也可以判断其他if (item==="app"||item==="gradle"||item==="settings.gradle") { numAndroid++; }
是Android项目就可以生成到对应的drawable下,否则就普通文件夹下,具体可以看源码示例。
二、根据对应模板代码和UI视图选择,生成对应的资源
模板是固定的,唯一改变的就是里面的属性,比如下图,无非就是radius和color里属性需要改,其他的都是固定的,需要改的就要绘制相关的UI视图了。
目前的UI视图,很简单,无非就是三个功能,实心,空心,和渐变,当然了,这是我定义的,如果你还有拓展的功能,可以自己在源码中自己拓展。
根据这三个功能,我们定义好固定的三个模板,根据视图中相关选择,动态改变即可。
实心模板,注意看相关注释。
实现代码,需要注意,这里我在视图中,定义了左上,右上,左下,右下,四个选择框,就是对应代码里左上右下相关的角度,依次对应的点击记录为,0,1,2,3。
//获取实心的代码functiongetSolidText(radius, color, checkText) { varcontent="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+"<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"; //不为空if (radius!=""&&radius!=null&&selectDp!=null&&selectDp!="") { //取出默认的dp配置前缀radius=selectDp+radius; } else { radius= (radius==null||radius==="") ?"" : radius+"dp"; } if (radius!=null&& (checkText==""||checkText=="0123")) { //全部content=content+" <corners android:radius=\""+radius+"\"></corners>\n"; } else { content=content+" <corners\n"; if (checkText.indexOf("0") !=-1) { content=content+" android:topLeftRadius=\""+radius+"\"\n"; } if (checkText.indexOf("1") !=-1) { content=content+" android:topRightRadius=\""+radius+"\"\n"; } if (checkText.indexOf("2") !=-1) { content=content+" android:bottomLeftRadius=\""+radius+"\"\n"; } if (checkText.indexOf("3") !=-1) { content=content+" android:bottomRightRadius=\""+radius+"\"\n"; } content=content+" />\n"; } //基础信息color不为空,就追加前缀color=getEndColor(color); content=content+" <solid android:color=\""+color+"\" />\n"; content=content+"</shape>"; returncontent; }
空心(带有边框的shape)模板
和实心的区别就是,加了一个边框,其他的都没怎么变。
代码实现:
//获取空心也就是带有边框的代码functiongetStrokeText(radius, solid, react, reactSize, checkText) { //不为空if (radius!=""&&radius!=null&&selectDp!=null&&selectDp!="") { //取出默认的dp配置前缀radius=selectDp+radius; } else { radius=radius+"dp"; } if (reactSize!=""&&reactSize!=null&&selectDp!=null&&selectDp!="") { //取出默认的dp配置前缀reactSize=selectDp+reactSize; } else { reactSize=reactSize+"dp"; } //边框颜色//基础信息color不为空,就追加前缀react=getEndColor(react); varcontent="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+"<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"+" >\n"; content=content+" <stroke\n"+" android:width=\""+reactSize+"\"\n"+" android:color=\""+react+"\" />\n"; if (radius!=null&& (checkText==""||checkText=="0123")) { //全部content=content+" <corners android:radius=\""+radius+"\" />\n"; } else { content=content+" <corners \n"; if (checkText.indexOf("0") !=-1) { content=content+"android:topLeftRadius=\""+radius+"\"\n"; } if (checkText.indexOf("1") !=-1) { content=content+" android:topRightRadius=\""+radius+"\"\n"; } if (checkText.indexOf("2") !=-1) { content=content+" android:bottomLeftRadius=\""+radius+"\"\n"; } if (checkText.indexOf("3") !=-1) { content=content+" android:bottomRightRadius=\""+radius+"\"\n"; } content=content+" />\n"; } solid=getEndColor(solid); content=content+" <solid android:color=\""+solid+"\"/>\n"; content=content+"</shape>"returncontent; }
渐变模板:
代码实现:
//获取渐变functiongetGradientXml(radius, checkText) { //渐变varinputReactStartColor=$(".input_react_start_color").val();//起始颜色varinputReactCenterColor=$(".input_react_center_color").val();//中间颜色varinputReactEndColor=$(".input_react_end_color").val();//结束颜色letinputReactGradientRadius=$(".input_react_gradient_radius").val();//渐变角度letshapeGradientType=$("input[name='shapeGradientType']:checked").val(); inputReactStartColor=getEndColor(inputReactStartColor); inputReactCenterColor=getEndColor(inputReactCenterColor); inputReactEndColor=getEndColor(inputReactEndColor); if (inputReactGradientRadius==null||inputReactGradientRadius=="") { showToast("请输入渐变角度"); return""; } varsgType; if (shapeGradientType==0) { sgType="linear"; } elseif (shapeGradientType==1) { sgType="radial"; } else { sgType="sweep"; } vargradient="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+"<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"+"\n"+" <gradient\n"+" android:angle=\""+inputReactGradientRadius+"\"\n"; if (inputReactCenterColor!=null&&inputReactCenterColor!="") { gradient=gradient+" android:centerColor=\""+inputReactCenterColor+"\"\n"; } gradient=gradient+" android:endColor=\""+inputReactEndColor+"\"\n"+" android:startColor=\""+inputReactStartColor+"\"\n"+" android:type=\""+sgType+"\" />\n"//不为空if (radius!=""&&radius!=null) { //取出默认配置前缀,若不为空,就追加if (selectDp!=""&&selectDp!=null) { radius=selectDp+radius; } else { radius=radius+"dp"; } if (checkText==""||checkText=="0123") { //全部gradient=gradient+" <corners android:radius=\""+radius+"\"></corners>\n"; } else { gradient=gradient+" <corners\n"; if (checkText.indexOf("0") !=-1) { gradient=gradient+" android:topLeftRadius=\""+radius+"\"\n"; } if (checkText.indexOf("1") !=-1) { gradient=gradient+" android:topRightRadius=\""+radius+"\"\n"; } if (checkText.indexOf("2") !=-1) { gradient=gradient+" android:bottomLeftRadius=\""+radius+"\"\n"; } if (checkText.indexOf("3") !=-1) { gradient=gradient+" android:bottomRightRadius=\""+radius+"\"\n"; } gradient=gradient+" />\n"; } } gradient=gradient+"</shape>"; returngradient; }
当然了具体逻辑相关的,大家可以查看源码,有任何问题,也可以留言咨询。
选择颜色这块用到了一个三方,也放到源码里面了,方便大家进行取色,UI图里特意设置了一个名字,因为shape资源生成有一定的规范用名,特别是在组件化开发的项目,所以简单的设置了一下规范用名,可以一键获取,根据你设置的颜色,角度来进行填充。
Shape的可视化生成就是如此的简单,好了各位老铁,下一篇,我们搞一个可视化的多渠道打包,一秒可生成N个渠道包,敬请期待!