文档章节

一款快速生成安卓shape的插件--NoCodeShape

落叶挽歌
 落叶挽歌
发布于 11/27 16:20
字数 1997
阅读 272
收藏 3

一、NoCodeShape介绍

NoCodeShape是一个能可视化界面操作生成Android中Shape.xml的Android Studio插件。对于新手能更好的去接受Android中相对应的属性,对于资深程序员则能简化操作,快速生成shape.xml。

二、NoCodeShape使用方法

1. 下载和安装

同一般的Android Studio插件下载一样,可以直接Preferences->Plugins 搜索 NoCodeShape搜索出来结果直接安装再重启即可。

在这里插入图片描述

也可以去jetbrains插件管理的官网下载各个版本的插件 https://plugins.jetbrains.com/plugin/13325-nocodeshape/versions
然后Preferences->Plugins 再Install plugins from disk从本地安装

2. 如何使用

在新建一个shape.xml文件后,右键选择NoCodeShape或者直接按快捷键Common+U

在这里插入图片描述

然后选择自己想要的属性,随着点击事件的进行会生成相关xml代码,并在Android Studio右边有对应的shape形状的展示。如果对应shape.xml有相关属性,NoCodeShape也会生成对应shape.xml属性的操作界面,非常方便。

示例:

在这里插入图片描述

三、实现原理

实现原理总体来说并不复杂,主要是界面相关操作逻辑比较繁琐。

对于新生成的一个shape.xml来说只需要弹出一个新的操作界面,用户只需要点击对应模块的属性即可。项目使用了单例模式+Bulider建造者模式去管理各个Shape属性,分别生成 Shape、Solid、Corners、Stroke、Gradient的单例,其内部拥有一个Builder用来去承各类型的具体属性。

通过界面的操作,对其内部的Builder进行数据的填充,最后在完成各类操作后,将各类型中的Builder中的所有属性提取出并生成一份完整的xml字符串并将其粘贴到Android Studio的操作界面上。

实现主要分为两大类:

1. 拼接生成xml字符串

字符串拼接算是其中最复杂的部分,一是各shape的类型拥有较多数据,其中一些属性有逻辑存在性,二是生成最终Android Studio的xml字符串的时候格式存在比较多的处理。对于各类属性都继承于BaseXml,其内部拥有一个静态内部Builder类,以相对简单的Solid类来举例,如下所示:

public class Solid extends BaseXml {

    private static Builder builder;
    private static Solid instance = null;

    public static Solid getInstance() {
        if (instance == null) {
            builder = new Builder();
            instance = new Solid();
        }
        return instance;
    }
    
 public static class Builder extends BaseBuilder {
        String color;
        String colorValue;

        public void setColor(String color) {
            this.colorValue = color;
            this.color = getAttrWithOutUnitStr("color", color);
        }
        @Override
        public String getBuilderString() {
            return StringUtils.getString(color);
        }

        @Override
        public void clearData() {
            StringUtils.clearObjectData(this);
        }

        @Override
        public void analysisAttribute(Attributes attributes) {
            Solid.getInstance().setChecked(true);
            setColor(attributes.getValue("android:color"));
        }
    }

其类继承于抽象类BaseXml,代码如下所示:

public abstract class BaseXml {
    private boolean isChecked = false;
    public String getCloser() {
        return " />";
    }
    public String getStartTag() { 
        return "";
    }
    public String generateXmlString() {
        return "";
    }
    protected String getLineFeedString() { 
        return "\n";
    }
    public boolean isChecked() {
        return isChecked;
    }
    public BaseXml setChecked(boolean checked) {
        isChecked = checked;
        return this;
    }
}

抽象类提取出来在字符串拼接阶段,各类型常用的基本操作,例如:返回“<solid”这类的开始标签," />"结束标签等

其内部BaseXml拥有对应类拥有的所有属性的常用操作,其继承与抽象类BaseBuilder,代码如下所示

public abstract class BaseBuilder {
    public abstract String getBuilderString();
    public abstract void clearData();
    public abstract void analysisAttribute(Attributes attributes);
    protected final String getAttrWithUnitStr(String attributeType, String value) {
        String unit;
        if (TextUtils.isEmpty(value)) {
            return "";
        }
        if (value.contains("px") || value.contains("dp")) {
            unit = "";
        } else {
            unit = DefaultData.UNIT;
        }
        return "android:" + attributeType + "=\"" + value + unit + "\"";
    }
    protected final String getAttrWithOutUnitStr(String attributeType, String value) {
        if (TextUtils.isEmpty(value)) {
            return "";
        }
        return "android:" + attributeType + "=\"" + value + "\"";
    }
    protected final String getValueOutUnit(String value) {
        if (TextUtils.isEmpty(value)) {
            return value;
        }
        return value.replace("dp", "").replace("px", "");
    }
}

BaseBuilder内部封装了一些属性的常用操作,例如生成:android:color="#FFFFFF"这样的字符串,获取是否带有单位的字串等。 并提供三个抽象方法:

   public abstract String getBuilderString(); //获取Builder中所有属性拼接好的字符串
   public abstract void clearData(); //清空Builder内部属性值
   public abstract void analysisAttribute(Attributes attributes); //分析xml数据中的值,这个在第二大点“将原有shape.xml字符串转化为对应操作界面”中将会讲到

以上是基本数据的构造,在最后在控件交互的地方会调用CommonAction类的refreshAndWriteData

abstract class CommonAction {
    JComponent component;
    NoShapeDialog noShapeDialog;

    void refreshAndWriteData() {
        NoCodeShapeAction.callWriteData();
    }
}

最后会调用基本的Action中的writeData()方法,其具体逻辑为

 /**
     * 数据写入xml文件
     */
    private static void writeData() {
        final Document document = FileDocumentManager.getInstance().getDocument(file);
        if (document == null) {
            try {
                throw new Exception("Document对象为空");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }
        new WriteCommandAction.Simple(project) {
            @Override
            protected void run() {
                document.setText(XMLString.getInstance().generateXmlString());
                //formatCode();
            }
        }.execute();
    }

利用XMLString.getInstance().generateXmlString()获取各操作类型的所有属性将其拼接为一份完整的shape.xml文件的字段,最后调用插件系统的相关命令将字符串粘贴在系统对应的输入框中。

2. 将原有shape.xml字符串转化为对应操作界面

上面已经讲述类如何拼接生成xml字符串并将其粘贴到Android Studio界面上,此外NoCodeShape不仅支持新生成的shape.xml,同样也支持对旧shape.xml进行修改的能力。与第一步相比较主要多了一步读取Android Studio xml文档并将其转化为对应操作界面的过程。其主要是在操作界面初始化之前执行了如下方法:

 private void initSax() {
        String text = FileDocumentManager.getInstance().getDocument(file).getText();
        ShapeSaxHandler handler = new ShapeSaxHandler();
        try {
            handler.createViewList(text);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

利用ShapeSaxHandler去解析xml里面的元素,参考了FindViewByMe的解析原理。

具体操作逻辑如下:

  public void createViewList(String string) throws ParserConfigurationException, SAXException, IOException {
        InputStream xmlStream = new ByteArrayInputStream(string.getBytes("UTF-8"));
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        parser.parse(xmlStream, this);
    }
    @Override
    public void startDocument() throws SAXException {
        if (shapePartList == null) {
            shapePartList = new ArrayList<ShapePart>();
        }
    }
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        switch (qName) {
            case "shape":
                Shape.getInstance().getBuilder().analysisAttribute(attributes);
                break;
            case "stroke":
                Stroke.getInstance().getBuilder().analysisAttribute(attributes);
                break;
            case "solid":
               Solid.getInstance().getBuilder().analysisAttribute(attributes);
                break;
            case "gradient":
                Gradient.getInstance().getBuilder().analysisAttribute(attributes);
                break;
            case "corners":
                Corners.getInstance().getBuilder().analysisAttribute(attributes);
                break;
            default:
                break;
        }

逻辑其实很清楚,主要对startTag的判断,然后通过调用 public abstract void analysisAttribute(Attributes attributes);的方法对相应的 类型的Buidler进行一个赋值操作。

以Stroke为例:

@Override
        public void analysisAttribute(Attributes attributes) {
            Stroke.getInstance().setChecked(true);
            setColor(attributes.getValue("android:color"));
            setDashGap(attributes.getValue("android:dashGap"));
            setWidth(attributes.getValue("android:width"));
            setDashWidth(attributes.getValue("android:dashWidth"));
        }

主要是获取到其中的属性,并对初始化的界面进行一些操作(例如选中或者赋值相关操作)。

四、总结

这个插件算是自己第一次做一个相对较实用的插件,都是利用工作的空闲事件进行编写,前前后后进行了将近一个月,其中收获颇多,但也踩过了各种各样的坑。在开发过程中由于相关文档较少,通过阅读官方文档还是有点小吃力,不过一步步还是走了下来,自己也得到了成长。其中坑也都添平了,但由于一些基础技术的欠缺,比如对Java GUI界面编程不太熟悉,导致开发过程中有很大一段时间都在跟界面作对,因此后面有机会将去深入了解Java的界面编程,争取能够将页面交互能够做到更好。

另外由于自身开发精力的原因,不能将插件做到完美,目前插件中还有如下几个问题:

1、Gradient中对应相关逻辑还需要再优化

2、还未支持Size跟Pading(从自身所处环境考虑用得较少,所以暂未支持)

3、对于颜色选择器默认打开后不支持对本地颜色字符串处理

4、存在大量的界面操作逻辑代码,需要优化

最后,希望大家能在使用过程中提出相关的意见或建议,也欢迎能一起加入到开发中,从而能将该插件做得更加完美。

项目地址:

https://github.com/VomPom/NoCodeShape

© 著作权归作者所有

落叶挽歌

落叶挽歌

粉丝 0
博文 40
码字总数 27938
作品 0
温州
Android工程师
私信 提问
加载中

评论(1)

好孩子哟
好孩子哟
想问一下我这个下载之后快捷键和右键都没有NoCodeShape怎么回事啊。。
七个 Android 程序猿提高效率必备工具

Android 程序猿提高效率必备工具 0x00 Code tree for GitHub 这个 Chrome 浏览器插件。 Github 作为最大同性交友网站,每天的工作几乎是从打开这个网站开始的。当我们浏览一个项目时,可以以...

hylinux1024
2017/04/05
0
0
开发人员必读,安卓开发工具知多少

安卓开发过程中需要用到各种工具,于是作为一名安卓开发人员,那么多工具,各种亚历山大啊,于是今天就给大家汇总了一下安卓开发工具,哪些是安卓开发中必须要用到。 1、Draw 9-Patch 这个九...

flyurt
2015/10/30
344
0
为Android开发环境安装BlackBerry PlayBook插件

为了让Android开发人员可以快速地将Android应用发布到PlayBook上,BlackBerry官方发布了PlayBook的 Eclipse插件,可以将这个插件安装到Android的Eclipse开发环境中,让Android开发人员可以直...

鉴客
2011/10/23
276
0
android通过shape.xml制作渐变背景

编写xml文件放到res/drawable-*/下面 最基本的写法如下 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:......

一别经年
2014/02/05
1K
0
Android studio插件整理

Android studio 以下简称AS,*号表示插件的常用比例,越高常用性越高 AS打开设置的快捷键Ctrl + alt + S 在线安装:File-->settings-->Plugins-->Browse repositories-->然后再输入框输入But...

惟吾德馨_慧
2018/05/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

使用zabbix自带的模板监控MySQL自带

一、安装zabbix server 略 二、安装zabbix agent 略 三、给主机套自带的模板 略 四、创建授权用户 mysql> grant all on *.* to 'zabbix'@'localhost' identified by 'musingtec2019';Quer......

雁南飞丶
13分钟前
4
0
notepad++快捷键

notepad++也情有独钟,最近发现了一个快捷键,就是选中单词,ctrl+shift+enter。不过现在想知道一个快捷键,假设有三行代码,选中后一般按TAB就可以三行全部缩进. Notepad++绝对是windows下进...

zhengzhixiang
34分钟前
5
0
区块链背景是什么?区块链的意义是什么?

一、前言 区块链技术的首次也是最著名的应用是比特币,一个在2009年1月初正式上线运行的去中心化数字货币应用,他的创始人叫中本聪,但目前大家并不知道此人的真实身份。 比特币不同于现代国...

daxiongdi
40分钟前
4
0
在Bash中循环浏览文件内容

如何使用Bash遍历文本文件的每一行? 使用此脚本: echo "Start!"for p in (peptides.txt)do echo "${p}"done 我在屏幕上得到以下输出: Start!./runPep.sh: line 3: syntax error......

技术盛宴
42分钟前
8
0
史上最强IP正则表达式

port ([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{4}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]) ipv4 ^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$ ipv4+mask......

蜗牛伊
45分钟前
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部