系列文章目录
第一章:Android自动生成代码,可视化脚手架,将大大提高开发效率 |
第二章:Android自动生成代码,可视化脚手架之环境搭建 |
第三章:Android自动生成代码,可视化脚手架之基础信息配置 |
第四章:Android自动生成Shape资源文件,迈出可视化脚手架第一步(上) |
系列文章Github开源地址(源码及各项资料不间断进行更新):
https://github.com/AbnerMing888/AndroidShortcutTools
上半部分,我们已经实现了可视化的基本信息配置,也就是可视化的第一个页面,这仅仅是一个前奏,在第一章里,我已经罗列了很多的功能,所以啊老铁们,抽个时间一定要去前边看一看,因为很多人的问题,我基本上前边都已经给出了解答,比如为什么不采用Android studio里的插件实现,工具在哪下载等等,都诉说的很清楚,这里就不一一赘述了,我们继续上半部分陈述。
针对Shape的实现,是可视化脚手架里最简单的,因为没有什么逻辑可言,都是现成的模板,无非就是改里面的属性而已,老铁们,记住啊,可视化说到底,确实没有什么技术含量,大多都是动态的更改,比如这个Shape,在正常的开发中,无非就是,实心的,空心的,渐变的,左上右下带角度的,那么针对常见的几种方式,我们做好模板,然后根据你在可视化工具的选择,动态的进行改变即可。
细节的源码,大家可以直接去GitHub上下载浏览,文章里就不过多的赘述,我们主要说下具体的实现方式。
一、页面绘制及保存到drawable文件夹下。
可视化的工具,之前说过,采用的是Web开发,相对于Android而言,还是比较简单的,无非就是各个标签的罗列,针对这个最终效果,毕竟我也不是搞Web开发的,也没有一个UI设计,所有的效果都是自己根据实际业务而来的,丑是丑了点,大家可以将就着看哈。
具体的页面,大家可以看源码中的shape.html这个文件,都是div标签,就不贴了,没啥好说的。4
选择对应生成文件夹,这个功能是在你选择项目或者一个文件夹的时候,下面有很多的子目录,或者说直接是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
}
var booDrawable = 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。
//获取实心的代码
function getSolidText(radius, color, checkText) {
var content = "<?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>";
return content;
}
空心(带有边框的shape)模板
和实心的区别就是,加了一个边框,其他的都没怎么变。
代码实现:
//获取空心也就是带有边框的代码
function getStrokeText(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);
var content = "<?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>"
return content;
}
渐变模板:
代码实现:
//获取渐变
function getGradientXml(radius, checkText) {
//渐变
var inputReactStartColor = $(".input_react_start_color").val();//起始颜色
var inputReactCenterColor = $(".input_react_center_color").val();//中间颜色
var inputReactEndColor = $(".input_react_end_color").val();//结束颜色
let inputReactGradientRadius = $(".input_react_gradient_radius").val();//渐变角度
let shapeGradientType = $("input[name='shapeGradientType']:checked").val();
inputReactStartColor = getEndColor(inputReactStartColor);
inputReactCenterColor = getEndColor(inputReactCenterColor);
inputReactEndColor = getEndColor(inputReactEndColor);
if (inputReactGradientRadius == null || inputReactGradientRadius == "") {
showToast("请输入渐变角度");
return "";
}
var sgType;
if (shapeGradientType == 0) {
sgType = "linear";
} else if (shapeGradientType == 1) {
sgType = "radial";
} else {
sgType = "sweep";
}
var gradient = "<?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>";
return gradient;
}
当然了具体逻辑相关的,大家可以查看源码,有任何问题,也可以留言咨询。
选择颜色这块用到了一个三方,也放到源码里面了,方便大家进行取色,UI图里特意设置了一个名字,因为shape资源生成有一定的规范用名,特别是在组件化开发的项目,所以简单的设置了一下规范用名,可以一键获取,根据你设置的颜色,角度来进行填充。
Shape的可视化生成就是如此的简单,好了各位老铁,下一篇,我们搞一个可视化的多渠道打包,一秒可生成N个渠道包,敬请期待!