玩转字符串篇--代码自动生成,解放双手

7,545 阅读6分钟

零、前言:

1.RecyclerView的Adapter自动生成器(含ViewHolder)
2.自定义属性的自定义View代码生成器(含自定义属性的初始化)
3.svg图标转换为Android可用xml生成器

最近喜欢切割字符串,这三个类是近期的作品,感觉挺好用的,在此分享一下
三个工具都会贴在本文末尾,本文末尾,本文末尾


一、RecyclerView的Adapter自动生成器(含ViewHolder)

最近写了几个RecyclerView的Adapter,控件一多ViewHolder写起来感觉挺不爽的
也感觉其中也只是布局文件里的几个id和View的类型有区别,写个工具读一下xml自动生成一下呗
既然ViewHolder自动生成了,顺便吧Adapter也一起生成算了,反正初始也就那一大段

演示一下:

1.把工具类拷贝到test包里
2.写上你xml的路径和生成的.java所在的包
3.点击运行,就可以生成了。如果有问题,欢迎指出

操作


Xml代码
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    android:id="@+id/id_cl_root"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    tools:context=".MainActivity">
    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="20dp"
        android:id="@+id/id_iv_icon"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    <TextView
        android:id="@+id/id_tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="5dp"
        android:maxLines="1"
        android:text="name"
        android:textColor="#000000"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@id/id_iv_info"
        app:layout_constraintStart_toEndOf="@id/id_iv_icon"
        app:layout_constraintTop_toTopOf="@id/id_iv_icon"/>
    <TextView
        android:id="@+id/id_iv_info"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="5dp"
        android:ellipsize="end"
        android:maxLines="2"
        android:text="infoinfoinfoinfo"
        android:textColor="@color/qq_line"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@id/guide_v_95"
        app:layout_constraintStart_toEndOf="@id/id_iv_icon"
        app:layout_constraintTop_toBottomOf="@id/id_tv_name"/>
    <android.support.constraint.Guideline
        android:id="@+id/guide_v_95"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.95"
        />
</android.support.constraint.ConstraintLayout>
自动生成的Adapter

自动生成Adapter.png

点一下,就生成这么多,一个一个敲怎么也要五分钟吧,这种枯燥的工作,还是留给计算机吧。
之后根据自己的业务需求,小修补一下就行了。


附加赠送:findViewById自动生成,控制台里,拷贝即用

虽然喜欢用butterknife,但感觉每次加依赖好麻烦,也就是想找个id,也没必要引个库,毕竟都占空间的。

附赠findViewById.png


二、自定义属性的自定义View代码生成器(含自定义属性的初始化)

这可谓我的得意之作,本人比较喜欢自定义控件,但自定义属相写起来费心费力,也没什么含量
基本上也就那么几个属性在变,一咬牙,写个工具类吧,然后就有了下文:

演示一下使用:

1.把工具类拷贝到test包里
2.写上你xml的路径和生成的.java所在的包,写上你的专属前缀
3.点击运行,就可以生成了。如果有问题,欢迎指出
注意点:自定义属性必须有自己的专属前缀(任意字符都可以,统一即可)

操作.png


attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TolyProgressBar">
        <attr name="z_pb_bg_color" format="color"/>
        <attr name="z_pb_bg_height" format="dimension"/>
        <attr name="z_pb_on_color" format="color"/>
        <attr name="z_pb_on_height" format="dimension"/>
        <attr name="z_pb_txt_color" format="color"/>
        <attr name="z_pb_txt_size" format="dimension"/>
        <attr name="z_pb_txt_offset" format="dimension"/>
        <attr name="z_pb_txt_gone" format="boolean"/>
    </declare-styleable>
</resources>

生成自定义控件.png


3.svg图标转换为Android可用xml生成器

和上面一样,将所有svg放在一个文件夹里,即可批处理

svg2xml.png


附录

1:Xml2Adapter.java
/**
 * 作者:张风捷特烈
 * 时间:2018/10/31 0031:8:47
 * 邮箱:1981462002@qq.com
 * 说明:初始化RecyclerView的Adapter
 */
public class Xml2Adapter {
    @Test
    public void main() {
        //你的布局xml所在路径
        File file = new File("I:\\Java\\Android\\APL\\VVI_MDs\\test\\src\\main\\res\\layout\\item_goods_list.xml");
        //你的Adapter的java类放在哪个包里
        File out = new File("I:\\Java\\Android\\APL\\VVI_MDs\\app\\src\\main\\java\\com\\toly1994\\vvi_mds\\pkg_08_other\\adapter");
        //你的Adapter的名字--不要加.java
        String name = "GoodsAdapter";
        initView(file, out, name);
    }

    @Test
    public void findView() {
        //你的布局xml所在路径
        File file = new File("I:\\Java\\Android\\Unit\\B\\asyn\\src\\main\\res\\layout\\item_list_pic.xml");
        findViewById(file);
    }

    private void findViewById(File in) {
        String res = readFile(in);
        HashMap<String, String> map = split(res);
        StringBuilder sb = new StringBuilder();
        map.forEach((id, view) -> {
            sb.append("public ").append(view).append(" ").append(formatId2Field(id)).append(";").append("\r\n");
        });
        sb.append("\n\n");
        map.forEach((id, view) -> {
            sb.append(formatId2Field(id))
                    .append("= itemView.findViewById(R.id.")
                    .append(id).append(");").append("\r\n");

            if ("Button".equals(view)) {
                sb.append(formatId2Field(id) + ".setOnClickListener(v -> {\n" +
                        "        });\n");
            }
        });
        System.out.println(sb.toString());
    }

    /**
     * 读取文件
     *
     * @param in   xml文件路径
     * @param out  输出的java路径
     * @param name
     */
    private static void initView(File in, File out, String name) {
        FileWriter fw = null;
        try {
            HashMap<String, String> map = split(readFile(in));
            String result = contactAdapter(in, out, name, map);

            //写出到磁盘
            File outFile = new File(out, name + ".java");
            fw = new FileWriter(outFile);
            fw.write(result);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 读取文件
     *
     * @param in
     * @return
     */
    private static String readFile(File in) {
        if (!in.exists() && in.isDirectory()) {
            return "";
        }

        FileReader fr = null;
        try {
            fr = new FileReader(in);
            //字符数组循环读取
            char[] buf = new char[1024];
            int len = 0;
            StringBuilder sb = new StringBuilder();
            while ((len = fr.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }

            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 直接拼接出Adapter
     */
    private static String contactAdapter(File file, File out, String name, HashMap<String, String> map) {
        StringBuilder sb = new StringBuilder();
        String path = out.getAbsolutePath();
        path.split("java");

        sb.append("package " + path.split("java\\\\")[1].replaceAll("\\\\", ".") + ";\n");
        sb.append("import android.content.Context;\n" +
                "import android.support.annotation.NonNull;\n" +
                "import android.support.constraint.ConstraintLayout;\n" +
                "import android.support.v7.widget.RecyclerView;\n" +
                "import android.view.LayoutInflater;\n" +
                "import android.view.View;\n" +
                "import android.view.ViewGroup;\n" +
                "import android.widget.Button;\n" +
                "import java.util.List;\n" +
                "import android.widget.TextView;\n");
        sb.append("public class " + name + " extends RecyclerView.Adapter<" + name + ".MyViewHolder> {\n");
        sb.append("private Context mContext;\n");
        sb.append("private List<String> mData;\n");
        sb.append("public " + name + "(List<String> data) {\n" +
                "    mData = data;\n" +
                "}");

        String layoutId = file.getName().substring(0, file.getName().indexOf(".x"));
        sb.append("@NonNull\n" +
                "@Override\n" +
                "public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n" +
                "mContext = parent.getContext();\n" +
                "    View view = LayoutInflater.from(mContext).inflate(R.layout." + layoutId + ", parent, false);\n" +
                "    return new MyViewHolder(view);\n" +
                "}\n");

        sb.append("@Override \n" +
                "public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {\n" +
                "String str = mData.get(position);"
        );

        map.forEach((id, view) -> {

            if (view.contains("\n")) {
                view = view.split("\n")[0];
            }

            if ("Button".equals(view)) {
                sb.append("holder." + formatId2Field(id) + ".setOnClickListener(v -> {\n" +
                        "        });\n");
            }

            if ("TextView".equals(view)) {
                sb.append("holder." + formatId2Field(id) + ".setText(str);\n");
            }
            if ("ImageView".equals(view)) {
                sb.append("holder." + formatId2Field(id) + ".setImageBitmap(null);\n");
            }
        });

        sb.append("}\n" +
                "@Override\n" +
                "public int getItemCount() {\n" +
                "return mData.size();\n" +
                "}");

        sb.append(contactViewHolder(map));
        return sb.toString();
    }

    /**
     * 连接字符串:ViewHolder
     */
    private static String contactViewHolder(HashMap<String, String> map) {
        StringBuilder sb = new StringBuilder();
        sb.append("class MyViewHolder extends RecyclerView.ViewHolder {\r\n");
        map.forEach((id, view) -> {
            if (view.contains("\n")) {
                view = view.split("\n")[0];
            }

            sb.append("public ").append(view).append(" ")
                    .append(formatId2Field(id)).append(";").append("\r\n");
        });

        sb.append("public MyViewHolder(View itemView) {\n" +
                "super(itemView);");

        map.forEach((id, view) -> {
            sb.append(formatId2Field(id))
                    .append("= itemView.findViewById(R.id.")
                    .append(id).append(");").append("\r\n");
        });
        sb.append("}\n" +
                "}\n}");

        return sb.toString();
    }

    private static String formatId2Field(String id) {
        if (id.contains("_")) {
            String[] partStrArray = id.split("_");
            id = "";
            for (String part : partStrArray) {
                String partStr = upAChar(part);
                id += partStr;
            }
        }
        return "m" + id;
    }

    /**
     * 将字符串仅首字母大写
     *
     * @param str 待处理字符串
     * @return 将字符串仅首字母大写
     */
    public static String upAChar(String str) {
        String a = str.substring(0, 1);
        String tail = str.substring(1);
        return a.toUpperCase() + tail;
    }

    private static HashMap<String, String> split(String res) {
        String[] split = res.split("<");
        HashMap<String, String> viewMap = new HashMap<>();
        for (String s : split) {
            if (s.contains("android:id=\"@+id") && !s.contains("Guideline")) {
                String id = s.split("@")[1];
                id = id.substring(id.indexOf("/") + 1, id.indexOf("\""));
                String view = s.split("\r\n")[0];
                String[] viewNameArr = view.split("\\.");
                if (viewNameArr.length > 0) {
                    view = viewNameArr[viewNameArr.length - 1];
                }
                viewMap.put(id, view);
            }
        }
        return viewMap;
    }
}

2.Attrs2View.java
/**
 * 作者:张风捷特烈
 * 时间:2018/10/31 0031:8:47
 * 邮箱:1981462002@qq.com
 * 说明:安卓自定义属性,代码生成器
 */
public class Attrs2View {
    @Test
    public void main() {
        //你的attr.xml所在路径
        File file = new File("I:\\Java\\Android\\Unit\\B\\test\\src\\main\\res\\values\\attrs.xml");
        //你的Adapter的java类放在哪个包里
        File out = new File("I:\\Java\\Android\\Unit\\B\\asyn\\src\\main\\java\\com\\toly1994\\app");
        String preFix = "z_";
        //你的前缀
        initAttr(preFix, file, out);
    }

    public static void initAttr(String preFix, File file, File out) {
        HashMap<String, String> format = format(preFix, file);
        String className = format.get("className");
        String result = format.get("result");

        StringBuilder sb = initTop(out, className, result);
        sb.append("TypedArray a = context.obtainStyledAttributes(attrs, R.styleable." + className + ");\r\n");
        format.forEach((s, s2) -> {
            String styleableName = className + "_" + preFix + s;
            if (s.contains("_")) {
                String[] partStrArray = s.split("_");
                s = "";
                for (String part : partStrArray) {
                    String partStr = upAChar(part);
                    s += partStr;
                }
            }
            if (s2.equals("dimension")) {
                // mPbBgHeight = (int) a.getDimension(R.styleable.TolyProgressBar_z_pb_bg_height, mPbBgHeight);
                sb.append("m" + s + " = (int) a.getDimension(R.styleable." + styleableName + ", m" + s + ");\r\n");
            }
            if (s2.equals("color")) {
                // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
                sb.append("m" + s + " =  a.getColor(R.styleable." + styleableName + ", m" + s + ");\r\n");
            }
            if (s2.equals("boolean")) {
                // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
                sb.append("m" + s + " =  a.getBoolean(R.styleable." + styleableName + ", m" + s + ");\r\n");
            }
            if (s2.equals("string")) {
                // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
                sb.append("m" + s + " =  a.getString(R.styleable." + styleableName + ");\r\n");
            }
        });
        sb.append("a.recycle();\r\n");

        sb.append("init();\n" +
                "    }");
        sb.append("private void init() {\n" +
                "\n" +
                "    }\n}");
        System.out.println(sb.toString());

        FileWriter fw = null;
        try {
            //写出到磁盘
            File outFile = new File(out, className + ".java");
            fw = new FileWriter(outFile);
            fw.write(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /*
     *
     * @param out
     * @param name
     */
    private static StringBuilder initTop(File out, String name, String field) {
        StringBuilder sb = new StringBuilder();
        String path = out.getAbsolutePath();
        path.split("java");
        sb.append("package " + path.split("java\\\\")[1].replaceAll("\\\\", ".") + ";\n");
        sb.append("public class " + name + " extends View {\n");
        sb.append(field);
        sb.append("public " + name + "(Context context) {\n" +
                "    this(context, null);\n" +
                "}\n" +
                "public " + name + "(Context context, AttributeSet attrs) {\n" +
                "    this(context, attrs, 0);\n" +
                "}\n" +
                "public " + name + "(Context context, AttributeSet attrs, int defStyleAttr) {\n" +
                "    super(context, attrs, defStyleAttr);\n");

        return sb;
    }

    /**
     * 读取文件+解析
     *
     * @param preFix 前缀
     * @param file   文件路径
     */
    public static HashMap<String, String> format(String preFix, File file) {
        HashMap<String, String> container = new HashMap<>();
        if (!file.exists() && file.isDirectory()) {
            return null;
        }
        FileReader fr = null;
        try {
            fr = new FileReader(file);
            //字符数组循环读取
            char[] buf = new char[1024];
            int len = 0;
            StringBuilder sb = new StringBuilder();
            while ((len = fr.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            String className = sb.toString().split("<declare-styleable name=\"")[1];
            className = className.substring(0, className.indexOf("\">"));
            container.put("className", className);
            String[] split = sb.toString().split("<");
            String part1 = "private";
            String type = "";//类型
            String name = "";
            String result = "";
            String def = "";//默认值

            StringBuilder sb2 = new StringBuilder();
            for (String s : split) {
                if (s.contains(preFix)) {
                    result = s.split(preFix)[1];
                    name = result.substring(0, result.indexOf("\""));
                    type = result.split("format=\"")[1];
                    type = type.substring(0, type.indexOf("\""));
                    container.put(name, type);
                    if (type.contains("color") || type.contains("dimension") || type.contains("integer")) {
                        type = "int";
                        def = "0";
                    }
                    if (result.contains("fraction")) {
                        type = "float";
                        def = "0.f";
                    }
                    if (result.contains("string")) {
                        type = "String";
                        def = "\"toly\"";
                    }
                    if (result.contains("boolean")) {
                        type = "boolean";
                        def = "false";

                    }
                    if (name.contains("_")) {
                        String[] partStrArray = name.split("_");
                        name = "";
                        for (String part : partStrArray) {
                            String partStr = upAChar(part);
                            name += partStr;
                        }
                        sb2.append(part1 + " " + type + " m" + name + "= " + def + ";\r\n");
                    }
                    container.put("result", sb2.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return container;
    }

    /**
     * 将字符串仅首字母大写
     *
     * @param str 待处理字符串
     * @return 将字符串仅首字母大写
     */
    public static String upAChar(String str) {
        String a = str.substring(0, 1);
        String tail = str.substring(1);
        return a.toUpperCase() + tail;
    }
}


3.Svg2Xml.java
/**
 * 作者:张风捷特烈
 * 时间:2018/10/31 0031:8:47
 * 邮箱:1981462002@qq.com
 * 说明:svg图标转换为Android可用xml生成器
 */
public class Svg2Xml {

    @Test
    public void svgDir() {
        String dirPath = "E:\\Material\\MyUI\\#svg\\factory";
        svg2xmlFromDir(dirPath);
    }

    @Test
    public void singleSvg() {
        File file = new File("C:\\Users\\Administrator\\Desktop\\dao.svg");
        svg2xml(file);
    }

    /**
     * 将一个文件夹里的所有svg转换为xml
     *
     * @param filePath
     */
    public static void svg2xmlFromDir(String filePath) {

        File file = new File(filePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.getName().endsWith(".svg")) {
                    System.out.println(f);
                    svg2xml(f);
                }
            }
        } else {
            svg2xml(file);
        }
    }

    /**
     * 将.svg文件转换为安卓可用的.xml
     *
     * @param file 文件路径
     */
    public static void svg2xml(File file) {
        if (!file.exists() && file.isDirectory()) {
            return;
        }

        FileWriter fw = null;
        FileReader fr = null;
        ArrayList<String> paths = new ArrayList<>();
        try {
            fr = new FileReader(file);
            //字符数组循环读取
            char[] buf = new char[1024];
            int len = 0;
            StringBuilder sb = new StringBuilder();
            while ((len = fr.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            //收集所有path
            collectPaths(sb.toString(), paths);

            //拼接字符串
            StringBuilder outSb = contactStr(paths);
            //写出到磁盘
            File outFile = new File(file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf(".")) + ".xml");
            fw = new FileWriter(outFile);
            fw.write(outSb.toString());

            System.out.println("转换成功:" + file.getName());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
                if (fr != null) {
                    fr.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 拼接字符串
     *
     * @param paths
     * @return
     */
    private static StringBuilder contactStr(ArrayList<String> paths) {
        StringBuilder outSb = new StringBuilder();
        outSb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
                "        android:width=\"48dp\"\n" +
                "        android:height=\"48dp\"\n" +
                "        android:viewportWidth=\"1024\"\n" +
                "        android:viewportHeight=\"1024\">\n");
        for (String path : paths) {
            outSb.append("    <path\n" +
                    "        android:fillColor=\"#FF7F47\"\nandroid:pathData=\"");
            outSb.append(path);
            outSb.append("\"/>");
        }
        outSb.append("</vector>");
        return outSb;
    }

    /**
     * 收集所有path
     *
     * @param result
     * @param paths
     */
    private static void collectPaths(String result, ArrayList<String> paths) {
        String regex = " d=\"(?<res>(.)*?)\"";
        Matcher matcher = Pattern.compile(regex).matcher(result);
        while (matcher.find()) {
            String path = matcher.group("res");
            paths.add(path);
        }
    }
}

后记:捷文规范

1.本文成长记录及勘误表
项目源码日期备注
V0.1--2018-11-14分享一下我的三个代码自动生成工具类--助你解放双手
V0.2--2018-12-7svg2Xml使用正则进行匹配
2.更多关于我
笔名QQ微信爱好
张风捷特烈1981462002zdl1994328语言
我的github我的简书我的掘金个人网站
3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持


icon_wx_200.png