查看原文
其他

通过自定义View打造一个海底世界

AndroidPub 2022-05-15

The following article is from 安安安安卓 Author 安安安安卓

项目地址:https://github.com/ananananzhuo-blog/UnderSeaView





绘制背景

首先大海应该是蓝色的吧,而且应该是一个渐变色,那么根据 paint 篇的内容,我们便可以使用 paint.setShader 来实现,实现思路就是使用 Canvas.drawRect 方法绘制整个画布,画笔 Paint 设置一个 Shader,代码如下:

paintBack.shader = LinearGradient(
            measuredWidth / 3f,
            0f,
            measuredWidth * 2 / 3f,
            measuredHeight.toFloat(),
            Color.WHITE,
            Color.parseColor("#000055"),
            Shader.TileMode.CLAMP
        )
canvas.drawRect(background, paintBack)

效果大概这个样子

绘制鲸鱼

咳咳,鲸鱼这个东西我在百度上面找了一张图片

而后使用 ps 抠出了其中最漂亮的大蓝鲸,并且使用 ps 液化工具对蓝鲸的尾巴进行了处理,目的就是让蓝鲸在运动的过程中尾巴不停的摆动从而实现游泳的效果

代码:

private val bitmaps = listOf<Bitmap>(
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish1),
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish2),
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish3),
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish4),
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish3),
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish2),
        BitmapFactory.decodeResource(context.resources, R.drawable.bluefish1),
    )
 canvas.run {
            drawBitmap(bitmaps[bitmapIndex % bitmaps.size], 100f100f, paint)
            bitmapIndex++
            drawFishWithPath()
        }

实现效果:

虎鲸光能摆动尾巴还是不够的,我们还应有让他能做游泳的动作,最好能按照我们指定的路径来运动是最好的了

定义路径的核心代码

 fishPath.moveTo(100f100f)
        for (i in 0..20) {
            fishPath.apply {
                cubicTo(randowX(), randowY(), randowX(), randowY(), randowX(), randowY())
            }
        }
        fishPath.close()

画出路径效果

这里是有问题的,因为路径转向的时候有很多锐角,可想而知这样是不行的,虎鲸宝宝也不会这么游泳的对吧,这个后续再优化,目前重要的是实现效果

让鱼沿着路径动起来

这里要借助PathMeasure工具了

dstPath.reset()
        var stop = start+100f
        pathMeasure.getSegment(start, stop, dstPath, true)
        val matrix =  Matrix()
        pathMeasure.getMatrix(stop, matrix, (PathMeasure.POSITION_MATRIX_FLAG.or(PathMeasure.TANGENT_MATRIX_FLAG)))
        val bitmap = bitmaps[bitmapIndex % bitmaps.size]
        matrix.preTranslate(-bitmap.width / 2f, -bitmap.height / 2f)
        canvas.drawBitmap(bitmap, matrix, paint)
        

代码是通过pathMeasure.getSegment方法可以截取path,从而在path的某个位置绘制鲸鱼图片,因为能拿到path某个点的正切值,所以我们的鲸鱼可以始终做到沿着切线游泳

游泳效果:

最终效果图如下:

画太阳和气泡

既然最终效果已经出来了,那么后面直接放画太阳和气泡的代码吧

画太阳

画太阳主要的点在于画周围的阳光。画阳光的时候我们将太阳弧度20等分,然后从两个等分点之间向外画三角就能实现我们的阳光效果了。

path.moveTo(radius + sunX, sunY)
        val degree = 3.14f * 2 / leafNum
        for (i in 1..leafNum) {
            val x1 = radius * cos(i * degree) + sunX
            val y1 = radius * sin(i * degree) + sunY

            val halfDegree = (i - 0.5) * degree
            val shineRadius = radius + Random.nextInt(50)
            val controllX = shineRadius * cos(halfDegree).toFloat() + sunX
            val controllY =  shineRadius * sin(halfDegree).toFloat() + sunY
            path.lineTo(controllX, controllY)
            path.lineTo(x1, y1)
        }
        path.close()

画气泡

我们使用RadiaGradient镜像渐变的方式实现气泡效果,要注意的点是镜像渐变的颜色中间点不要选在圆心,否则会很难看

paint.shader=RadialGradient(cycleX+40,cycleY-40,radius+300, Color.WHITE,Color.GREEN,Shader.TileMode.CLAMP)
        canvas.drawCircle(cycleX, cycleY, radius, paint)




~ FIN ~



推荐阅读


面试官:你了解 LiveData 的 postValue 吗?


Kotlin Flow 场景化学习


Kotlin 1.5 新特性:密封接口有啥用?


Compose 架构如何选?MVP & MVVM & MVI




加我好友拉你进技术交流群,每天干货聊不停~


↓关注公众号↓↓添加微信交流↓



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存