#关于Android新语言Kotlin初识
2017年5月的IO大会上,Google宣布,kotlin成为开发Android的一级编程语言.
Kotlin不仅可以再JVM上运行,还可以直接将Kotlin源代码转换成JavaScript,理论上将,kotlin可以在任何支持JavaScript的环境中运行,例如WEB应用,reactNatice(Android和IOS),微信公众号,微信小程序,Node.js等,另外,还有一些地方是JavaScript做不到的,如开发本地应用,但kotlin可以做到,kotlin不仅仅可以生成JavaScript代码,还可以直接编译成本地代码,如Windows的exe文件,iOS APP等,这样一来,kotlin几乎和可以开发所有类型的应用.
##kotlin的优势:
- 容易学习
kotlin是一门包含很多函数式编程思想的面相对象编程语言
- 轻量级
相比其他编程语言,kotlin的函数库更小,小于7000个
- 高度可互操作性:
kotlin可以和其他java类库友好且简单的进行互操作
- 继承Androidstudio及gradle
专门的插件,和工具
- 其他语法层面的特性,如数据模型类,空类型安全,扩展函数等
kotlin能做什么
- 服务端开发
kotlin是基于JVM的编程语言,自然可以使用所有基于JVM的服务端框架 例如:spring
- 以JavaScript方式运行
kotlin提供了生成JavaScript源代码的能力
- 开发Android App
kotlin和java源代码可以在同一个工程中,可以联合进行调试
##kotlin的基本语法
- 定义变量
// 定义变量
var m: Int? = 0
// 自动推导变量的数据类型
var n = 10
// 定义常量
val f = 30
// 定义数组
var arr1 = arrayOf(1, 2, 3, 4)
var arr3: IntArray? = null
// 定义集合
var list1 = arrayListOf<String>()
var list2: ArrayList<String>? = null
// 定义字符串
var s1 = "Hello \nworld"
var s2: String = "世界\n 你好"
var s3 = """
hello
world
I love you.
"""
复制代码
- 定义方法
// 定义普通方法
fun test() {
}
// 定义带返回值方法
fun test1(): String {
return "返回值"
}
复制代码
- get,set方法
var address: String = ""
get() {
println("get Method is run")
return if (field.contains("北京市")) {
field.replace("北京市", "许昌市")
} else {
field
}
}
set(value) {
println("setMethod is run")
field = value
}
复制代码
- 定义构造器
// 一级构造器
class KotlinBean constructor() {
}
// 二级构造器
class KotlinBean constructor() {
constructor(index: Int) : this()
}
复制代码
- 数值类型
数据类型 | 占用字节数 |
Double | 8 |
Float | 4 |
Long | 8 |
Int | 4 |
Short | 2 |
Byte | 1 |
- 字符串模板
// 字符串模板 添加占位符 $
override fun toString(): String {
return "KotlinBean(m=$m, n=$n, f=$f, arr1=${Arrays.toString(arr1)}, arr3=${Arrays.toString(arr3)}, list1=$list1, list2=$list2)"
}
复制代码
- if语句
// 定义带返回值方法
fun test1(): String {
return if (n > 10) {
"ssss"
} else {
"返回值"
}
}
复制代码
- when语句(替代原来的switch语句)
rg_navigation.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
R.id.rb_home -> {
switchTab(0)
}
R.id.rb_mirror -> {
switchTab(1)
}
R.id.rb_bbs -> {
switchTab(2)
}
R.id.rb_mine -> {
switchTab(3)
}
}
}
fun test2(n: Int) {
when (n) {
1 -> {
}
2 -> {
}
// 使用in关键字
in 3..5 -> {
}
// 使用函数表达式的返回值
test3() -> {
}
else -> {
}
}
}
复制代码
- for循环
fun test4() {
// 强循环,直接遍历
list1.forEach { t: String? -> println(t) }
for (s: String in list1) {
println(s)
}
// 根据角标遍历
for (i in list1.indices) {
println(list1[i])
}
// 根据角标遍历,同时获取角标和对应的元素值
for ((index, value) in list1.withIndex()) {
println(index)
println(value)
}
}
复制代码
- 修饰符
- private 仅在类的内部可以访问
- protected 类似private,但在子类中也可以访问
- internal 任何在模块内部类都可以访问
- public 任何类都可以访问
- 类的继承
与Java不同,kotlin类的继承需要使用冒号(:),冒号后面需要调用父类的构造器
需要注意的是kotlin默认是的class是final的,也就是说默认class不允许继承,需要显示的使用open关键字允许继承class
open class KotlinBean constructor() {
constructor(index: Int) : this()
}
复制代码
- 重写方法 & 重写属性
在kotlin中,不仅类默认是不可继承的,连方法默认也是不可重写的.因此,如果要在子类中重写方法,就需要在父类相应的方法前面加open关键字,而且要在子类重写的方法前面加override关键字
- 接口
kotlin中的接口与Java中的类似,使用interface关键字声明.一个类可以实现多个接口,并且接口中的属性和方法都是open的
interface KotlinInterfaceTest {
fun getUserInfo()
// 在kotlin中,允许接口的方法包含默认的方法体
fun setUserInfo(name: String) {
val userBean = UserBean()
userBean.name = name
}
复制代码
}
- null 值安全性
在java中饱受null异常的困扰,在kotlin中可以试图解决这个问题
kotlin中导致NPE情况
- 明确调用throw NullPointerException()
- 使用!! 操作符
- 外部的Java代码导致
- 初始化过程中存在某些数据不一致
- 开始使用kotlin进行Android开发
- 创建一个应用
勾选kotlin选项,点击Next,知道项目创建完成
- 编写XML文件
- 在activity中设置UI显示
- 其他示例
class HomeFragment @SuppressLint("ValidFragment")
private constructor() : Fragment() {
var value: HomeFragment? = null
var imageList = arrayListOf<Int>()
lateinit var mContext: Context
lateinit var mHomeAdapter: HomeAdapter
private object Holder {
val INSTANCE = HomeFragment()
}
companion object Factory {
fun getInstance(): HomeFragment {
return Holder.INSTANCE
}
}
override fun onAttach(context: Context?) {
super.onAttach(context)
mContext = context!!
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initData()
}
private fun initView() {
banner.setImageLoader(GlideImageLoader())
imageList.add(R.mipmap.ic_driver)
imageList.add(R.mipmap.ic_police)
imageList.add(R.mipmap.ic_trance)
banner.setImages(imageList).start()
rv_top_lines.setHasFixedSize(true)
rv_top_lines.layoutManager = LinearLayoutManager(mContext)
}
class MyLinear2(context: Context?) : LinearLayoutManager(context) {
override fun canScrollVertically(): Boolean {
return false
}
}
private fun initData() {
mHomeAdapter = HomeAdapter()
rv_top_lines.adapter = mHomeAdapter
HttpManager.getInstance().getRetrofit().create(IApiServices::class.java).getHostList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { t: BaseBean<List<MovieBean>>? ->
if (t != null) {
val data = t.data
data.forEach { t -> println(t.toString()) }
}
}.subscribe(object : Observer<BaseBean<List<MovieBean>>> {
override fun onSubscribe(d: Disposable) {
println("onSubscribe")
}
override fun onError(e: Throwable) {
println("onError" + e.message)
Toast.makeText(mContext, e.message, Toast.LENGTH_SHORT).show()
}
override fun onNext(t: BaseBean<List<MovieBean>>) {
println("onNext" + t.toString())
mHomeAdapter.setNewData(t.data)
}
override fun onComplete() {
println("onComplete")
Toast.makeText(mContext, "onComplete", Toast.LENGTH_SHORT).show()
}
})
}
override fun onStart() {
super.onStart()
//开始轮播
banner.startAutoPlay()
}
override fun onStop() {
super.onStop()
//结束轮播
banner.stopAutoPlay()
}
}
复制代码
- home页tab切换
package com.bidostar.kotlindemo.activity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.bidostar.kotlindemo.R
import com.bidostar.kotlindemo.fragment.BbsFragment
import com.bidostar.kotlindemo.fragment.HomeFragment
import com.bidostar.kotlindemo.fragment.MineFragment
import com.bidostar.kotlindemo.fragment.MirrorFragment
import kotlinx.android.synthetic.main.activity_home.*
class HomeActivity : AppCompatActivity() {
// 1. lateinit 延迟加载
// 2.lateinit 只能修饰, 非kotlin基本类型
lateinit var mHomeFragment: HomeFragment
lateinit var mMirrorFragment: MirrorFragment
lateinit var mBbsFragment: BbsFragment
lateinit var mMineFragment: MineFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
initView()
val transaction = supportFragmentManager.beginTransaction()
transaction.add(R.id.fl_content, mHomeFragment, "home")
transaction.add(R.id.fl_content, mMirrorFragment, "mirror")
transaction.add(R.id.fl_content, mBbsFragment, "bbs")
transaction.add(R.id.fl_content, mMineFragment, "mine")
transaction.commitAllowingStateLoss()
rg_navigation.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
R.id.rb_home -> {
switchTab(0)
}
R.id.rb_mirror -> {
switchTab(1)
}
R.id.rb_bbs -> {
switchTab(2)
}
R.id.rb_mine -> {
switchTab(3)
}
}
}
switchTab(0)
}
private fun initView() {
mHomeFragment = HomeFragment.getInstance()
mMirrorFragment = MirrorFragment.getInstance()
mBbsFragment = BbsFragment.getInstance()
mMineFragment = MineFragment.getInstance()
}
private fun switchTab(index: Int) {
val transaction = supportFragmentManager.beginTransaction()
when (index) {
0 -> {
transaction.show(mHomeFragment)
.hide(mMirrorFragment)
.hide(mBbsFragment)
.hide(mMineFragment)
.commitAllowingStateLoss()
}
1 -> {
transaction.show(mMirrorFragment)
.hide(mHomeFragment)
.hide(mBbsFragment)
.hide(mMineFragment)
.commitAllowingStateLoss()
}
2 -> {
transaction.show(mBbsFragment)
.hide(mHomeFragment)
.hide(mMirrorFragment)
.hide(mMineFragment)
.commitAllowingStateLoss()
}
3 -> {
transaction.show(mMineFragment)
.hide(mHomeFragment)
.hide(mMirrorFragment)
.hide(mBbsFragment)
.commitAllowingStateLoss()
}
}
}
}
复制代码
- adapter使用
package com.bidostar.kotlindemo.adapter
import com.bidostar.kotlindemo.R
import com.bidostar.kotlindemo.bean.MovieBean
import com.bumptech.glide.Glide
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.BaseViewHolder
/**
* @author zsh27
* @date 2017/12/14.
* description .
* @since 0
*/
class HomeAdapter(layoutResId: Int) : BaseQuickAdapter<MovieBean, BaseViewHolder>(layoutResId) {
init {
}
constructor() : this(R.layout.home_movie_item)
override fun convert(helper: BaseViewHolder?, item: MovieBean?) {
if (item != null) {
helper?.setText(R.id.tv_name, item.movieName)
helper?.setText(R.id.tv_desc, item.name)
Glide.with(mContext)
.load(item.img)
.into(helper?.getView(R.id.iv_cover))
}
}
}
复制代码
项目实战
kotlin&java混编
- 项目配置
project级的gradle配置
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.tencent.bugly:tinker-support:1.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
module级的gradle配置
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation "junit:junit:$rootProject.dependVersion.junit"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.android.support:design:$rootProject.dependVersion.support"
implementation "com.android.support.constraint:constraint-layout:$rootProject.dependVersion.constraint_layout"
}
复制代码
- 常量类
object NetConstant {
/**
* 连接超时
*/
const val DEFAULT_CONNECT_TIME_OUT = 15L
/**
* 读取超时
*/
const val DEFAULT_READ_TIME_OUT = 15L
/**
* 写入超时
*/
const val DEFAULT_WRITE_TIME_OUT = 30L
}
复制代码
- 单例类
class HttpManager private constructor() {
/**
* 构造方法私有
*/
init {
Logger.d("HttpManager:" + "init:" + "_debug:" + Debug)
initRetrofit()
}
companion object {
/**
* 单例对象
*/
var BaseUrl: String? = null
var BaseResourceUrl: String? = null
var Debug = false
var InterceptorList: List<Interceptor>? = null
var NetworkInterceptorList: List<Interceptor>? = null
/**
* 获取HttpManager
*
* @return HttpManager实例 instance
*/
val instance: HttpManager
get() {
return Holder.INSTANCE
}
fun paramsBuild(): HttpManager.ParamsBuild {
return HttpManager.ParamsBuild()
}
/**
* 自定义Retrofit示例
* 可以设置一些自定义参数,通过buildRetrofit()构建Retrofit实例
* 或者通过buildApiService()直接构建一个网络请求接口
*
* @return 自定义RetrofitBuilder http builder . builder
*/
fun newBuilder(): HttpBuilder.Builder {
return HttpBuilder.Builder()
}
}
}
使用方法
1. java文件中使用
HttpManager.Companion.getInstance()
.create(IAppServices.class)
.getDeviceActivateStatus()
.compose(RxSchedulers.INSTANCE.<com.bidostar.netlibrary.BaseResponse<DeviceStatusBean>>applyIoSchedulers())
.compose(this.<com.bidostar.netlibrary.BaseResponse<DeviceStatusBean>>bindToLifecycle())
2. kotlin文件中使用
HttpManager.instance
.create(IAppServices::class.java)
.deleteDevicePic(mDeviceCode, bean.id.toLong())
.compose(RxSchedulers.applyIoSchedulers())
.compose(this.bindToLifecycle())
复制代码
- view控件
- findViewById()
var mTvTitle = findViewById(R.id.tv_title)
var mIvBack = findViewById(R.id.iv_back)
复制代码
- kotlin注解方式
import kotlinx.android.synthetic.main.activity_mirror_album_detail.*
mAdapter = AlbumPagerAdapter(mContext)
view_pager.adapter = mAdapter
view_pager.addOnPageChangeListener(this)
mAdapter!!.setNewData(mItemList)
view_pager.currentItem = mIndex
mIvBack.setOnClickListener {
finish()
}
tv_delete.setOnClickListener(this)
tv_download.setOnClickListener(this)
复制代码
- mvp使用
M层
class DeviceCaptureModelImpl : BaseModel(), DeviceCaptureContract.IDeviceCaptureModel {
override fun getLastPicture(alarmId: Int, deviceCode: Long, alarmDay: String, pageSize: Int, respType: String): Observable<BaseResponse<List<MirrorAlbumBean>>> {
return HttpManager.instance
.create(IAppServices::class.java)
.getDeviceAlarm(alarmId, deviceCode, alarmDay, pageSize, respType)
.compose(RxSchedulers.applyIoSchedulers())
}
override fun getDeviceStatus(deviceCode: Long, rousing: Boolean): Observable<BaseResponse<Int>> {
return HttpManager.instance.create(IAppServices::class.java)
.deviceStatus(deviceCode, rousing)
.compose(RxSchedulers.applyIoSchedulers())
}
override fun devicecapture(deviceCode: Long): Observable<BaseResponse<CaptureBean>> {
return HttpManager.instance.create(IAppServices::class.java)
.deviceCapture(deviceCode)
.compose(RxSchedulers.applyIoSchedulers())
}
override fun sendMessage(deviceCode: Long, content: String): Observable<BaseResponse<String>> {
return HttpManager.instance.create(IAppServices::class.java)
.sendTextToDevice(deviceCode, content)
.compose(RxSchedulers.applyIoSchedulers())
}
}
P层
class DeviceCapturePresenterImpl : BasePresenter<DeviceCaptureContract.IDeviceCaptureView, DeviceCaptureModelImpl>(),
DeviceCaptureContract.IDeviceCapturePresenter {
override fun getDeviceStatus(deviceCode: Long, rousing: Boolean) {
m.getDeviceStatus(deviceCode, rousing)
.compose(RxSchedulers.applyIoSchedulers())
.subscribe(object : BaseObserver<Int>() {
override fun handleResult(response: BaseResponse<Int>) {
when (response.resultCode) {
BaseResponse.RET_HTTP_STATUS_OK -> {
when (response.data) {
BaseResponse.RET_HTTP_STATUS_OK -> {
v.onLine()
}
BaseResponse.RET_MIRROR_STATUS_NO -> {
v.offLine()
}
BaseResponse.RET_MIRROR_STATUS_YES -> {
v.onStarting()
}
}
}
BaseResponse.RET_TIMEOUT_CODE -> {
v.timeOut()
}
else -> {
v.onError()
v.showErrorTip(response.errorMsg)
}
}
}
})
}
}
V层
@Route(path = ARouterConstant.DEVICE_CAPTURE)
class DeviceCaptureActivity : BaseMvpActivity<DeviceCapturePresenterImpl>(), DeviceCaptureContract.IDeviceCaptureView,
Handler.Callback, DeviceConnectDialog.onMirrorConnectClickListener {
private lateinit var mTvTitle: TextView
private lateinit var mIvBack: ImageView
private var mCar: Car? = null
private var mHandler: WeakRefHandler? = null
private var mIsCapture = false
private var mContent = ""
private var mConnectDialog: DeviceConnectDialog? = null
override fun newPresenter(): DeviceCapturePresenterImpl {
return DeviceCapturePresenterImpl()
}
override fun getLayoutView(savedInstanceState: Bundle?): Int {
return R.layout.activity_device_capture
}
override fun initView(savedInstanceState: Bundle?) {
mIvBack = findViewById(R.id.iv_back)
mTvTitle = findViewById(R.id.tv_title)
mIvBack.setOnClickListener {
finish()
}
mTvTitle.text = "后视镜远程操控"
mIvBack.setOnClickListener {
finish()
}
ivCapture.setOnClickListener {
mIsCapture = true
if (mCar?.deviceType != 1) {
DialogUtils.showBindDialog(mContext)
return@setOnClickListener
}
ivCapture.isClickable = false
showMirrorConnectDialog()
p.getDeviceStatus(mCar!!.deviceCode, true)
}
ivAlbum.setOnClickListener {
if (mCar?.getDeviceType() != 1) {
DialogUtils.showBindDialog(mContext)
return@setOnClickListener
}
ARouter.getInstance().build(ARouterConstant.DEVICE_CAPTURE_ALBUM)
.navigation()
}
tvSendMessage.setOnClickListener {
mIsCapture = false
if (mCar!!.deviceType != 1) {
DialogUtils.showBindDialog(mContext)
return@setOnClickListener
}
mContent = etMessage.text.toString().trim()
if (TextUtils.isEmpty(mContent)) {
showToast("消息内容不能为空")
return@setOnClickListener
}
tvSendMessage.isClickable = false
showMirrorConnectDialog()
p.getDeviceStatus(mCar!!.deviceCode, true)
}
}
override fun initData() {
mCar = ApiCarDb.getCar(mContext)
mHandler = WeakRefHandler(this)
if (mCar?.deviceType == 1) {
p.getLastPicture(207, mCar!!.deviceCode, "", 1, "list")
}
}
}
复制代码
- java文件转换成kotlin
java文件
public class AlbumPagerAdapter extends PagerAdapter {
private List<MirrorAlbumItemBean> mDataList;
private Context mContext;
private final LayoutInflater mInflater;
public AlbumPagerAdapter(Context context) {
mContext = context;
mInflater = LayoutInflater.from(mContext);
if (mDataList == null) {
mDataList = new ArrayList<>();
}
}
public void setNewData(List<MirrorAlbumItemBean> list) {
if (mDataList == null) {
mDataList = new ArrayList<>();
}
mDataList.clear();
mDataList.addAll(list);
notifyDataSetChanged();
}
@Override
public int getCount() {
return mDataList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return object == view;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
MirrorAlbumItemBean bean = mDataList.get(position);
View inflate = mInflater.inflate(R.layout.device_album_viewpager_item_layout, container, false);
ImageView imageView = inflate.findViewById(R.id.pv_image);
TextView tvLocation = inflate.findViewById(R.id.tv_location);
TextView tvTime = inflate.findViewById(R.id.tv_time);
tvLocation.setText(TextUtils.isEmpty(bean.getLocation()) ? "位置位置" : bean.getLocation());
tvTime.setText(bean.getAlarmTime());
Glide.with(mContext)
.load(bean.getUrl())
.into(imageView);
container.addView(inflate);
return inflate;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
复制代码
```
class AlbumPagerAdapter(private val mContext: Context) : PagerAdapter() {
private var mDataList: MutableList<MirrorAlbumItemBean>? = null
private val mInflater: LayoutInflater = LayoutInflater.from(mContext)
init {
if (mDataList == null) {
mDataList = ArrayList()
}
}
fun setNewData(list: List<MirrorAlbumItemBean>) {
if (mDataList == null) {
mDataList = ArrayList()
}
mDataList!!.clear()
mDataList!!.addAll(list)
notifyDataSetChanged()
}
fun removeData(position: Int) {
if (mDataList!!.size > position) {
mDataList!!.removeAt(position)
}
notifyDataSetChanged()
}
override fun getCount(): Int {
return mDataList!!.size
}
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return `object` === view
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val bean = mDataList!![position]
val inflate = mInflater.inflate(R.layout.device_album_viewpager_item_layout, container, false)
val imageView = inflate.findViewById<ImageView>(R.id.pv_image)
val tvLocation = inflate.findViewById<TextView>(R.id.tv_location)
val tvTime = inflate.findViewById<TextView>(R.id.tv_time)
tvLocation.text = if (TextUtils.isEmpty(bean.location)) "未知位置" else bean.location
tvTime.text = bean.alarmTime
Glide.with(mContext)
.load(bean.url)
.into(imageView)
container.addView(inflate)
return inflate
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(`object` as View)
}
override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}
}
复制代码