我主要是在通过实现自定义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: SpeedLinearLayoutManger
private lateinit var mAdapter: BarChartAdapter
private var mClickListener: ItemOnClickListener? = null
private 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 = context
init { }
你可以在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:
a
n
k
o
v
e
r
s
i
o
n
"
c
o
m
p
i
l
e
"
o
r
g
.
j
e
t
b
r
a
i
n
s
.
a
n
k
o
:
a
n
k
o
−
r
e
c
y
c
l
e
r
v
i
e
w
−
v
7
−
c
o
r
o
u
t
i
n
e
s
:
anko_version" compile "org.jetbrains.anko:anko-recyclerview-v7-coroutines:
ankoversion"compile"org.jetbrains.anko:anko−recyclerview−v7−coroutines:anko_version”
compile “org.jetbrains.anko:anko-sdk25:
a
n
k
o
v
e
r
s
i
o
n
"
c
o
m
p
i
l
e
"
o
r
g
.
j
e
t
b
r
a
i
n
s
.
a
n
k
o
:
a
n
k
o
−
a
p
p
c
o
m
p
a
t
−
v
7
:
anko_version" compile "org.jetbrains.anko:anko-appcompat-v7:
ankoversion"compile"org.jetbrains.anko:anko−appcompat−v7:anko_version”
// Coroutine listeners for Anko Layouts
compile “org.jetbrains.anko:anko-sdk25-coroutines:
a
n
k
o
v
e
r
s
i
o
n
"
c
o
m
p
i
l
e
"
o
r
g
.
j
e
t
b
r
a
i
n
s
.
a
n
k
o
:
a
n
k
o
−
a
p
p
c
o
m
p
a
t
−
v
7
−
c
o
r
o
u
t
i
n
e
s
:
anko_version" compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:
ankoversion"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.BOTTOM
bottomMargin = 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 0x19
public final static color(Landroid/content/Context;I)I
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC “$receiver”
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 12 L1
ALOAD 0
ILOAD 1
INVOKESTATIC android/support/v4/content/ContextCompat.getColor (Landroid/content/Context;I)I
IRETURN
L2
LOCALVARIABLE $receiver Landroid/content/Context; L0 L2 0
LOCALVARIABLE colorRes I L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x19
public final static color(Landroid/view/View;I)I
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC “$receiver”
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 14 L1
ALOAD 0
INVOKEVIRTUAL android/view/View.getContext ()Landroid/content/Context;
DUP
LDC “context”
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkExpressionValueIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
ILOAD 1
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
架构师筑基包括哪些内容
我花了将近半个月时间将:深入 Java 泛型.、注解深入浅出、并发编程.、数据传输与序列化、Java 虚拟机原理、反射与类加载、高效 IO、Kotlin项目实战等等Android架构师筑基必备技能整合成了一套系统知识笔记PDF,相信看完这份文档,你将会对这些Android架构师筑基必备技能有着更深入、更系统的理解。
由于文档内容过多,为了避免影响到大家的阅读体验,在此只以截图展示部分内容
注:资料与上面思维导图一起看会更容易学习哦!每个点每个细节分支,都有对应的目录内容与知识点!
这份资料就包含了所有Android初级架构师所需的所有知识!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
注:资料与上面思维导图一起看会更容易学习哦!每个点每个细节分支,都有对应的目录内容与知识点!
[外链图片转存中…(img-Z10Bk3vs-1713523862958)]
[外链图片转存中…(img-7FdYuBzH-1713523862959)]
这份资料就包含了所有Android初级架构师所需的所有知识!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!