分享一下我的三個程式碼自動生成工具類--助你解放雙手
零、前言:
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.點選執行,就可以生成了。如果有問題,歡迎指出

操作.png
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 * 郵箱:[email protected] * 說明:初始化RecyclerView的Adapter */ public class Xml2Adapter { @Test public void main() { //你的佈局xml所在路徑 File file = new File("I:\\Java\\Android\\Unit\\B\\asyn\\src\\main\\res\\layout\\item_list_pic.xml"); //你的Adapter的java類放在哪個包裡 File out = new File("I:\\Java\\Android\\Unit\\B\\asyn\\src\\main\\java\\com\\toly1994\\app"); //你的Adapter的名字--不要加.java String name = "MyRVAdapter"; 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 inxml檔案路徑 * @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("\npublic class " + name + " extends RecyclerView.Adapter<MyRVAdapter.MyViewHolder> {\n"); sb.append("private Context mContext;\n"); sb.append("private List<String> mData;\n"); sb.append("public MyRVAdapter(Context context, List<String> data) {\n" + "mContext = context;\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" + "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"); map.forEach((id, view) -> { if ("Button".equals(view)) { sb.append("holder." + formatId2Field(id) + ".setOnClickListener(v -> {\n" + "});\n"); } if ("TextView".equals(view)) { sb.append("holder." + formatId2Field(id) + ".setText(\"\");\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) -> { 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 * 郵箱:[email protected] * 說明:安卓自定義屬性,程式碼生成器 */ 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 * 郵箱:[email protected] * 說明:svg圖示轉換為Android可用xml生成器 */ public class Svg2Xml { @Test public void svgDir() { String dirPath = "E:\\Material\\MyUI\\#svg\\基礎"; svg2xmlFromDir(dirPath); } @Test public void singleSvg() { File file = new File("E:\\Material\\MyUI\\#svg\\字母\\icon_a.svg"); svg2xml(file); svg2xmlFromDir("E:\\Material\\MyUI\\#svg\\字母"); } /** * 將一個資料夾裡的所有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()); } 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[] split = result.split("<path"); for (String s : split) { int fPos = s.indexOf("f"); int dPos = s.indexOf("d="); if (fPos != -1 && fPos < dPos) { String[] pathTemp = s.split("d="); String path = pathTemp[1].substring(0, pathTemp[1].indexOf("/")); paths.add(path); continue; } if (s.contains("path")) { int endIndex; if (!s.contains("fill")) { endIndex = s.indexOf("p"); } else { endIndex = Math.min(s.indexOf("f"), s.indexOf("p")); } String path = s.substring(s.indexOf("\""), endIndex); System.out.println(path); paths.add(path); } } } }