分享一下我的三個程式碼自動生成工具類–助你解放雙手

張風捷特烈發表於2019-03-04

零、前言:

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("
");
        });
        sb.append("

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

            if ("Button".equals(view)) {
                sb.append(formatId2Field(id) + ".setOnClickListener(v -> {
" +
                        "        });
");
            }
        });
        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("\\", ".") + ";
");
        sb.append("import android.content.Context;
" +
                "import android.support.annotation.NonNull;
" +
                "import android.support.constraint.ConstraintLayout;
" +
                "import android.support.v7.widget.RecyclerView;
" +
                "import android.view.LayoutInflater;
" +
                "import android.view.View;
" +
                "import android.view.ViewGroup;
" +
                "import android.widget.Button;
" +
                "import java.util.List;
" +
                "import android.widget.TextView;
");
        sb.append("public class " + name + " extends RecyclerView.Adapter<" + name + ".MyViewHolder> {
");
        sb.append("private Context mContext;
");
        sb.append("private List<String> mData;
");
        sb.append("public " + name + "(List<String> data) {
" +
                "    mData = data;
" +
                "}");

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

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

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

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

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

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

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

        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 {
");
        map.forEach((id, view) -> {
            if (view.contains("
")) {
                view = view.split("
")[0];
            }

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

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

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

        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("
")[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 + ");
");
        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 + ");
");
            }
            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 + ");
");
            }
            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 + ");
");
            }
            if (s2.equals("string")) {
                // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
                sb.append("m" + s + " =  a.getString(R.styleable." + styleableName + ");
");
            }
        });
        sb.append("a.recycle();
");

        sb.append("init();
" +
                "    }");
        sb.append("private void init() {
" +
                "
" +
                "    }
}");
        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("\\", ".") + ";
");
        sb.append("public class " + name + " extends View {
");
        sb.append(field);
        sb.append("public " + name + "(Context context) {
" +
                "    this(context, null);
" +
                "}
" +
                "public " + name + "(Context context, AttributeSet attrs) {
" +
                "    this(context, attrs, 0);
" +
                "}
" +
                "public " + name + "(Context context, AttributeSet attrs, int defStyleAttr) {
" +
                "    super(context, attrs, defStyleAttr);
");

        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 + ";
");
                    }
                    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"?>
" +
                "<vector xmlns:android="http://schemas.android.com/apk/res/android"
" +
                "        android:width="48dp"
" +
                "        android:height="48dp"
" +
                "        android:viewportWidth="1024"
" +
                "        android:viewportHeight="1024">
");
        for (String path : paths) {
            outSb.append("    <path
" +
                    "        android:fillColor="#FF7F47"
android: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-7 svg2Xml使用正則進行匹配
2.更多關於我
筆名 QQ 微信 愛好
張風捷特烈 1981462002 zdl1994328 語言
我的github 我的簡書 我的掘金 個人網站
3.宣告

1—-本文由張風捷特烈原創,轉載請註明
2—-歡迎廣大程式設計愛好者共同交流
3—-個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4—-看到這裡,我在此感謝你的喜歡與支援


icon_wx_200.png

相關文章