Android自动生成Shape资源文件(下)

827 阅读7分钟

系列文章目录

索引标题
第一章Android自动生成代码,可视化脚手架,将大大提高开发效率
第二章Android自动生成代码,可视化脚手架之环境搭建
第三章Android自动生成代码,可视化脚手架之基础信息配置
第四章Android自动生成Shape资源文件,迈出可视化脚手架第一步!(上)

系列文章Github开源地址(源码及各项资料不间断进行更新):

github.com/AbnerMing88…

上半部分,我们已经实现了可视化的基本信息配置,也就是可视化的第一个页面,这仅仅是一个前奏,在第一章里,我已经罗列了很多的功能,所以啊老铁们,抽个时间一定要去前边看一看,因为很多人的问题,我基本上前边都已经给出了解答,比如为什么不采用Android studio里的插件实现,工具在哪下载等等,都诉说的很清楚,这里就不一一赘述了,我们继续上半部分陈述。

针对Shape的实现,是可视化脚手架里最简单的,因为没有什么逻辑可言,都是现成的模板,无非就是改里面的属性而已,老铁们,记住啊,可视化说到底,确实没有什么技术含量,大多都是动态的更改,比如这个Shape,在正常的开发中,无非就是,实心的,空心的,渐变的,左上右下带角度的,那么针对常见的几种方式,我们做好模板,然后根据你在可视化工具的选择,动态的进行改变即可。

细节的源码,大家可以直接去GitHub上下载浏览,文章里就不过多的赘述,我们主要说下具体的实现方式。

一、页面绘制及保存到drawable文件夹下。

可视化的工具,之前说过,采用的是Web开发,相对于Android而言,还是比较简单的,无非就是各个标签的罗列,针对这个最终效果,毕竟我也不是搞Web开发的,也没有一个UI设计,所有的效果都是自己根据实际业务而来的,丑是丑了点,大家可以将就着看哈。

具体的页面,大家可以看源码中的shape.html这个文件,都是div标签,就不贴了,没啥好说的。

image.png

选择对应生成文件夹,这个功能是在你选择项目或者一个文件夹的时候,下面有很多的子目录,或者说直接是Module,这种情况下,你就可以进行选择生成到哪个Module下,这个选择是根据基础配置里的选择路径而来的,所以啊,基础配置选择项目生成路径,大家尽量选择一个Android项目。选择生成到哪个Module下之后,就会自动在保存在对应的res下的drawable文件夹下。

如何根据选择的Android项目,保存到对应的drawable下呢?这个就很简单了,项目路径(基础配置页面选择的路径)加上当前页面选择的生成文件夹路径,就可以很好的知道对应的drawable路径了。

image.png

每个module下的drawable是固定的,无非在上边的两个路径下,拼接后边的drawable路径即可。

如下图代码,selectPath是基础配置页面里选择的项目路径,selectFile是你选择的路径,后边拼接固定的 /src/main/res即可,再后面就是shape文件资源的名字了。

image.png

有一个需要注意的点是,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视图了。

image.png

目前的UI视图,很简单,无非就是三个功能,实心,空心,和渐变,当然了,这是我定义的,如果你还有拓展的功能,可以自己在源码中自己拓展。

image.png

根据这三个功能,我们定义好固定的三个模板,根据视图中相关选择,动态改变即可。

实心模板,注意看相关注释。

image.png

实现代码,需要注意,这里我在视图中,定义了左上,右上,左下,右下,四个选择框,就是对应代码里左上右下相关的角度,依次对应的点击记录为,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)模板

和实心的区别就是,加了一个边框,其他的都没怎么变。

image.png

代码实现:

//获取空心也就是带有边框的代码
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;

}

渐变模板:

image.png

代码实现:

//获取渐变
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个渠道包,敬请期待!

image.png