朱涛 · Kotlin 编程第一课
朱涛
Google 认证的 Kotlin、Android 开发者专家,博客“Kotlin Jetpack 实战”作者
6717 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 50 讲
朱涛 · Kotlin 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

题目解答 | 期中考试版本参考实现

你好,我是朱涛。上节课我给你布置了一份考试题,你完成得怎么样了呢?这节课呢,我会来告诉你我是如何用 Kotlin 来做这个图片处理程序的,供你参考。
由于上节课我们已经做好了前期准备,所以这里我们直接写代码就行了。

1.0 版本

对于图片反转和裁切的这个问题,如果一开始你就去想象一个大图片,里面有几万个像素点,那你可能会被吓到。但是,如果你将数据规模缩小,再来分析的话,你会发现这个问题其实很简单。
这里,我们就以一张 4X4 像素的照片为例来分析一下。
这其实就相当于一个抽象的模型,如果我们基于这张 4X4 的照片,继续分析翻转和裁切,就会容易很多。我们可以来画一个简单的图形:
上面这张图,从左到右分别是原图、横向翻转、纵向翻转、裁切。其中,翻转看起来是要复杂一些,而裁切是最简单的。
我们先来处理裁切。对于裁切,其实只需要将图片当中某个部分的像素拷贝到内存,然后存储成为一张新图片就行了。
/**
* 图片裁切
*/
fun Image.crop(startY: Int, startX: Int, width: Int, height: Int): Image {
val pixels = Array(height) { y ->
Array(width) { x ->
getPixel(startY + y, startX + x)
}
}
return Image(pixels)
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

这篇文章介绍了作者使用Kotlin编写图片处理程序的过程,分为1.0版本和2.0版本两部分。在1.0版本中,作者通过简单的示例解释了图片裁切和翻转的实现原理,并展示了相应的Kotlin代码。此外,作者还介绍了如何编写针对图像算法的单元测试,以及如何使用单元测试来验证代码的正确性。在2.0版本中,作者扩展了程序的功能,实现了从网络下载图片并保存的功能,并使用Kotlin协程来实现挂起函数。文章还总结了Kotlin语言的特点和协程的使用方法,并提出了一个思考题,引发读者对于挂起函数内部线程切换方式的思考。整体而言,本文通过实际代码示例和技术讨论,全面展示了Kotlin语言和协程在图片处理程序中的应用,适合读者快速了解Kotlin语言和协程的特点及应用场景。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《朱涛 · Kotlin 编程第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(10)

  • 最新
  • 精选
  • Allen
    思考题中关于调度器的使用不太好,一般的使用方式是通过暴露参数的方式让使用者来传。 通过传参的方式有两个好处: 1. 增加了代码的灵活性和可用性。 2. 有利于单元测试。

    作者回复: 嗯,各有利弊。

    2022-02-28
    4
  • 白乾涛
    class Image(private val pixels: Array<Array<Color>>) { val height: Int = pixels.size val width: Int = pixels[0].size private fun getPixel(y: Int, x: Int): Color = pixels[y][x] fun flipHorizontal() = changeImage { y, x -> getPixel(y, width - 1 - x) } //横向翻转图片 fun flipVertical() = changeImage { y, x -> getPixel(height - 1 - y, x) } //纵向翻转图片 fun crop(startY: Int, startX: Int, width: Int, height: Int): Image = toImage(height, width) { y, x -> getPixel(startY + y, startX + x) } //图片裁切 } fun Image.changeImage(init: (Int, Int) -> Color): Image = toImage(height, width, init) fun loadImage(imageFile: File): Image = ImageIO.read(imageFile) .let { toImage(it.height, it.width) { x, y -> Color(it.getRGB(x, y)) } } fun toImage(height: Int, width: Int, init: (Int, Int) -> Color): Image = Array(height) { y -> // 创建一个 Array,元素类型也是 Array Array(width) { x -> // 创建一个 Array,元素类型是 Color init(x, y) // 每个元素都是在 init() 中初始化的 } }.let { Image(it) }

    作者回复: 这代码改进的很不错,赞!

    2022-03-06
    2
  • PoPlus
    暂时没想到 context 动态传入有什么好处,按理说下载图片只需要 IO 就行了,希望老师能解惑。

    作者回复: 其实,这个问题没有标准答案。我只是提出这个问题,希望大家思考这中间的差异。因为,有的时候,更多的灵活性也并不意味着是好事。 动态传入的方式,有利有弊。利:更灵活;弊,调用方可能传错Context。 总的来说,还是要结合使用场景来分析。有些场景下,我们更加重视灵活性,就选择动态传入,有的场景下,我们都希望尽可能的不出问题。

    2022-03-01
    2
  • Allen
    这个下载图片的实现和我们普通开线程下载的主要区别就是使用同步的写法(不需要写 callback)来实现了异步的操作。

    作者回复: 是的。

    2022-02-28
    2
  • Geek_Adr
    灵活会带来API难用,增加犯错的概率,但又不能把灵活丢掉 兼得的方法:先给一套默认最佳实现,满足大多数情况,使用者清晰知道默认实现不满足时再给灵活的方案 我认为可配有默认实现的方式更好

    作者回复: 很好的思路。

    2022-03-12
  • 曾帅
    这个问题 或许 可以根据项目来,有些项目可能有自己的 线程池 或者一些 生命周期 的要求,就可以根据业务需求进行传参进行处理。如果没有这些需求的话,直接用 IO 也是可以的。

    作者回复: 是的,要结合具体的场景来分析。

    2022-03-02
  • L先生
    我觉得会好点,给调用者选择的权利,并且赋予默认值,方便单元测试

    作者回复: 嗯,后者的灵活性更强。

    2022-03-01
  • 24隋心所欲
    使用 withContext() 的主意作用就是切换线程吗?
    2022-10-29归属地:河北
  • 郑峰
    InputStream应该也需要正常close吧?例如 body.byteStream().use { outputFile.outputStream().use { fileOut -> it.copyTo(fileOut) } }
    2022-08-19归属地:美国
    1
  • Link
    有个疑惑,类似 fun Image.writeToFile(),这样的扩展多了话会不会导致其他地方在使用Image类的时候产生疑惑(过度扩展?),但函数式风格的编程似乎就必须写一堆扩展方法?望老师解惑
    2022-06-09
收起评论
显示
设置
留言
10
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部