2018/03/23 补充

  • run函数
    在Fragment中,调用每个控件的id之前,都要在前面加多一个’view.’,这样,就让代码变得有点繁琐,这时候就可以用run函数,来让代码变得更加简洁,具体如下:
 view.run { // 用户详情ivEdit.setOnClickListener { startActivity<UserInfoActivity>() } // 消息中心cardMessage.setOnClickListener { startActivity<MessageActivity>() } // 收藏夹cardCollect.setOnClickListener { startActivity<CollectActivity>() } // 设置ivSetting.setOnClickListener { startActivity<SettingActivity>() }}
// 加载数据
entity.run { // 头像imagePath = entity.imgImageUtil.glideCircleImage(context, img, ivHead) // 昵称tvName.text = name // 手机号码tvTel.text = mobile // 邮箱etEmail.setText(email)}
  • repeat函数
    顾名思义,就是重复操作,这个函数主要用在写静态数据的时候,具体使用如下:

  • let函数
     val drawable = R.mipmap.ic_launcher.let { getDrawable(it) }val color = R.color.white.let { getColor(it) }

kotlin项目的创建

Android Studio 3.0以后的版本,是默认支持kotlin的,创建新项目的时候,会自动添加kotlin的支持;
创建项目成功,在module的build.gradle的顶部会添加

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

并且,在dependencies中会添加以下代码,(其中$kotlin_version是版本号,写在project的gradle中)

compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

然后,在project的build.gradle中,会添加以下代码

 // 版本号ext.kotlin_version = '1.1.2-4'dependencies {classpath 'com.android.tools.build:gradle:2.3.0'classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}

至此,就可以在项目中创建kotlin的文件了

为什么kotlin写代码很简洁?

kotlin的基础语法可以才kotlin的官方文档学习,也可以通过 kotlin官方文档中文版;

每个人都知道kotlin相比Java,代码量会减少3倍,但是,在哪个方面会减少代码量呢?以下几点可以做到:

1. 无须再findviewbyid

布局还是一样,使用xml作为Activity的布局,布局中有个TextView,ID为tvTitle;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.hebin.kotlin.study.ui.MainActivity"><TextView
        android:id="@+id/tvTitle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"/></LinearLayout>

然后在Activity中,通过import,声明布局即可拿到ID进行操作;

import kotlinx.android.synthetic.main.activity_main.*class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tvTitle.setOnClickListener { toast("点击了标题") }}
}

从上面的代码可以看出,相比Java,省去了findviewbyid的操作,可根据xml的ID直接拿到控件进行操作,而且,kotlin支持lambda表达,所以点击事件也变得特别简洁。

2. 无须再写getter和setter

在实现列表加载数据的时候,用到最多的,就是通过实体类,set数据给实体类,然后在Adapter中,通过实体类get得到数据,加载到控件中,如果需要实现id跟title的实体类,Java中的写法如下:

public class DataEntity {private String id;private String title;public void setId(String id) {this.id = id;}public void setTitle(String title) {this.title = title;}public String getId() {return id;}public String getTitle() {return title;}
}

那么换成kotlin的话,只需一句话即可实现同样的功能:

data class DataEntity(val id: String, val title: String)

3. 其他

kotlin很多语法,相比Java都较为简洁,比如说在kotlin用when代替switch,而且能实现更多功能:

       val result: Any = 78when (result) {100 -> println("等于100")78, 80, 90 -> println("等于78或80或90")"我是谁" -> println("我是谁")in 60..99 -> println("在60-99区间内")is Int -> println("该字段是Int类型")else -> println("该字段不是Int类型")}

不仅仅可以判断类型,还可以判断数字的准确值、判断字符串、判断是否在区间内、判断几个或关系的条件。

再比如列表,kotlin为列表提供了很多方法,(关于List需要注意的是,在Java中,List是有add、addAll这些方法的,但是在kotlin中的List是没有这些方法的,但是可以用MutableList代替)

 // 集合fun setList() {// list = {0,1,2,3,4,5,6,7,8,9,10}val list = Array(11, { i -> i })//any —— 判断集合中 是否有满足条件 的元素;输入结果:falseprintln("Hebin${list.any({ it > 10 })}")//all —— 判断集合中的元素 是否都满足条件;输入结果:trueprintln("Hebin${list.all { it in 0..20 }}")//none —— 判断集合中是否 都不满足条件,是则返回true;输入结果:trueprintln("Hebin${list.none({ it > 10 })}")//count —— 查询集合中 满足条件 的 元素个数;输出结果:6println("Hebin${list.count({ it > 4 })}")//reduce —— 从 第一项到最后一项进行累计 ;输出结果:55println("Hebin${list.reduce({ total, next -> total + next })}")//reduceRight —— 从 最后一项到第一项进行累计;输出结果:55println("Hebin${list.reduceRight({ total, next -> total + next })}")//fold —— 与reduce类似,不过有初始值,而不是从0开始累计;输出结果:55+1=56println("Hebin${list.fold(1) { total, next -> total + next }}")//foldRight —— 和reduceRight类似,有初始值,不是从0开始累计;输出结果:55+3=58println("Hebin${list.foldRight(3) { total, next -> total + next }}")//forEach —— 循环遍历元素,元素是it,可对每个元素进行相关操作;输出结果:0,1,2,3,4,5,6,7,8,9,10list.forEach { println("Hebin$it") }println()//forEachIndexed —— 循环遍历元素,同时得到元素index(下标);输出结果:(0:0),(1:1)...(10:10)list.forEachIndexed { index, it -> println("Hebin($index:$it)") }println()//max —— 查询最大的元素,如果没有则返回null;输出结果:10println("Hebin${list.max()}")//maxBy —— 获取方法处理后返回结果最大值对应的那个元素的初始值,如果没有则返回null;输出结果:5println("Hebin${list.maxBy { it * (list.size - it) }}")//min —— 查询最小的元素,如果没有则返回null;输出结果:0println("Hebin${list.min()}")//minBy —— 获取方法处理后返回结果最小值对应那个元素的初始值,如果没有则返回null;输出结果:0println("Hebin${list.minBy { it * (list.size - it) }}")//sumBy —— 获取 方法处理后返回结果值 的 总和;输出结果:385println("Hebin${list.sumBy { it * it }}")//drop--返回去掉前n个元素后的列表;输出结果:4,5,6,7,8,9,10println("Hebin${list.drop(4)}")//dropWhile —— 返回从第一项起,去掉满足条件的元素,直到不满足条件的一项为止;输出结果:4,5,6,7,8,9,10println("Hebin${list.dropWhile { it < 4 }}")//dropLastWhile--返回从最后一项起,去掉满足条件的元素,直到不满足条件的一项为止;输出结果:0,1,2,3,4println("Hebin${list.dropLastWhile { it > 4 }}")//take--从第一个开始的n个元素;输出结果:0,1,2,3println("Hebin${list.take(4)}")//takeLast —— 返回从最后一个开始的n个元素;输出结果:7,8,9,10println("Hebin${list.takeLast(4)}")//takeWhile--返回不满足条件的下标前面的所有元素的集合;输出结果:0,1,2,3println("Hebin${list.takeWhile { it < 4 }}")//filter--过滤掉所有不满足条件的元素;输出结果:0, 2, 4, 6, 8, 10println("Hebin${list.filter { it % 2 == 0 }}")//filterNot —— 过滤掉所有满足条件的元素;输出结果:1, 3, 5, 7, 9println("Hebin${list.filterNot { it % 2 == 0 }}")//filterNotNull--过滤掉所有值为null的元素;输出结果:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10println("Hebin${list.filterNotNull()}")}

T-MVP在kotlin中的应用

mvp模式在Android中是被广泛接受,但是mvp的缺点之一就是类特别多,解决这个问题,有个很好的办法,就是用泛型。因为我在小公司,做的都是一些比较小的项目,没有涉及到太多的业务逻辑,所以,我这里直接省去了M层,只剩下v跟p;

第一步,创建一个接口,实现一些基础方法,然后让BaseActivity继承这个view实现这些方法,一般是一些通用的方法,例如加载对话框、隐藏对话框等,然后让所有的Activity继承BaseActivity

interface BaseView {// 显示加载对话框fun showLoading()// 隐藏加载对话框fun hideLoading()// 显示空数据布局fun showNullLayout()// 隐藏空数据布局fun hideNullLayout()// 显示请求失败布局fun showErroLayout()// 隐藏请求失败布局fun hideErroLayout()// 请求失败后,将得到的结果通过接口,从P层传到V层fun getFailed(type: Any, T: Any)
}

第二步,创建一个接口,继承BaseView,然后再实现多两个接口,供v层调用

interface UniversalView : BaseView {// view层设置数据,Presenter层通过接口得到数据,用于网络请求等参数的动态改变fun getData(): Any// 请求成功后,将得到的结果通过接口,从P层传到V层fun getSuccess(type: Any, T: Any)}

第三步,封装基础的网络请求,我用的是OkGo,网络请求分为get请求跟post请求,代码分别如下:

GET

open class BaseOkgoGet : SingletonUtil<BaseOkgoGet>() {override fun newInstance(): BaseOkgoGet {return getInstance() as BaseOkgoGet}companion object {/***@param type 用于一个Activity有多个网络请求的时候,在Presenter中区分回调类型** @param url 请求网址** */fun getData(context: Context, type: Int, url: String, universal: UniversalView, successListener: SuccessListener) {OkGo.get(url).execute(object : StringCallback() {override fun onBefore(request: BaseRequest<*>?) {universal.showLoading()}override fun onSuccess(s: String, call: Call, response: Response) {ToastUtil.printData(s)universal.hideErroLayout()successListener.onSuccess(context, type, s)}override fun onError(call: Call?, response: Response?, e: Exception?) {universal.showErroLayout()}override fun onAfter(s: String?, e: Exception?) {universal.hideLoading()}})}}
}

POST

class BaseOkgoPost : SingletonUtil<BaseOkgoPost>() {override fun newInstance(): com.zerom.management.base.okgo.BaseOkgoPost {return getInstance()!!}companion object {/*** @param type 用于一个Activity有多个网络请求的时候,在Presenter中区分回调类型** @param map post 的参数** @param url 请求网址** */fun getData(context: Context, type: Int, url: String, map: HashMap<String, String>, universalView: UniversalView, successListener: SuccessListener) {com.lzy.okgo.OkGo.post(url).params(map).execute(object : com.lzy.okgo.callback.StringCallback() {override fun onBefore(request: com.lzy.okgo.request.BaseRequest<out BaseRequest<*>>?) {super.onBefore(request)universalView.showLoading()}override fun onSuccess(result: String?, p1: okhttp3.Call?, p2: okhttp3.Response?) {universalView.hideErroLayout()successListener.onSuccess(context, type, result!!)ToastUtil.printData(result)}override fun onAfter(t: String?, e: java.lang.Exception?) {super.onAfter(t, e)universalView.hideLoading()}override fun onError(call: okhttp3.Call?, response: okhttp3.Response?, e: java.lang.Exception?) {super.onError(call, response, e)universalView.showErroLayout()}})}}}

请求网络数据,得到的结果,通过SuccessListener接口抛出;

interface SuccessListener {fun onSuccess(context: Context, type: Any, results: String)
}

然后,如果在一个Presenter中,有多个网络请求,可以用以下的写法来实现Presenter

class MainPresenter(val context: Context, val view: UniversalView) : SuccessListener {companion object {val GET_SUCCESS_01 = 1val GET_SUCCESS_02 = 2}val entity: DataEntity = view.getData() as DataEntityfun getData_1() {BaseOkgoGet.getData(context, GET_SUCCESS_01, "url${entity.title}", view, this)}fun getData_2() {val map = hashMapOf("id" to entity.id, "title" to entity.title)BaseOkgoPost.getData(context, GET_SUCCESS_02, "url", map, view, this)}override fun onSuccess(context: Context, type: Any, results: String) {when (type) {GET_SUCCESS_01 -> {view.getSuccess(GET_SUCCESS_01, "我是第一个请求")}GET_SUCCESS_02 -> {view.getSuccess(GET_SUCCESS_02, "我是第二个请求")}}}
}

然后在Activity中,设置参数,发起请求,得到数据之后,加载到视图中

class MainActivity : BaseActivity(), UniversalView {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tvTitle.setOnClickListener { toast("点击了标题") }//实现网络请求val presenter: MainPresenter = MainPresenter(this, this)presenter.getData_1()presenter.getData_2()}// 将数据传给p层override fun getData(): Any {val entity = DataEntity()entity.title = "我是标题"entity.id = "我是ID"return entity}override fun getSuccess(type: Any, T: Any) {when (type) {// 第一个网络请求成功MainPresenter.GET_SUCCESS_01 -> {val info = T as Stringtoast(info)}//第二个请求成功MainPresenter.GET_SUCCESS_02 -> {val info = T as Stringtoast(info)}}}
}

MVP的一大特性就是可重复调用,比如说,写一个获取用户信息的Presenter,有十个界面需要获取用户信息,那么都只要调用这一个Presenter就足够了;

kotlin的最强库anko的详细使用

学kotlin的人,基本都知道anko,它的强大已经不是一两句话就能说得清楚的了。具体可以通过anko插件库以及anko wiki学习。

用kotlin之前,要先添加依赖

    compile "org.jetbrains.anko:anko:$anko_version"compile "org.jetbrains.anko:anko-support-v4:$anko_version"//版本号ext.anko_version = '0.10.1'

anko提供很多简化的方法,例如

        toast("")// 跳转ActivitystartActivity<MainActivity>()//跳转Activity带参数startActivity<MainActivity>("TAG" to "title")// startActivityForResultstartActivityForResult<MainActivity>(1,"TAG" to "title","name" to "Hebin")// 带启动模式的startActivitystartActivity(intentFor<MainActivity>("TAG" to "title").singleTop())

anko除了提供大量简化的方法之外,还提供了一种DSL布局,用来代替xml;DSL布局看上去很清晰,书写、熟悉难度也不大,最大的一个缺点,估计就是,没得预览;虽然官方有提供Anko Preview,但是,在高版本的Android Studio中,无法使用。。

DSL布局可以直接写在Activity中,但是,我不喜欢把所有代码都堆在一块,所以,我是新建了个Mainview.kt,布局内添加一个按钮,代码如下:

class Mainview : AnkoComponent<Activity> {companion object {val BTN_ID = 1}override fun createView(ui: AnkoContext<Activity>) = with(ui) {// verticalLayout相当于垂直方向的LinearLayoutverticalLayout {gravity = Gravity.CENTER// dip 是将dp转换为px,px2dip是将px转换为dp,px2sp是将px转换为sppadding = dip(10)backgroundColor = Color.GRAYlparams {width = matchParentheight = matchParent}button("登录") {id = BTN_IDtextSize = 16ftextColor = Color.WHITEonClick { toast("点击了按钮") }backgroundColor = resources.getColor(R.color.colorPrimary)}.lparams {width = matchParentheight = dip(50)}}}
}

然后在Activity的oncreat中,setContentView即可;

 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)Mainview().setContentView(this)}

效果图如下:

![这里写图片描述](https://img-blog.csdn.net/20170728193923264?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSGViaW4zMjAzMjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 成功加载布局之后,那么接下来就是怎么对布局中的控件进行操作,从上面布局的代码中可以看出,按钮button有个ID。所以,在Activity中就可以通过ID来操控控件,具体代码如下:

 find<Button>(Mainview.BTN_ID).text = "我成功改变了按钮文字"

这种方法虽然可以操作控件,但是,当你在实践的过程你会发现,ID只能是数字,当一个布局嵌套了多个布局的时候,ID的赋值会变得很头大,所以我就换了种方式实现,修改了一下view的代码:

@SuppressLint("StaticFieldLeak")
class Mainview : AnkoComponent<Activity> {companion object {var btnLoad: Button? = null}override fun createView(ui: AnkoContext<Activity>) = with(ui) {// verticalLayout相当于垂直方向的LinearLayoutverticalLayout {gravity = Gravity.CENTER// dip 是将dp转换为px,px2dip是将px转换为dp,px2sp是将px转换为sppadding = dip(10)backgroundColor = Color.GRAYlparams {width = matchParentheight = matchParent}btnLoad = button("登录") {textSize = 16ftextColor = Color.WHITEonClick { toast("点击了按钮") }backgroundColor = resources.getColor(R.color.colorPrimary)}.lparams {width = matchParentheight = dip(50)}}}
}

创建了一个Button,并且将这个布局用到创建布局中,然后在Activity中操作控件:

Mainview.btnLoad?.text  = "我成功改变了按钮文字"

然后,你可能又会想到一个问题,很多界面都会有标题栏,如果我想把标题栏写成一个布局,然后让需要用到标题栏的布局,直接包含它就可以,就跟xml的include一样。其实DSL也是支持include布局中的xml文件的;

 include<LinearLayout>(R.layout.activity_main)

但是,既然都用DSL布局了,那就全都用DSL吧,首先,创建一个SimpleTitle.kt文件,可以通过以下代码实现布局的可调用性;

@Suppress("NOTHING_TO_INLINE")
inline fun ViewManager.simpleTitle() = simpleTitle({})inline fun ViewManager.simpleTitle(init: SimpleTitle.() -> Unit, theme: Int = 0) = ankoView(::SimpleTitle, theme, init)

标题栏布局中有个返回按钮跟一个居中的标题,具体代码如下;

class SimpleTitle : RelativeLayout {companion object {var ivBack: ImageView? = nullvar tvTitle: TextView? = null}@SuppressLint("ResourceType")private fun init() = AnkoContext.createDelegate(this).apply {relativeLayout {lparams {width = matchParentheight = matchParent}backgroundColor = resources.getColor(R.color.colorPrimary)ivBack = imageView {scaleType = ImageView.ScaleType.CENTERimageResource = R.drawable.ic_back_white}.lparams {width = dip(50)height = matchParent}tvTitle = textView("我是标题") {textSize = 20ftextColor = Color.WHITE}.lparams {centerInParent()}}}constructor(context: Context?) : super(context) {init()}constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {init()}constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {init()}
}@Suppress("NOTHING_TO_INLINE")
inline fun ViewManager.simpleTitle() = simpleTitle({})inline fun ViewManager.simpleTitle(init: SimpleTitle.() -> Unit, theme: Int = 0) = ankoView(::SimpleTitle, theme, init)

然后在Mainview的布局中添加多一句` simpleTitle()`,即可将标题栏给包含进来,同样,在Activity中,可以直接拿到标题栏布局中的控件进行操作;

     SimpleTitle.ivBack?.setOnClickListener({ finish() })SimpleTitle.tvTitle?.text = "我是标题"

kotlin可以学习的东西还有很多,将来如果有什么学习体会,会继续补充进来;如果有哪儿说得不好,或者有更好的学习的东西,欢迎回复或者私信我。

项目在GitHub的地址

https://github.com/Hebin320/KotlinStudy

项目在CSDN的下载地址

http://download.csdn.net/download/hebin320320/9913739

Kotlin的学习汇总相关推荐

  1. Kotlin语言学习笔记

    目录 文章目录 目录 汇总一 1.变量 2.kotlin的range表达式 3.kotlin的when表达式 4.kt的String模板 5.kt的函数 6.kt的匿名函数 7.kt语言的函数引用学习 ...

  2. stm32怎么加载字库_收藏 | STM32单片机超详细学习汇总资料(二)

    点击"蓝字"关注我们 3110月 收藏 | STM32单片机超详细学习汇总资料(一) ◆41.DMA仲裁器分为软件和硬件两种.软件部分分为4个等级,分别是很高优先级.高优先级.中等 ...

  3. 【C++学习汇总】【黑马程序员】

    [C++学习汇总] 1 黑马程序员 2 深蓝学院 3 自发式收集学习 1 黑马程序员 [C++][第一篇][黑马 p84 - p105 ][引用][重载][类和对象-struct.class] [C+ ...

  4. Java基础学习汇总

    Java基础学习汇总 java语言基础 java函数和数组 java面向对象 java异常 整理用,早就停更... 写作不易,如果您觉得写的不错,欢迎给博主点赞.收藏.评论.收藏来一波~让博主更有动力 ...

  5. kotlin的学习记录

    kotlin的学习记录 前言 一.Kotlin是什么? 二.学习引入 1.第一个Kotlin程序 2.基本语法 三.Kotlin 基本数据类型 1. Kotlin 的基本数值类型包括 2.比较 3. ...

  6. 【廖雪峰Java入门】学习汇总

    [廖雪峰Java入门]学习汇总 一.快速入门 1. 基本概念 2. 语言基础 3. 流程控制 4. 数组操作 二.面向对象编程 1. 面向对象基础 2. Java核心类 三.The End 网址:Ja ...

  7. Java学习第七天 ———— 第一周学习汇总(粗略)

    Java学习第七天 ---- 第一周学习汇总 第一章Java Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大 ...

  8. 自我学习汇总:SpringBoot篇

    自我学习汇总:SpringBoot篇 什么是SpringBoot?为什么要用SpringBoot? SpringBoot是一款基于Spring的轻量化技术框架,SpringBoot便于搭建环境,省略了 ...

  9. Kotlin基础学习(1)

    Kotlin基础学习(1) 本文主要讲解kotlin的部分基础知识,并不是全部基础. 提示:纯属个人理解,如有理解错误,欢迎留言指正批评. 一.Null检查机制 kotlin对于声明可为空的参数,使用 ...

最新文章

  1. js_sqlite_ADODB.Connection
  2. android 反编译_Android 反编译实战
  3. OpenCVSharp::FindContours 错误:“total()==0||data!=NULL“
  4. DIV布局之position详解
  5. 为什么 kubernetes 天然适合微服务
  6. CV之IC:计算机视觉之图像分类(Image Classification)方向的简介、使用方法、案例应用之详细攻略
  7. 小白学习机器学习---第六章:SVM算法原理(1)
  8. C 多线程的互斥锁应用RAII机制
  9. win7中输入文件夹首字母跳到相应的文件或者文件夹,却在搜索栏出现输入的字母...
  10. java性能最好的mvc框架_详解Spring MVC的异步模式(高性能的关键)
  11. 数据库迁移Flyway
  12. 推荐系统实践:从多领域优化到AutoML框架
  13. 教你在Zabbix环境下快速升级nginx版本!
  14. android自定义手势解锁View
  15. 李沐动手学深度学习V2-语义分割和Pascal VOC2012数据集加载代码实现
  16. 【聚类3】密度聚类+层次聚类
  17. ubuntu安装xp字体
  18. Win10指定用户访问共享文件及“无法访问。你可能没有权限使用网络资源。”问题解决
  19. reentrantlock与synch区别优点
  20. 设置PPT幻灯版自动翻页播放

热门文章

  1. 硬盘分区2----GPT与MBR的区别
  2. Python简单爬取电影磁力链接
  3. 【硬件】电脑主机结构 | 总分总
  4. 水果掉落小游戏(原生js+css动画)
  5. linux ip转发 丢包,高并发下iptables丢包导致网络变慢解决方法
  6. QT模拟表单上传文件到微信服务器
  7. Jquery考试面试题(二)
  8. origin绘图软件中文版下载和安装教程
  9. signature=78718ebfda6f8d955fae3e9c9c284f5d,OFDMA PREAMBLES SYSTEM AND METHOD
  10. java求两点之间的斜率