初学Kotlin——在自定义View里的应用
什么是Kotlin
Kotlin
,它是JetBrains
开发的基于JVM的面向对象的语言。2017年的时候被Google
推荐Android
的官方语言,同时Android studio 3.0
正式支持这门语言,在这个编译器上创建一个Kotlin
项目,非常方便,甚至可以Java
转为Kotlin
。
我主要是在通过实现自定义View
过程中,说一下Kotlin
与Java
的异同,其实两者非常相似
对Kotlin
语法不是太了解的,可以先去看看它的官方翻译文档
以Barchart-Kotlin开始说起
Barchart-Kotlin是我用Kotlin
写的一个简易灵活的柱状图库,喜欢的可以点个star!
1.类的属性
在一个类里面我们需要定义一些属性来保存数据和状态
我们先来看看Java
代码,在BarChartView
定义了一些属性
private SpeedLinearLayoutManger mLayoutManager;private BarChartAdapter mAdapter;private ItemOnClickListener mClickListener;private int mDefaultWidth = 150;
复制代码
然后我们再看看Kotlin
是怎么定义这些属性的,下面的是Kotlin
代码
private lateinit var mLayoutManager: SpeedLinearLayoutMangerprivate lateinit var mAdapter: BarChartAdapterprivate var mClickListener: ItemOnClickListener? = nullprivate val mDefaultWidth = 150
复制代码
你会发现不一样的声明方式,但重要的是var
和val
这两个关键字
var
代表的是可变的变量,相当于现在Java
声明变量的方式
val
代表的是不可变的变量,初始化后不能再修改,相当于加了final
关键字的变量
而且在Kotlin
中属性是需要初始化的,没有值的时候你可以赋值null
,不然编译会报错。加上?
的意思是你不确定是否是这个类型,或者说是否为null
。如果觉得实在是不方便你的使用逻辑,你可以使用这两种方式延迟初始化。
懒初始化 by lazy
lazy
是指推迟一个变量的初始化时机,只有在使用的时候才会去实例化它。适用于一个变量直到使用时才需要被初始化。在我这个项目里面没有使用by lazy
,它大致的用法是这样的
val data: Data by lazy {Data(number,string)}复制代码
延迟初始化 lateinit
lateinit
是指你保证接下来在使用之前或者使用的时候会实例化它,不然你就会crash掉,这不就跟我们使用Java
属性的方式一样么。。。它适用于一些view
和必须用到数据结构的初始化,我觉得还是谨慎使用比较好。
2.空安全
Kotlin
可以说是分了两个大类型,可空类型和不可空类型,这样做的原因是它希望在编译阶段就把空指针这问题显式的检测出来,把问题留在了编译阶段,让程序更加健壮。它通过?
来表达可为空。
mClickListener?.invoke1(position)
mClickListener?.invoke2(position)
mClickListener?.invoke3(position)
复制代码
如果mClickListener
为null
的话,后面的语句是不会执行的。而且Kotlin
提供了更加简洁的操作符let
mListener?.let {
it.invoke1(position)
it.invoke2(position)
it.invoke3(position)
}复制代码
只有在非空的情况下才执行let
里面的操作,非常简洁。
3.构造器
通过简单了解之后,我们开始写一个自定义View,我们需要继承View
,Java
的实现方式是这样的
public class BarChart extends View {public BarChart(Context context) {super(context);}public BarChart(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public BarChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}复制代码
用Kotlin
你可以实现的更简洁
class BarChart @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0): View(context, attrs, defStyleAttr) {private val mContext: Context = contextinit { }
复制代码
你可以在init
代码块里面获得构造函数的传参,当然你也可以直接在声明属性的时候获得,@JvmOverloads
如果你没有加上这个注解,它只能重载相匹配的的构造函数,而不是全部。
而且可能你也发现了,你可以在传参里面初始化,这相对于Java
来说,灵活太多
fun shadow(width:Int=100,height:Int = 180){ }
//你可以这么使用
shadow()
shadow(140)
shadow(140,200)
复制代码
4.UI布局
一般用我们创造view
的布局是xml
,Kotlin
也是支持的,但是它更推荐你使用Anko
的布局方式。
那是什么是Anko
呢,Anko
是JetBrains
开发的一个强大的库,它主要的目的是用来替代以前xml
的方式来使用代码生成UI布局的,它包含了很多的非常有帮助的函数和属性来避免让你写很多的模版代码。
有兴趣的你可以去看看它的源码与更多使用方式 -- Anko
首先你要在Gradle
里添加Anko
的引用
// Anko Layouts
compile "org.jetbrains.anko:anko-recyclerview-v7:$anko_version"
compile "org.jetbrains.anko:anko-recyclerview-v7-coroutines:$anko_version"
compile "org.jetbrains.anko:anko-sdk25:$anko_version"
compile "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Coroutine listeners for Anko Layouts
compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"复制代码
然后你可以在代码里写UI布局的代码了,就是用Kotlin
代码替代xml
private fun createView(attrs: AttributeSet? = null, defStyleAttr: Int = 0) {var height = dip(150)attrs?.let {val typeArray = mContext.obtainStyledAttributes(it, R.styleable.BarChartView, defStyleAttr, 0)height = typeArray.getDimension(R.styleable.BarChartView_chart_height, dip(150).toFloat()).toInt()typeArray.recycle()}verticalLayout {lparams(width = matchParent, height = matchParent)frameLayout {lparams(width = matchParent, height = wrapContent)mLineView = view {backgroundColor = R.color.gray_light}.lparams(width = matchParent, height = dip(0.5f)) {gravity = Gravity.BOTTOMbottomMargin = dip(9)}mBarView = recyclerView {lparams(width = matchParent, height = height)}}mDateView = relativeLayout {lparams(width = matchParent, height = wrapContent) {leftPadding = dip(10)rightPadding = dip(10)}mLeftTv = textView {textSize = 15f}mRightTv = textView {textSize = 15f}.lparams(width = wrapContent, height = wrapContent) {alignParentRight()}}}}复制代码
可以从代码里看见verticalLayout
(其实就是LinearLayout
的vertical
模式)包裹了frameLayout
和relativeLayout
,里面又有各自的子view
,而且你会发现xml
有的属性这里也有,调用起来非常的简洁明了,熟练xml
的来写这个,我觉得上手应该会很快,它的缺点就是没有预览效果,以及实现复杂的view结构的时候会比较繁琐,考验盲写的功力了。。。。
5.扩展函数
扩展函数是Kotlin非常方便实用的一个功能,它可以让我们随意的扩展SDK的库,你如果觉得SDK的api不够用,这个时候你可以用扩展函数完全去自定义。
例如你需要这样来获取颜色,每次你都需要一个上下文context
mColor = ContextCompat.getColor(mContext, R.color.primary)
复制代码
那你可以通过扩展Context
这个SDK的类来实现更方便的使用
fun Context.color(colorRes: Int) = ContextCompat.getColor(this, colorRes)
fun View.color(colorRes: Int) = context.color(colorRes)
复制代码
而且为了更方便在View
里面使用,又扩展了View
。在第一个方法里面可以发现getColor
所需要的this
上下文,就是Context
。同样,View
里面的context
是getContext()
所得到,也就是View
里面本身具有的公有方法。
这其实是很惊艳的功能
那我们可以想一想为啥可以这样做,我们知道的是Kotlin
和Java
都是在Jvm
上运行的,既然都是编译成class
字节码,那我们是不是可以通过字节码来了解一些事情。
通过Android studio 3.0
上的Tools
的工具Show Kotlin Bytecode
,可以将刚才的扩展函数的代码编译成字节码
// access flags 0x19public final static color(Landroid/content/Context;I)I@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0L0ALOAD 0LDC "$receiver"INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)VL1LINENUMBER 12 L1ALOAD 0ILOAD 1INVOKESTATIC android/support/v4/content/ContextCompat.getColor (Landroid/content/Context;I)IIRETURNL2LOCALVARIABLE $receiver Landroid/content/Context; L0 L2 0LOCALVARIABLE colorRes I L0 L2 1MAXSTACK = 2MAXLOCALS = 2// access flags 0x19public final static color(Landroid/view/View;I)I@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0L0ALOAD 0LDC "$receiver"INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)VL1LINENUMBER 14 L1ALOAD 0INVOKEVIRTUAL android/view/View.getContext ()Landroid/content/Context;DUPLDC "context"INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkExpressionValueIsNotNull (Ljava/lang/Object;Ljava/lang/String;)VILOAD 1INVOKESTATIC shadow/barchart/ExtensionsKt.color (Landroid/content/Context;I)IIRETURNL2LOCALVARIABLE $receiver Landroid/view/View; L0 L2 0LOCALVARIABLE colorRes I L0 L2 1MAXSTACK = 3MAXLOCALS = 2复制代码
这就是编译后的字节码,看不懂是吧,我也看不懂。。但是我们可以反编译啊,生成Java
代码
public static final int color(@NotNull Context $receiver, int colorRes) {Intrinsics.checkParameterIsNotNull($receiver, "$receiver");return ContextCompat.getColor($receiver, colorRes);}public static final int color(@NotNull View $receiver, int colorRes) {Intrinsics.checkParameterIsNotNull($receiver, "$receiver");Context var10000 = $receiver.getContext();Intrinsics.checkExpressionValueIsNotNull(var10000, "context");return color(var10000, colorRes);}复制代码
通过反编译我们可以知道这是个静态函数,Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
这个函数只起到了判空的作用,真正的代码是return ContextCompat.getColor($receiver, colorRes);
这个不就是我们刚刚用的Java
代码嘛。
重点是$receiver
接收的对象,接收的是Context
实例,这样的话就可以调用这个类的所有公有方法和公有属性,而且它是静态函数,它可以通过类直接调用。所以扩展函数的实现只不过是加了一个需要当前对象的静态方法,调用的时候传入一个当前对象而已。
我们刚刚用到了反编译,因为我们知道Kotlin
和Java
的生成字节码是一样的,那我们可以了解一下Kotlin
的编译过程,它跟Java
的区别是什么。可以看一下这篇文章Kotlin编译过程分析
通过这篇文章你可以了解到Kotlin
在编译过程中,与Java
是大致相同的,只是在最后生成目标代码的时候做了很多类似于封装的事情,生成相同的语法结构,Kotlin
将我们本来在代码层做的一些封装工作转移到了编译后端阶段。那我们可不可以在学习Kotlin
的时候去这样理解,其实Kotlin
是一种封装了Java
的强大的语法糖,Java
做不到的事情,Kotlin
其实也做不到,例如对象只能访问公有属性。
6.数据类
在Kotlin
中你要实现数据类是非常简单的,并不需要手动加上get/set
方法
data class BarItem(private val barData: BarData,var select: Boolean = false) {fun getData(): Double {return barData.getData()}fun getTag(): String {return barData.getTag()}
}
复制代码
在这个类里面你会发现,我还声明了两个方法,我需要的是BarData
里的数据,但又不仅仅只需要这个数据,所以我声明了一个类来封装它,其实这个相当于装饰者模式了。Kotlin
有更好的方式实现这个模式
data class BarItem(private val barData: BarData,var select: Boolean = false) : BarData by barData
复制代码
7.when
在BarChartView
里用到一个与switch
语法类似的语句
mSelectPosition = when (mStyle) {ScrollStyle.DEFAULT -> mDataList.size - 1ScrollStyle.START -> 0ScrollStyle.NONE -> -1ScrollStyle.CUSTOM -> mSelectPositionelse -> { }}
复制代码
它是起到了跟switch
一样的作用,并且更强大,因为它是表达式,所以是有返回值的,在Kotlin
中控制流大都是表达式,都是可以有返回值的。
8.集合
Kotlin
是区分可变集合和不可变集合的,它给你提供这两种选择。
//不可变Set<out T>Map<K, out V>List<out T>//可变MutableSet<T>MutableMap<K, V>MutableList<T>
复制代码
不可变的集合提供只读属性,例如size
,get
等,Kotlin
不提供专门的语法结构创建list
或者set
,是用标准库获取的,我们可以看一下它的源码是怎样实现。
/*** Returns an immutable list containing only the specified object [element].* The returned list is serializable.* @sample samples.collections.Collections.Lists.singletonReadOnlyList*/
@JvmVersion
public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)/*** Returns an empty new [MutableList].* @sample samples.collections.Collections.Lists.emptyMutableList*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()复制代码
从源码可以看见这是Java
的java.util.Collections.singletonList
和ArrayList
,这就可以理解为啥不可变和可变的了。。。
总结
Kotlin
相对于Java
,更像是封装了Java
的强大语法糖,使用了更简洁的语法提高了生产力。
初学Kotlin——在自定义View里的应用相关推荐
- 如何在自定义View里使用ViewModel
前言 ViewModel只能在Activty和Fragment里使用吗,能不能在View里使用呢? 假如我要提供一个View,它包含一堆数据和状态,比如一个新闻列表.时刻表等.我是否可以再这个这个自定 ...
- iOS 自定义view里实现控制器的跳转
1.view里实现控制器的modal 拿到主窗口的根控制器,用根控制器进行modal需要的modal的控制器 场景:点击自定义view里的按钮实现控制器的modal UIViewController ...
- 使用Kotlin+RecyclerView+自定义View实现简易的通讯录字母导航功能
之前实习的时候了解到小组内突然有一个需求,就是要把所有的用户列表都像微信和QQ的通讯录一样,很好的展示在界面上,当时因为忙于其他事,一直记住这个需求,但是没想着具体实现,现在闲下来,想到这个需求,看了 ...
- kotllin自定义view_GitHub - wangshuaialex/Kotlin-CustomView: Kotlin实现的自定义View(仪表盘、饼状图、圆形头像)...
#自定义View-Kotlin版 本文的目的有两个: 大多数时候,自定义View并不会被用到,但一旦用到,通常都是很炫酷的效果.App的开发本身并不酷,让它们变酷的是设计师们的想象力与创造力.对于开发 ...
- android使用自定义,Android 自定义View的使用
在Android开发中,很多自带的View满足不了我们的要求,所有我们可以自定义View来达到自己想要的效果 自定义View其实很简单也很好学,话不多说现在开始. 第一步:我们需要新建一个JAVA类 ...
- Android初级教程初谈自定义view自定义属性
有些时候,自己要在布局文件中重复书写大量的代码来定义一个布局.这是最基本的使用,当然要掌握:但是有些场景都去对应的布局里面写对应的属性,就显得很无力.会发现,系统自带的控件无法满足我们的要求,这个时候 ...
- Autolayout_自定义View
Autolayout笔记:自定义View 如果你想在自定义View里用Autolayout进行布局的话,有下面几个点需要注意: 指定Intrinsic Content Size 区分frame和ali ...
- 自定义 View 之联系人字母索引及定位效果
博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此 博主:威威喵 | 博客主页:https://blog.csdn.net/ ...
- Android开发自定义View实现数字与图片无缝切换的2048
本博客地址:http://blog.csdn.net/talentclass_ctt/article/details/51952378 最近在学自定义View,无意中看到鸿洋大神以前写过的2048(附 ...
最新文章
- github 与git 使用 及配置
- 获取html元素的位置,如何获取页面元素的位置
- AndroidOrientation Sensor(方向传感器),新的替代方法详解(安卓官方提供)
- ssh免密码和ssh-copy-id命令
- [网络安全自学篇] 五十一.恶意样本分析之HGZ进程和网络行为动态分析及防御
- C#合并文件夹图片列表 自定义排版顺序
- 【Linux学习篇】This virtual machine is configured for 64-bit guest operating systems.……
- android studio 运行找不到类,android-studio – Android Studio找不到TestCase类
- 睿智的目标检测22——Keras搭建RFBnet目标检测平台
- php手机网页下载文件,php 手机下载 POST 类
- Linux 信号学习
- 如何使用Redis Data Reveal(rdr)查看redis中每个key占用的内存大小
- 数据全量、增量、比较更新
- java 流式FlowLayout布局换行方法
- 在线教育大数据营销平台实战(搭建篇)
- 华为:研发协同,为什么说效率是第一重要的事儿?
- B/S基于springMVC的网上选课系统(功能齐全javaweb)
- 【转】APT攻击检测与防御详解
- 办公套件新选择 WPS Office for Mac vs. 微软 Office 2019
- web前端学习--仿QQ空间留言板功能
热门文章
- coreldraw x5 选择工具快捷键_CorelDRAW设置和优化提高工作效率
- Element穿梭框Transfer与进度条组件绑定
- 关于php正则表达式得选择题,经典PHP笔试题
- 计算机国二笔试试题,全国计算机等考试二笔试试题(2).ppt
- 多用户用linux会很卡顿吗,新手学Linux系统,解决Linux系统卡顿的方法
- pcc定义_PCC介绍——智能管道
- python中logging模块详解_python logging日志模块详解
- python.集合转列表_python 列表,元组,字典,集合,字符串相互转换
- [通达信] 融维创富主图公式
- AI需要你帮忙 | 把两栖爬行动物框出来,提高AI识别准确率