使用Kotlin从零开始写一个现代Android 项目-Part1

// at the top of file

apply plugin: ‘kotlin-kapt’

android {

//other things that we already used

dataBinding.enabled = true

}

dependencies {

//other dependencies that we used

kapt “com.android.databinding:compiler:3.0.0-beta1”

}

请注意,数据绑定编译器的版本与项目build.gradle文件中的gradle版本相同:

classpath ‘com.android.tools.build:gradle:3.0.0-beta1’

然后,点击Sync Now,打开activity_main.xml,将Constraint Layout用layout标签包裹

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”>

<android.support.constraint.ConstraintLayout

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“me.mladenrakonjac.modernandroidapp.MainActivity”>

<TextView

android:id=“@+id/repository_name”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginEnd=“16dp”

android:layout_marginStart=“16dp”

android:textSize=“20sp”

app:layout_constraintBottom_toBottomOf=“parent”

app:layout_constraintHorizontal_bias=“0.0”

app:layout_constraintLeft_toLeftOf=“parent”

app:layout_constraintRight_toRightOf=“parent”

app:layout_constraintTop_toTopOf=“parent”

app:layout_constraintVertical_bias=“0.083”

tools:text=“Modern Android app” />

<TextView

android:id=“@+id/repository_has_issues”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginEnd=“16dp”

android:layout_marginStart=“16dp”

android:layout_marginTop=“8dp”

android:text=“@string/has_issues”

android:textStyle=“bold”

app:layout_constraintBottom_toBottomOf=“@+id/repository_name”

app:layout_constraintEnd_toEndOf=“parent”

app:layout_constraintHorizontal_bias=“1.0”

app:layout_constraintStart_toEndOf=“@+id/repository_name”

app:layout_constraintTop_toTopOf=“@+id/repository_name”

app:layout_constraintVertical_bias=“1.0” />

<TextView

android:id=“@+id/repository_owner”

android:layout_width=“0dp”

android:layout_height=“wrap_content”

android:layout_marginBottom=“8dp”

android:layout_marginEnd=“16dp”

android:layout_marginStart=“16dp”

android:layout_marginTop=“8dp”

app:layout_constraintBottom_toBottomOf=“parent”

app:layout_constraintEnd_toEndOf=“parent”

app:layout_constraintStart_toStartOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/repository_name”

app:layout_constraintVertical_bias=“0.0”

tools:text=“Mladen Rakonjac” />

<TextView

android:id=“@+id/number_of_starts”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginBottom=“8dp”

android:layout_marginEnd=“16dp”

android:layout_marginStart=“16dp”

android:layout_marginTop=“8dp”

app:layout_constraintBottom_toBottomOf=“parent”

app:layout_constraintEnd_toEndOf=“parent”

app:layout_constraintHorizontal_bias=“1”

app:layout_constraintStart_toStartOf=“parent”

app:layout_constraintTop_toBottomOf=“@+id/repository_owner”

app:layout_constraintVertical_bias=“0.0”

tools:text=“0 stars” />

</android.support.constraint.ConstraintLayout>

注意,你需要将所有的xml移动到layout 标签下面,然后点击Build图标或者使用快捷键Cmd + F9,我们需要构建项目来使Data Binding库为我们生成ActivityMainBinding类,后面在MainActivity中将用到它。

如果没有重新编译项目,你是看不到ActivityMainBinding的,因为它在编译时生成。

我们还没有完成绑定,我们只是定义了一个非空的 ActivityMainBinding 类型的变量。你会注意到我没有把? 放在 ActivityMainBinding 的后面,而且也没有初始化它。这怎么可能呢?lateinit 关键字允许我们使用非空的延迟被初始化的变量。和 ButterKnife 类似,在我们的布局准备完成后,初始化绑定需要在 onCreate 方法中进行。此外,你不应该在 onCreate 方法中声明绑定,因为你很有可能在 onCreate 方法外使用它。我们的 binding 不能为空,所以这就是我们使用 lateinit 的原因。使用 lateinit 修饰,我们不需要在每次访问它的时候检查 binding 变量是否为空。

我们初始化binding变量,你需要替换:

setContentView(R.layout.activity_main)

为:

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

就是这样,你成功的绑定了所有View,现在你可以访问它并且做一些更改,例如,我们将仓库名字改为Modern Android Medium Article:

binding.repositoryName.text = “Modern Android Medium Article”

如你所见,现在我们可以通过bingding变量来访问main_activity.xml的所有View了(前提是它们有id),这就是Data Binding 比ButterKnife 好用的原因。

kotlin的 Getters 和 setters

大概,你已经注意到了,我们没有像Java那样使用.setText(),我想在这里暂停一下,以说明与Java相比,Kotlin中的getter和setter方法如何工作的。

首先,你需要知道,我们为什么要使用getters和setters,我们用它来隐藏类中的变量,仅允许使用方法来访问这些变量,这样我们就可以向用户隐藏类中的细节,并禁止用户直接修改我们的类。假设我们用 Java 写了一个 Square 类:

public class Square {

private int a;

Square(){

a = 1;

}

public void setA(int a){

this.a = Math.abs(a);

}

public int getA(){

return this.a;

}

}

使用setA()方法,我们禁止了用户向Square类的a变量设置一个负数,因为正方形的边长一定是正数,要使用这种方法,我们必须将其设为私有,因此不能直接设置它。这也意味着我们不能直接获得a,需要给它定一个get方法来返回a,如果有10个变量,那么我们就得定义10个相似的get方法,写这样无聊的样板代码,通常会影响我们的心情。

Kotling使我们的开发人员更轻松了。如果你调用下面的代码:

var side: Int = square.a

这并不意味着你是在直接访问a变量,它和Java中调用getA()是相同的

int side = square.getA();

因为Kotlin自动生成默认的getter和setter。在Kotlin中,只有当您有特殊的setter或getter时,才应指定它。否则,Kotlin会为您自动生成:

var a = 1

set(value) { field = Math.abs(value) }

field ? 这又是个什么东西?为了更清楚明白,请看下面代码:

var a = 1

set(value) { a = Math.abs(value) }

这表明你在调用set方法中的set(value){},因为Kotlin的世界中,没有直接访问属性,这就会造成无限递归,当你调用a = something,会自动调用set方法。使用filed就能避免无限递归,我希望这能让你明白为什么要用filed关键字,并且了解getters和setters是如何工作的。

回到代码中继续,我将向你介绍Kotlin语言的另一个重要功能:apply函数:

class MainActivity : AppCompatActivity() {

lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

binding.apply {

repositoryName.text = “Medium Android Repository Article”

repositoryOwner.text = “Mladen Rakonjac”

numberOfStarts.text = “1000 stars”

}

}

}

apply 允许你在一个实例上调用多个方法,我们仍然还没有完成数据绑定,还有更棒的事儿,让我们为仓库定义一个UI模型(这个是github仓库的数据模型Repository,它持有要展示的数据,请不要和Repository模式的中的Repository搞混淆了哈),要创建一个Kotlin class,点击New -> Kotlin File/Class :

class Repository(var repositoryName: String?,var repositoryOwner: String?,var numberOfStars: Int? ,var hasIssues: Boolean = false)

在Kotlin中,主构造函数是类头的一部分,如果你不想定义次构造函数,那就是这样了,数据类到此就完成了,构造函数没有参数分配给字段,没有setters和getters,整个类就一行代码。

回到MainActivity.kt,为Repository创建一个实例。

var repository = Repository(“Medium Android Repository Article”,

“Mladen Rakonjac”, 1000, true)

你应该注意到了,创建类实例,没有用new

现在,我们在activity_main.xml中添加data标签。

<variable

name=“repository”

type=“me.mladenrakonjac.modernandroidapp.uimodels.Repository”

/>

我们可以在布局中访问存储的变量repository,例如,我们可以如下使用id是repository_name的TextView,如下:

`android:text=“@{repository.repositoryName}”`

repository_name文本视图将显示从repository变量的属性repositoryName获取的文本。剩下的唯一事情就是将repository变量从xml绑定到MainActivity.kt中的repository。

点击Build使DataBinding 为我们生成类,然后在MainActivity中添加两行代码:

binding.repository = repository

binding.executePendingBindings()

如果你运行APP,你会看到TextView上显示的是:“Medium Android Repository Article”,非常棒的功能,是吧?

但是,如果我们像下面这样改一下呢?

Handler().postDelayed({repository.repositoryName=“New Name”}, 2000)

新的文本将会在2000ms后显示吗?不会的,你必须重新设置一次repository,像这样:

Handler().postDelayed({repository.repositoryName=“New Name”

binding.repository = repository

binding.executePendingBindings()}, 2000)

但是,如果我们每次更改一个属性都要这么写的话,那就非常蛋疼了,这里有一个更好的方案叫做Property Observer

让我们首先解释一下什么是观察者模式,因为在rxJava部分中我们也将需要它:

可能你已经听说过http://androidweekly.net/,这是一个关于Android开发的周刊。如果您想接收它,则必须订阅它并提供您的电子邮件地址。过了一段时间,如果你不想看了,你可以去网站上取消订阅。

这就是一个观察者/被观察者的模式,在这个例子中, Android 周刊是被观察者,它每周都会发布新闻通讯。读者是观察者,因为他们订阅了它,一旦订阅就会收到数据,如果不想读了,则可以停止订阅。

Property Observer在这个例子中就是 xml layout,它将会监听Repository实例的变化。因此,Repository被观察者,例如,一旦在Repository类的实例中更改了repository nane 属性后,xml不调用下面的代码也会更新:

binding.repository = repository

binding.executePendingBindings()

如何让它使用Data Binding 库呢?,Data Binding库提供了一个BaseObservable类,我们的Repostory类必须继承它。

class Repository(repositoryName : String, var repositoryOwner: String?, var numberOfStars: Int?

, var hasIssues: Boolean = false) : BaseObservable(){

@get:Bindable

var repositoryName : String = “”

set(value) {

field = value

notifyPropertyChanged(BR.repositoryName)

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-9ZKDgGpH-1710739739818)]
[外链图片转存中…(img-bxTzBImH-1710739739819)]
[外链图片转存中…(img-lwOhP5BH-1710739739820)]
[外链图片转存中…(img-Zmq0icP2-1710739739820)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-cP8PT2Vv-1710739739821)]

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-NOgHfkIP-1710739739822)]
本文在开源项目:【GitHub 】中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值