为什么会有这篇文章呢,是因为我发现我以前在学习开发知识时,对代码的整理太不好了,经常是会发生以前学习过的知识,现在却找不到代码在哪里的情况,于是又要重新开始
但如果时想要系统的去整理笔记的话,就必须要分文件夹进行管理,每个文件夹对应着不同方向的知识,而我们很多时候,每个练习项目都有着很多需要重复配置的东西
比如常用的依赖啊,工具类,分包啊,还有基本的测试按钮,比如你想学习数据库操作,可能就会在xml布局中书写4个Button,再写上id,点击事件...基本上这些弄完,10分钟过去了
而我接下来分享的小技巧,可能让你把原本1小时才能做完的事情,在5分钟内完成,因为我们会编写一个联系项目的模板,把重复的东西抽取出来
项目采用Java语言,同时提供kotlin语言版本作为小彩蛋,需要说明的是,项目的开发环境是3.1.1,在3.0以下的软件运行起来需要调整,具体怎么调整我不告诉你,因为推荐升级软件
项目源代码已经上传的Github仓库的template文件夹内了
现在,我们创建我们的template项目
创建项目结构
项目结构.png我们创建两个module,一个是默认的app,一个是appk,一个库common
- app用来编写Java测试代码
- appk用来编写kotlin测试代码
- common库用于存放一些公共的依赖,工具类
当然,这里的app和appk并不要求百分百严格区分Java和kotlin代码,看实际情况,比如你比较多的时间是在使用Java,那么就在app里面写,偶尔也可以在里面写些kotlin,这都无所谓
GIF.gif现在打开右上角的File-Prokect Structure,我们把common库的依赖添加到app和appk中,这样两个module就能使用Common里面的东西啦
App运行库
完成分包,配置主题,创建Menu菜单,drawable-hdpi,color,assets文件夹操作
1)创建项目的包结构
module分包.png我们在module下创建以下包
然后,我来具体解释一下每个包的含义(其实看名字也挺清晰的了)
-
adapter 适配器包,用来存放RecyclerView等的适配器
-
app 创建自定义Application,因为后面可能配置很多第三方,而第三方的初始化建议是每个第三方单独的创建一个单例模式的类,提供一个init方法去初始化,以减少代码的量,后面会提供一个AppConfig类作为示范
-
module 业务包,用来存放我们的各种实际练习代码,这里视你的项目是用来做什么来定,假如你是实际开发,就是一个登录包,一个主页包,如图,假如该项目你是用来学习Android原生控件,那么下面就是imageview,textview这样子
还有一个refrence包,这是用来存放你的练习草稿的,怎么说呢,你在练习的时候如果有什么需要单独提取出来测试的,你都可以在里面创建一个Activity,那个draft.txt也很好理解,就是草稿,你可以把在网上复制的代码先放到里面,后面写时可以去参考
-
net包用来存放网络请求相关的类
-
util包用来存放工具类
-
view包用来存放用到的自定义控件,可能你会有疑问了,实际开发中,view包不是都是放置在Common通用库里面的吗?是的,因为我们实际开发可能会使用组件化,多Module的开发,把view提取到Common通用库确确实实是为了让多个module来使用,但是这里我们只是练习模板项目,如果还要使用组件化那这个模板项目也就太复杂了,不符合我们当初节约时间的初衷,所以这里直接是,哪个Module的view就放在哪个module
2)配置主题和颜色
以前自己学习东西创建项目时,很喜欢把主题改成没有标题的,然后再到创建的Activity里面去创建几个Button,写点击事件去测试,这是多浪费时间的事情啊!
其实我们可以使用默认的主题来解决这个问题(为什么能解决后面会说),其实默认的主题并不丑,丑的是它的颜色,所以我们到res-values包下的colors文件,修改一下它的配色
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
Screenshot_1544633796.png运行程序
你看,这样就好看多了吧
3)创建Menu文件,实现点击事件
可能你还会觉得,默认的带标题的主题,未免有点占用我们的屏幕,是的,但是它的标题,却带给我们一个平时用的少,但是测试起来用的爽的东西,Menu
我们在res包下创建Menu文件夹,再创建menu.xml文件
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/one"
android:title="one" />
<item
android:id="@+id/two"
android:title="two" />
<item
android:id="@+id/three"
android:title="three" />
<item
android:id="@+id/four"
android:title="four" />
<item
android:id="@+id/five"
android:title="five" />
<item
android:id="@+id/six"
android:title="six" />
<item
android:id="@+id/seven"
android:title="seven" />
<item
android:id="@+id/eight"
android:title="eight" />
</menu>
menu.png8个按钮,足够你一个Activity测试了吧,毕竟比如练习数据库操作也就是增删改查,再多的代码也不适合放在同一个Activity里了,后面我们希望看到的,就是点击右边的..,就可以弹出我们的Menu测试按钮
之后我们在module-refrence包下创建JavaActiivty,将Activity作为第一启动项,编写以下代码来实现菜单的点击事件
/**
* Created by 舍长
* describe:
*/
public class JavaActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java);
}
//创建菜单
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
//菜单选项
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int i = item.getItemId();
switch (i) {
//
case R.id.one:
Toast.makeText(this, "one", Toast.LENGTH_SHORT).show();
break;
//
case R.id.two:
break;
//
case R.id.three:
T.s(true);
break;
//
case R.id.four:
Toast.makeText(this, "four", Toast.LENGTH_SHORT).show();
break;
//
case R.id.five:
Toast.makeText(this, "five", Toast.LENGTH_SHORT).show();
break;
//
case R.id.six:
Toast.makeText(this, "six", Toast.LENGTH_SHORT).show();
break;
//
case R.id.seven:
Toast.makeText(this, "seven", Toast.LENGTH_SHORT).show();
break;
//
case R.id.eight:
Toast.makeText(this, "eight", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
}
menu运行程序
ok,我们已经完美的完成了,可能你会觉得,这样代码量也不少啊,是的,但是它在所有Activity里面的代码都是一样的啊!
使用代码模板制作菜单.gif我们需要在哪个Activity用,就直接复制粘贴就可以了,其实也不推荐复制粘贴的方法,我们有更高效的,使用软件自己的代码模板功能,接下来演示一下该方法
代码模板具体的制作方法可以查看慕课网的教程的第三章,第二节,传送门
另外我们还可以添加一个ButterKnife来节约我们findId控件的代码,但是实际项目开发中不建议使用,因为实际上我遇到过使用该框架后与其他东西的冲突,gradle版本也会对它造成影响,集成教程
4)创建drawable-hdpi,color,asstes文件夹
为什么我们要在res下创建drawable-hdpi包呢,因为放置在该文件夹里面的图片,系统会帮助你进行一些处理,当你不使用图片加载框架的时候,放置在ImageView有可能因为图片过大而导致内存溢出,程序崩溃
图片分包.png而放置在drawable-hdpi下的图片不会,还有一个原因就是保持代码的整洁,我通常是.xml结尾的,例如选择器,使用shape制作的图片,vector图片就放置在drawable,而其它能直接看到的格式的,如,png,jpg,就会放置在drawable-hdpi
而上面的颜色选择color包可能会在某些场景下用,比如使用BottomNavigationView的时候
最后是我们的assets文件夹,用来存放如字体,本地调试html页面等,在源码里的Refrence包下会有调用字体文件的查考代码
Common通用库
该库用来存放我们练习中几乎必备的工具类和相关的依赖,我们把这些东西提取在Common包里面,让app添加它,这样app就不用显示过多的依赖,显得比较简洁,而且我们的appk同样可以使用它
1)Log工具类
我们在common库中创建util文件夹,然后创建L日志打印工具类,简化我们后面的日志操作
/**
* Created by 舍长 on 2018/12/14
* describe: 日志工具类
*/
public class L {
public void d(String msg) {
Log.d("helloWorld", msg);
}
}
后面就可以直接L.d使用啦
//日志工具类
case R.id.one:
L.d("打印d级别的Log请求,Log是helloWorld");
Toast.makeText(this, "one", Toast.LENGTH_SHORT).show();
break;
2)Toast工具类
我们的Toast工具类也是想放到Common包下的,但是它的使用需要获取对应的context,而如果我们的参数要传入两个参数,1是传入Context,2是要打印的内容的话
这样就体现不出工具类的易用性了,所以我们应该创建一个全局的Context,这在组件化的开发中也很常见,实际操作并不复杂,我们来看一下
我们的Common库中创建app包,在该包下创建BaseApp类,让该类继承于Application,然后我们提供一个getContext()方法来返回Context独享
/**
* Created by 舍长 on 2018/12/14
* describe:项目基础Application
*/
public class BaseApp extends Application {
private static Context context;
//我们也可以在这里初始化一些项目通用的东西
//比如查看数据库数据文件的Stetho
//这里就不演示了,详细集成可以查看我的另一篇文章
//https://www.jianshu.com/p/6a3b0ae4aeb4
@Override
public void onCreate() {
super.onCreate();
}
//创建一个静态的方法,以便获取context对象
public static Context getContext() {
return context;
}
}
接着写我们的Toast工具类
/**
* Created by 舍长 on 2018/12/14
* describe:
*/
public class T {
/**
* 弹出短吐司
*/
public static void s(Object object) {
//这里的参数为object而不是String的目的是为了能传入更多类型的数据
//而不管传入什么类型,我们都把它转成String类型,这样就使用起来就比较方便了,不用再进行toString操作
//而我们如果传进来,不是数据类型,而是自己定义的类,就会打印出对象的toString方法
String s = object.toString();
Toast.makeText(BaseApp.getContext(), "" + s, Toast.LENGTH_SHORT).show();
}
}
我们使用BaseApp.getContext()方法来获取Context,但是到现在为止,其实BaseApp和我们的App程序是没有任何关系的,我们当然也可以直接到AppModule的权限菜单引用它
但是这样后面我们要在自定义的Application配置一些东西的话,就得返回到Common库,有点麻烦。所以我们选择创建AppModule自己的自定义Application类,App
然后让该类去去继承与BaseApp,最后在AppModule的权限菜单应用的,就是我们AppModule里面自己的Appilication类,这样就即可以让Common库有Context对象,又方便我们后面Application的使用
/**
* Created by 舍长 on 2018/12/14
* describe:自定义Application
*/
public class App extends BaseApp{
@Override
public void onCreate() {
super.onCreate();
}
}
在权限菜单中去引用
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tonjies.template">
<application
android:name=".app.App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
</activity>
<!--菜单Activity-->
<activity android:name=".module.refrence.JavaActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
之后就可以在我们之间创建的,refrence包下的JavaActivity中去使用啦
//菜单选项
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int i = item.getItemId();
switch (i) {
//日志工具类
case R.id.one:
L.d("打印d级别的Log请求,Log是helloWorld");
// L.d(true);//经过类型判断的版本
Toast.makeText(this, "one", Toast.LENGTH_SHORT).show();
break;
//吐司工具类
case R.id.two:
T.s("你好,世界");
break;
//吐司工具类
case R.id.three:
T.s(true);
break;
//
case R.id.four:
Toast.makeText(this, "four", Toast.LENGTH_SHORT).show();
break;
//
case R.id.five:
Toast.makeText(this, "five", Toast.LENGTH_SHORT).show();
break;
//
case R.id.six:
Toast.makeText(this, "six", Toast.LENGTH_SHORT).show();
break;
//
case R.id.seven:
Toast.makeText(this, "seven", Toast.LENGTH_SHORT).show();
break;
//
case R.id.eight:
Toast.makeText(this, "eight", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
其实我们的L日志工具类也可以像我们的吐司工具类一样加入类型转换操作
/**
* Created by 舍长 on 2018/12/14
* describe: 日志工具类
*/
public class L {
//普通版
public static void d(String msg) {
Log.d("helloWorld", msg);
}
//进行类型判断
public static void d(Object msg) {
String string = msg.toString();
Log.d("helloWorld", string);
}
}
到这里,工具类部分举例子结束,除了这两个常用的,我们还可以在工具类中加入SharedPreferences啊,获取EditView的值,随机数,设置字体,等工具类,在源码中我会把几个工具类一并补上,作为本篇文章的小彩蛋
3)添加常用依赖
这里需要注意的是,3.0以上后,AndroidStudio添加依赖推荐使用api和implementation和替代compile,不然build时就会报红(虽然还是可以运行)
implementation和api的区别在于,implementation用于运行Module,api用于库添加依赖,什么意思呢,就是如果你在Common使用implementation来添加依赖,那么你的AppModule是检测不到你添加的依赖的
换句话来说,implementation只能是当它自己的范围使用,不管你是库Module还是运行Module
接下来我们在Common的budile.gradle下添加这几个依赖
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//Anko框架,用于kotlin开发扩展
api "org.jetbrains.anko:anko-commons:0.10.7"
//圆形处理框架
api 'de.hdodenhof:circleimageview:2.2.0'
// 材料设计
api 'com.android.support:design:28.0.0'
// Retrofit库
api 'com.squareup.retrofit2:retrofit:2.4.0'
api 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' // 此处一定要注意使用RxJava2的版本
api 'com.squareup.retrofit2:converter-gson:2.4.0' // 支持Gson解析
// Okhttp库
api 'com.squareup.okhttp3:okhttp:3.11.0'
api 'com.squareup.okhttp3:logging-interceptor:3.11.0'
// 支持Gson解析
api 'com.squareup.retrofit2:converter-gson:2.4.0'
// RxJava
api 'io.reactivex.rxjava2:rxjava:2.1.7'
api 'io.reactivex.rxjava2:rxandroid:2.0.1'
// 图片加载框架
api 'com.github.bumptech.glide:glide:4.7.1'
// 卡片布局
api 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
}
除了上述封装的
实际练习代码的分类
1)笔记库的搭建
有了上面的项目模板,后面我们需要的话,就可以直接复制,粘贴就可以直接上手学习了,但是我们的Android开发的内容很多,显然不是一个项目能放得下的
所以根据学习内容的不同,我大致将它们分为以下内容,进而我们可以为不同的练习内容设置不同的项目文件名
开发内容 | 项目英文名 | 解释 |
---|---|---|
Java基础 | java | 这里用IDEA |
kotlin基础 | kotlin | 这里用IDEA |
Android架构,开发规范 | framework | 如Mvp,mvc,代码抽取 |
Android控件 | materialDesign | Android原生控件,和原生有关的界面操作 |
四大组件 | unit | 包括Fragment也放在这里 |
数据库存储 | data | 数据库框框架,数据库操作 |
Android混合开发 | mixed | 与H5的交互,ndk开发 |
开源库 | library | 比较小的UI库,比如PickerView |
主流框架 | frame | 如RxJava,Dagger等 |
第三方服务 | third | 友盟,腾讯bugly |
业务代码 | profession | 例如多语言切换 |
系统交互 | system | 如蓝牙,相机,通知栏 |
Android安全与适配 | safety | 系统适配方案 |
你自己的练习项目1 | ||
你自己的练习项目2 |
大致就是这样,我们复制以上数量的项目文件夹,更改完名字后,我们的笔记库到这里就结束了,也许整个过程是稍显复杂,但是我们只要制作一次
后面我们需要找代码就方便很多,还有的好处就是如果你上次在某个方面学习到一半,有其他的事情,你下次可以很快的找到,继续学习
上面可能你会有个疑问,开源库和主流框架的区别,其实本质上是没有区别,但是我去区分它们的标准是,它们是否极其重要,不可替代的,极其重要的,如RxJava,就把它归纳到框架里面
而其它的,像提供某个UI控件的库,使用的少,我们可以找到替代的,甚至我们可以自己写一个的,就会被归纳到库library里面
2)配合笔记软件
笔记目录.png上面制作的笔记目录,不一定适合你,实际上你可以使用笔记软件,石墨文档制作属于你自己的练习项目名称目录
而有一些笔记,也是使用笔记软件记录比较合适,简单来说就是,你的文字笔记和你的练习代码仓库的目录是一致的,不过需要注意的是,文字笔记和我们的练习代码仓库一样,不同类别的笔记是重新创建新的笔记来写的
这是因为石墨文档如果你这是同一份文档写的内容太多就会卡,这里我把我的总目录文档发现出来作为参考,传送门
笔记文件夹.png不过不同类别的文档都会被放置在Android笔记的文件夹中
好了,本篇文章就到这里了,老实说,这是我写的30篇文章依赖感觉最累的一次,因为涉及的东西不难,但是很多,是让很累,但是本篇文章基本都是干货,实际运用中,能节约你大量配置项目的时间
理直气壮如果本篇文章对你有用,希望能给个喜欢,这对我来说是种很好的鼓励
网友评论