日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
AndroidStudio模板之文件組

文件組模板是基于FreeMarker模板語言的一個(gè)功能很強(qiáng)大的Android開發(fā)模板,可以這樣說,代碼片段模板和文件模板是一種提高編碼效率的工具,而文件組模板可以算是一種模板引擎。

網(wǎng)站設(shè)計(jì)、成都網(wǎng)站設(shè)計(jì)中從網(wǎng)站色彩、結(jié)構(gòu)布局、欄目設(shè)置、關(guān)鍵詞群組等細(xì)微處著手,突出企業(yè)的產(chǎn)品/服務(wù)/品牌,幫助企業(yè)鎖定精準(zhǔn)用戶,提高在線咨詢和轉(zhuǎn)化,使成都網(wǎng)站營銷成為有效果、有回報(bào)的無錫營銷推廣。成都創(chuàng)新互聯(lián)專業(yè)成都網(wǎng)站建設(shè)10多年了,客戶滿意度97.8%,歡迎成都創(chuàng)新互聯(lián)客戶聯(lián)系。

效果圖展示  

已有工程中使用模板效果圖  

創(chuàng)建工程時(shí)使用模板 

示例場景

在進(jìn)行Android開發(fā)時(shí),我們經(jīng)常會(huì)創(chuàng)建一個(gè)Demo工程,目的可能有很多種,可能是為了驗(yàn)證一個(gè)問題,可能是為了學(xué)習(xí)一個(gè)框架的使用,可能為了測試自己寫的一個(gè)lib庫等等。這個(gè)時(shí)候我們可能會(huì)創(chuàng)建一個(gè)Activity,然后再在xml寫一些按鈕,再在Activity里寫該按鈕的事件監(jiān)聽邏輯,也就是說為了執(zhí)行一段代碼我們要做這么多操作。為了簡化這段重復(fù)操作,我這邊寫了一個(gè)DebugActivity類,然后支持我們只需要寫個(gè)子類來繼承它,然后像下面這樣寫幾個(gè)方法即可,運(yùn)行的時(shí)候會(huì)根據(jù)方法動(dòng)態(tài)創(chuàng)建按鈕,并在點(diǎn)擊按鈕時(shí)執(zhí)行該方法的代碼邏輯。

 
 
 
 
  1. public void _test() { 
  2.  
  3. T("彈出Toast"); 
  4.  
  5. }  

由于本文主要介紹模板相關(guān)的,所以該場景相關(guān)的具體代碼技術(shù)細(xì)節(jié)就不多說了,有興趣的可以看下,DebugActivity的代碼,這里提出來只是為模板開發(fā)簡單的做個(gè)鋪墊。

模板位置

Android Studio Template中有系統(tǒng)預(yù)設(shè)的一些模板,我們可以直接修改,也可以另行添加新的模板。打開Android Studio安裝目錄/Contents/plugins/android/lib/templates這個(gè)文件夾我們能看到下面的目錄結(jié)構(gòu),這里便是AS中模板存放的位置。 

我們接下來的工作也就在這里,保險(xiǎn)起見我們?cè)谶@里新建一個(gè)目錄,我們自己寫的模板都放在自己新建的目錄里,例如我這里就創(chuàng)建了一個(gè)叫pk的目錄。

模板規(guī)范

在上面的基礎(chǔ)上,我們可以直接打開/activies/EmptyActivity目錄,如下圖 

我們可以看到上面紅色區(qū)域便是Template的文件結(jié)構(gòu),大致說下各個(gè)文件(夾)的含義

  • globals.xml.ftl 模板中參數(shù)配置的地方(可選)
  • recipe.xml.ftl 模板行為執(zhí)行處,引入這個(gè)模板之后,接下來要做什么事情,就是它說的算(可選,但是不選就沒有意義了,因?yàn)槟0逡胧且袨轵?qū)動(dòng)的)
  • root 存放模板文件及引入資源的目錄,模板文件可以是.xml、.java、.gradle等任何一個(gè)文本格式的文件,資源一般是我們引入的.png資源文件(可選,不選同上)
  • template_blank_activity.png 引入模板時(shí)的引導(dǎo)圖(可選)
  • template.xml 面向模板引擎的配置文件(必選)

我們可以看到,真正核心的部分就是root、recipe.xml.ftl和template.xml,接下來這重點(diǎn)說明這三部分。

我們可以打開root目錄,能夠看到里面的文件除了圖片資源文件都是以.ftl結(jié)尾的,而.ftl是標(biāo)準(zhǔn)的FreeMarker的文件。FreeMarker是類似于Velocity的一種模板框架,據(jù)說對(duì)于多文件處理時(shí)它具有更好的性能,大概也是Android Studio選擇Velocity作為單文件模板,選擇FreeMarker作為文件組模板的原因吧。有興趣的可以去FreeMarker官網(wǎng)學(xué)習(xí)一下,它的自定義標(biāo)簽功能還是很強(qiáng)大的,個(gè)人感覺比Velocity的更加接地氣。

接下來我們看一下recipe.xml.ftl 的內(nèi)容,打開如下 

這里以<#開頭的都是FreeMarker的語法,基本上比葫蘆畫瓢就能看明白,就不多說了。其實(shí)對(duì)于這個(gè)文件最重要的部分是下面四個(gè)標(biāo)簽:

  • copy 就是簡單的copy,把模板root目錄下的某個(gè)文件copy到目標(biāo)工程的某個(gè)目錄下
  • instantiate 跟copy很類似,***多的一點(diǎn)功能就是并不只簡單的走IO流進(jìn)行copy,而是通過FreeMarker框架按照模板中的FreeMarker能識(shí)別的邏輯判斷和數(shù)據(jù)引入來生成最終的目標(biāo)文件
  • merge 目標(biāo)項(xiàng)目中有了某文件,而我們還要想該文件合并一些我們的模板的部分時(shí),就選用merge,例如我們添加一個(gè)Activity時(shí)需要mergeAndroidManifest.xml的配置。目前支持的merge格式有.xml和.gradle,但是對(duì).gradle支持的不怎么好,不過不影響該模板的開發(fā),對(duì)于這套模板引擎的開發(fā)者來說,這可能是最麻煩的部分了,但是對(duì)于我們使用者就不用考那么多了,直接使用吧
  • open 這個(gè)很簡單,就是指定模板引入之后要IDE打開的文件

然后看下template.xml內(nèi)容

 
 
 
 
  1.  
  2.  
  3.  
  4.     format="5" 
  5.  
  6.     revision="5" 
  7.  
  8.     name="Empty Activity" 
  9.  
  10.     minApi="7" 
  11.  
  12.     minBuildApi="14" 
  13.  
  14.     description="Creates a new empty activity"> 
  15.  
  16.      
  17.  
  18.      
  19.  
  20.     
  21.  
  22.         id="activityClass" 
  23.  
  24.         name="Activity Name" 
  25.  
  26.         type="string" 
  27.  
  28.         constraints="class|unique|nonempty" 
  29.  
  30.         suggest="${layoutToActivity(layoutName)}" 
  31.  
  32.         default="MainActivity" 
  33.  
  34.         help="The name of the activity class to create" /> 
  35.  
  36.     
  37.  
  38.         id="generateLayout" 
  39.  
  40.         name="Generate Layout File" 
  41.  
  42.         type="boolean" 
  43.  
  44.         default="true" 
  45.  
  46.         help="If true, a layout file will be generated" /> 
  47.  
  48.     
  49.  
  50.         id="layoutName" 
  51.  
  52.         name="Layout Name" 
  53.  
  54.         type="string" 
  55.  
  56.         constraints="layout|unique|nonempty" 
  57.  
  58.         suggest="${activityToLayout(activityClass)}" 
  59.  
  60.         default="activity_main" 
  61.  
  62.         visibility="generateLayout" 
  63.  
  64.         help="The name of the layout to create for the activity" /> 
  65.  
  66.     
  67.  
  68.         id="isLauncher" 
  69.  
  70.         name="Launcher Activity" 
  71.  
  72.         type="boolean" 
  73.  
  74.         default="false" 
  75.  
  76.         help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher" /> 
  77.  
  78.      
  79.  
  80.     
  81.  
  82.         id="packageName" 
  83.  
  84.         name="Package name" 
  85.  
  86.         type="string" 
  87.  
  88.         constraints="package" 
  89.  
  90.         default="com.mycompany.myapp" /> 
  91.  
  92.      
  93.  
  94.      
  95.  
  96.          
  97.  
  98.         template_blank_activity.png 
  99.  
  100.      
  101.  
  102.      
  103.  
  104.      
  105.  
  106.   

當(dāng)我們進(jìn)行模板引入時(shí),AS會(huì)彈出一個(gè)如下圖的UI界面,要我們來填入或選擇一些數(shù)據(jù),例如輸入Activity的的名稱,選擇SDK的版本之類的。而這個(gè)界面就是根據(jù)由該文件而來的。 

內(nèi)容比較多,為減少篇幅我挑些重要的說

  • template標(biāo)簽
    • name 引入模板時(shí)的模板名稱,就死根據(jù)他選擇哪個(gè)模板的
    • description 彈出Dialog的標(biāo)題,對(duì)應(yīng)上去的區(qū)域1
  • category 表示該模板屬于哪種分類,在引入的時(shí)候會(huì)有個(gè)分類的選擇
  • parameter 每個(gè)該標(biāo)簽就對(duì)應(yīng)Dialog界面的一個(gè)輸入項(xiàng)
    • id 該參數(shù)的***標(biāo)識(shí)符,也是我們?cè)?ftl中引入的值,例如定義的id為username,引用時(shí)就是$username
    • name 對(duì)應(yīng)Dialog上面該輸入項(xiàng)的名稱
    • type 對(duì)應(yīng)該參數(shù)的類型,Dialog就是根據(jù)這個(gè)來決定對(duì)應(yīng)輸入是選擇框、輸入框還是下拉框等等
    • constraints 對(duì)應(yīng)該參數(shù)的約束,如果有多個(gè)要用|分割開
    • suggest 建議值,這個(gè)輸入部分是由級(jí)聯(lián)效應(yīng)的,可能你改了A參數(shù),B參數(shù)也會(huì)跟著改變,就是根據(jù)這個(gè)參數(shù)決定的
    • default 參數(shù)的默認(rèn)值
    • visibility 可見性,要配置一個(gè)boolean類型的參數(shù),一般指向另一個(gè)輸入源
    • help 當(dāng)焦點(diǎn)在某個(gè)輸入源上面時(shí),上圖的區(qū)域3的就限制這兒的內(nèi)容

操刀實(shí)戰(zhàn)

了解了模板規(guī)范之后,我們編寫模板時(shí)就不會(huì)那么被動(dòng)了,下面我們來自己動(dòng)手編寫文章開始部分展示的模板。

首先在剛才提到的自定義的模板下創(chuàng)建如下圖所示的目錄結(jié)構(gòu) 

然后將下面的代碼對(duì)應(yīng)貼進(jìn)去(圖片部分隨便找一張代替好了…)

globals.xml.ftl 

recipe.xml.ftl 

template.xml

 
 
 
 
  1.  
  2.  
  3.  
  4.     format="5" 
  5.  
  6.     revision="5" 
  7.  
  8.     name="Debug Activity" 
  9.  
  10.     minApi="7" 
  11.  
  12.     minBuildApi="14" 
  13.  
  14.     description="創(chuàng)建一個(gè)Debug的Activity"> 
  15.  
  16.      
  17.  
  18.      
  19.  
  20.     
  21.  
  22.         id="activityClass" 
  23.  
  24.         name="Activity名稱" 
  25.  
  26.         type="string" 
  27.  
  28.         constraints="class|unique|nonempty" 
  29.  
  30.         default="SetupActivity" 
  31.  
  32.         help="創(chuàng)建Activity的名稱" /> 
  33.  
  34.     
  35.  
  36.         id="addExample" 
  37.  
  38.         name="是否添加按鈕使用示例" 
  39.  
  40.         type="boolean" 
  41.  
  42.         default="false" 
  43.  
  44.         help="選擇時(shí)會(huì)自動(dòng)生成測試按鈕;否則不生成" /> 
  45.  
  46.      
  47.  
  48.     
  49.  
  50.         id="addJumpActivity" 
  51.  
  52.         name="是否添加跳轉(zhuǎn)Activity示例" 
  53.  
  54.         type="boolean" 
  55.  
  56.         default="false" 
  57.  
  58.         help="選擇時(shí)會(huì)自動(dòng)生成跳轉(zhuǎn)Activity相關(guān)邏輯;否則不生成" /> 
  59.  
  60.     
  61.  
  62.         id="isLauncher" 
  63.  
  64.         name="設(shè)為啟動(dòng)頁面" 
  65.  
  66.         type="boolean" 
  67.  
  68.         default="true" 
  69.  
  70.         help="選擇時(shí)設(shè)置該頁面為啟動(dòng)頁面;否則不設(shè)" /> 
  71.  
  72.      
  73.  
  74.     
  75.  
  76.         id="packageName" 
  77.  
  78.         name="包名" 
  79.  
  80.         type="string" 
  81.  
  82.         constraints="package" 
  83.  
  84.         default="com.mycompany.myapp" 
  85.  
  86.         help="輸入Application包名" /> 
  87.  
  88.      
  89.  
  90.      
  91.  
  92.          
  93.  
  94.         template_debug_activity.png 
  95.  
  96.      
  97.  
  98.      
  99.  
  100.      
  101.  
  102.   

AndroidManifest.xml.ftl 

DebugActivity.java.ftl

 
 
 
 
  1. package ${packageName}; 
  2.  
  3. import android.app.Activity; 
  4.  
  5. import android.content.Context; 
  6.  
  7. import android.content.Intent; 
  8.  
  9. import android.os.Bundle; 
  10.  
  11. import android.util.Log; 
  12.  
  13. import android.view.View; 
  14.  
  15. import android.widget.Button; 
  16.  
  17. import android.widget.LinearLayout; 
  18.  
  19. import android.widget.ScrollView; 
  20.  
  21. import android.widget.Toast; 
  22.  
  23. import java.lang.annotation.ElementType; 
  24.  
  25. import java.lang.annotation.Retention; 
  26.  
  27. import java.lang.annotation.RetentionPolicy; 
  28.  
  29. import java.lang.annotation.Target; 
  30.  
  31. import java.lang.reflect.Method; 
  32.  
  33. import java.util.ArrayList; 
  34.  
  35. import java.util.List; 
  36.  
  37. /** 
  38.  
  39.  * Debug測試類,快速調(diào)試Demo工程 
  40.  
  41.  * 使用姿勢(shì): 
  42.  
  43.  * 1. 新建一個(gè)子類繼承該類 
  44.  
  45.  * 2. 跳轉(zhuǎn)Activity: 在子類配置{@link Jump}注解, 然后在注解中配置跳轉(zhuǎn)Activity的類型 
  46.  
  47.  * 3. 點(diǎn)擊按鈕觸發(fā)方法: 在子類聲明一個(gè)名稱以"_"開頭的方法(支持任意修飾符),最終生成按鈕的文字便是改方法截去"_" 
  48.  
  49.  * 4. 方法參數(shù)支持缺省參數(shù)和單個(gè)參數(shù) 
  50.  
  51.  * 5. 如果是單個(gè)參數(shù),參數(shù)類型必須是Button或Button的父類類型,當(dāng)方法執(zhí)行時(shí),該參數(shù)會(huì)被賦值為該Buttom對(duì)象 
  52.  
  53.  * https://github.com/puke3615/DebugActivity 
  54.  
  55.  * 

     

  56.  
  57.  * 
  58.  
  59.  * @author zijiao 
  60.  
  61.  * @version 16/10/16 
  62.  
  63.  */ 
  64.  
  65. public abstract class DebugActivity extends Activity { 
  66.  
  67.     protected static final String FIXED_PREFIX = "_"; 
  68.  
  69.     private final String TAG = getClass().getName(); 
  70.  
  71.     private final List buttonItems = new ArrayList<>(); 
  72.  
  73.     protected LinearLayout linearLayout; 
  74.  
  75.     protected Context context; 
  76.  
  77.     @Target(ElementType.TYPE) 
  78.  
  79.     @Retention(RetentionPolicy.RUNTIME) 
  80.  
  81.     public @interface Jump { 
  82.  
  83.         Class[] value() default {}; 
  84.  
  85.     } 
  86.  
  87.     @Override 
  88.  
  89.     protected void onCreate(Bundle savedInstanceState) { 
  90.  
  91.         super.onCreate(savedInstanceState); 
  92.  
  93.         this.context = this; 
  94.  
  95.         ScrollView scrollView = new ScrollView(this); 
  96.  
  97.         setContentView(scrollView); 
  98.  
  99.         this.linearLayout = new LinearLayout(this); 
  100.  
  101.         this.linearLayout.setOrientation(LinearLayout.VERTICAL); 
  102.  
  103.         scrollView.addView(linearLayout); 
  104.  
  105.         try { 
  106.  
  107.             resolveConfig(); 
  108.  
  109.             createButton(); 
  110.  
  111.         } catch (Throwable e) { 
  112.  
  113.             error(e.getMessage()); 
  114.  
  115.         } 
  116.  
  117.     } 
  118.  
  119.     private void createButton() { 
  120.  
  121.         for (ButtonItem buttonItem : buttonItems) { 
  122.  
  123.             linearLayout.addView(buildButton(buttonItem)); 
  124.  
  125.         } 
  126.  
  127.     } 
  128.  
  129.     protected View buildButton(final ButtonItem buttonItem) { 
  130.  
  131.         final Button button = new Button(this); 
  132.  
  133.         button.setText(buttonItem.name); 
  134.  
  135.         button.setOnClickListener(new View.OnClickListener() { 
  136.  
  137.             @Override 
  138.  
  139.             public void onClick(View v) { 
  140.  
  141.                 if (buttonItem.target != null) { 
  142.  
  143.                     to(buttonItem.target); 
  144.  
  145.                 } else { 
  146.  
  147.                     Method method = buttonItem.method; 
  148.  
  149.                     method.setAccessible(true); 
  150.  
  151.                     Class[] parameterTypes = method.getParameterTypes(); 
  152.  
  153.                     int paramSize = parameterTypes.length; 
  154.  
  155.                     switch (paramSize) { 
  156.  
  157.                         case 0: 
  158.  
  159.                             try { 
  160.  
  161.                                 method.invoke(DebugActivity.this); 
  162.  
  163.                             } catch (Throwable e) { 
  164.  
  165.                                 e.printStackTrace(); 
  166.  
  167.                                 error(e.getMessage()); 
  168.  
  169.                             } 
  170.  
  171.                             break; 
  172.  
  173.                         case 1: 
  174.  
  175.                             if (parameterTypes[0].isAssignableFrom(Button.class)) { 
  176.  
  177.                                 try { 
  178.  
  179.                                     method.invoke(DebugActivity.this, button); 
  180.  
  181.                                 } catch (Throwable e) { 
  182.  
  183.                                     e.printStackTrace(); 
  184.  
  185.                                     error(e.getMessage()); 
  186.  
  187.                                 } 
  188.  
  189.                                 break; 
  190.  
  191.                             } 
  192.  
  193.                         default: 
  194.  
  195.                             error(method.getName() + "方法參數(shù)配置錯(cuò)誤."); 
  196.  
  197.                             break; 
  198.  
  199.                     } 
  200.  
  201.                 } 
  202.  
  203.             } 
  204.  
  205.         }); 
  206.  
  207.         return button; 
  208.  
  209.     } 
  210.  
  211.     private void resolveConfig() { 
  212.  
  213.         Class cls = getClass(); 
  214.  
  215.         //讀取跳轉(zhuǎn)配置 
  216.  
  217.         if (cls.isAnnotationPresent(Jump.class)) { 
  218.  
  219.             Jump annotation = cls.getAnnotation(Jump.class); 
  220.  
  221.             for (Class activityClass : annotation.value()) { 
  222.  
  223.                 buttonItems.add(buildJumpActivityItem(activityClass)); 
  224.  
  225.             } 
  226.  
  227.         } 
  228.  
  229.         //讀取方法 
  230.  
  231.         for (Method method : cls.getDeclaredMethods()) { 
  232.  
  233.             handleMethod(method); 
  234.  
  235.         } 
  236.  
  237.     } 
  238.  
  239.     protected void handleMethod(Method method) { 
  240.  
  241.         String methodName = method.getName(); 
  242.  
  243.         if (methodName.startsWith(FIXED_PREFIX)) { 
  244.  
  245.             methodName = methodName.replaceFirst(FIXED_PREFIX, ""); 
  246.  
  247.             ButtonItem buttonItem = new ButtonItem(); 
  248.  
  249.             buttonItem.method = method; 
  250.  
  251.             buttonItem.name = methodName; 
  252.  
  253.             buttonItems.add(buttonItem); 
  254.  
  255.         } 
  256.  
  257.     } 
  258.  
  259.     protected ButtonItem buildJumpActivityItem(Class activityClass) { 
  260.  
  261.         ButtonItem buttonItem = new ButtonItem(); 
  262.  
  263.         buttonItem.name = "跳轉(zhuǎn)到" + activityClass.getSimpleName(); 
  264.  
  265.         buttonItem.target = activityClass; 
  266.  
  267.         return buttonItem; 
  268.  
  269.     } 
  270.  
  271.     public void L(Object s) { 
  272.  
  273.         Log.i(TAG, s + ""); 
  274.  
  275.     } 
  276.  
  277.     public void error(String errorMessage) { 
  278.  
  279.         T("[錯(cuò)誤信息]\n" + errorMessage); 
  280.  
  281.     } 
  282.  
  283.     public void T(Object message) { 
  284.  
  285.         Toast.makeText(context, String.valueOf(message), Toast.LENGTH_SHORT).show(); 
  286.  
  287.     } 
  288.  
  289.     public void to(Class target) { 
  290.  
  291.         try { 
  292.  
  293.             startActivity(new Intent(this, target)); 
  294.  
  295.         } catch (Exception e) { 
  296.  
  297.             e.printStackTrace(); 
  298.  
  299.             error(e.getMessage()); 
  300.  
  301.         } 
  302.  
  303.     } 
  304.  
  305.     public void T(String format, Object... values) { 
  306.  
  307.         T(String.format(format, values)); 
  308.  
  309.     } 
  310.  
  311.     protected static class ButtonItem { 
  312.  
  313.         public String name; 
  314.  
  315.         public Method method; 
  316.  
  317.         public Class target; 
  318.  
  319.     } 
  320.  
  321. }  

JumpActivity.java.ftl 

SimpleActivity.java.ftl

 
 
 
 
  1. package ${packageName}; 
  2.  
  3. @DebugActivity.Jump({ 
  4.  
  5. <#if addJumpActivity> 
  6.  
  7.     JumpActivity.class, 
  8.  
  9. <#else> 
  10.  
  11.  
  12.  
  13. }) 
  14.  
  15. public class ${activityClass} extends DebugActivity { 
  16.  
  17. <#if addExample> 
  18.  
  19.     private int number = 0; 
  20.  
  21.     public void _無參方法調(diào)用() { 
  22.  
  23.     T("無參方法調(diào)用"); 
  24.  
  25.     } 
  26.  
  27.     public void _有參方法調(diào)用(Button button) { 
  28.  
  29.         button.setText("number is " + number++); 
  30.  
  31.     } 
  32.  
  33.     //代碼執(zhí)行不到,直接彈出toast提示報(bào)錯(cuò) 
  34.  
  35.     public void _錯(cuò)誤參數(shù)調(diào)用(String msg) { 
  36.  
  37.         T("test"); 
  38.  
  39.     } 
  40.  
  41.     //方法名沒有以"_"開頭,按鈕無法創(chuàng)建成功 
  42.  
  43.     public void 無效調(diào)用() { 
  44.  
  45.         T("test"); 
  46.  
  47.     } 
  48.  
  49.     //crash會(huì)被會(huì)被catch住,以toast方式彈出 
  50.  
  51.     public void _Crash測試() { 
  52.  
  53.         int a = 1 / 0; 
  54.  
  55.     } 
  56.  
  57.  
  58.  
  59. }  

ok,到此對(duì)于該模板的編寫過程就結(jié)束了,接下來重啟下Android Studio,然后New Project一路next下去,直到這個(gè)界面,這里就是我們自定義的DebugActivity模板了


當(dāng)前名稱:AndroidStudio模板之文件組
瀏覽地址:http://www.dlmjj.cn/article/cdeopph.html