Android:使用drawBitmapMesh方法产生水波(一)

本文详细介绍了如何在Android中利用drawBitmapMesh方法创建水波效果,从理解网格扭曲原理到实现波浪的绘制和优化,逐步展示了一个动态波纹的完整过程。内容包括创建View、显示图片、计算偏移量、动态调整半径等关键步骤。
摘要由CSDN通过智能技术生成

Android:使用drawBitmapMesh方法产生水波(一)

标签(空格分隔): kotlin android
作者:陈小默


前言:这篇博客并没有什么好看的,众爱卿都退下吧


一、认识Canvas.drawBitmapMesh

Mesh的含义是“网格”,也就是说它将整个Bitmap分成若干个网格,再对每一个网格进行相应的扭曲处理。至于其具体是怎么运作的,我们边做边说。by:陈小默

1.1 创建一个View

class RippleView : View {
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}

当View创建完成后,我们将其添加到布局文件中

    <com.cccxm.ripple.RippleView
        android:id="@+id/mRippleView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

目前为止我们只是创建了一个空的View,什么也没有做,下一步,我们让他能显示图片

1.2 显示图片

接下来,我们给View设置一个类型为Bitmap的属性,并添加一个Set方法

var background: Bitmap? = null
        set(value) {
            field = value
            invalidate()
        }

当然,如果我们想显示这个图片的话,就必须重写onDraw()方法

    private val paint = Paint()

    override fun onDraw(canvas: Canvas) {
        background?:return
        canvas.drawBitmap(background,0F,0F,paint)
    }

接下来我们在MainActivity的onCreate方法给View设置图片

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mRippleView.background = BitmapFactory.decodeResource(resources, R.drawable.bac)
    }

1.2 运行效果

1.3 初识网格扭曲原理

现在,我们看一下网格扭曲需要的参数

public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight,
            float[] verts, int vertOffset, int[] colors, int colorOffset,
            Paint paint) 

这里重点介绍meshWidthmeshHeightverts三个参数。

  • meshWidth:网格的宽,这里指的是这个Bitmap横向被分割成多少份
  • meshHeight:网格的高,这里指的是这个Bitmap被纵向分割成多少份
  • verts:这是一个数组,里面存放的是需要显示的网格的坐标(后面详细介绍)

1.3-1
上述图片表示的是meshWidth=4;meshHeight=9,也就是说这张图片被划分成了36份,那么,verts里存放的又是什么?请看下图
1.3-2
这里所有红色圈圈住的(只标注了几个,其他的没有画,主要是怕密恐狗投诉),从这里看出verts里存放的是每一个分割线焦点的坐标,包括屏幕边缘。所以verts数组的大小为二倍的网格宽加一*网格高加一

2(meshWidth+1)(meshHeight+1)
为什么要乘2?因为坐标是以(x,y)形式成对存在的。
当我们调用扭曲方法时,其会从verts中依次取出各个坐标值,与原始坐标值比对,假如有原始坐标值为(10,10),但是verts中对应位置(比如数组中的第10,11位)的坐标值为(20,20),那么其就会通过一定的方法将(20,20)坐标附近的像素扭曲到(10,10)坐标附近。如下图所示(画图略丑,轻吐槽)

1.3-3
1.3-4

1.4 实践扭曲效果

通过1.3 节的讲述,应该已经知道了扭曲的基本原理,接下来我们通过一个简单的小实验来看一下扭曲的效果
首先,声明我们需要将图片分割为横30格,竖30格,和我们存储坐标的数组

    private val WIDTH = 30
    private val HEIGHT = 30
    private val COUNT = (WIDTH + 1) * (HEIGHT + 1)
    private val verts = FloatArray(COUNT * 2)
    private val orig = FloatArray(COUNT * 2)
解释一下orig的作用,在这里我们声明了一个和verts一样的数组,里面存储的是图片原始的焦点与坐标对应的关系,如果没有这个数组当我们修改verts造成扭曲效果之后就无法复原了。

接下来,我们在设置图片的set方法中给数组赋值

var background: Bitmap? = null
        set(value) {
            field = value
            invalidate()
            val bitmapWidth = field!!.width.toFloat()
            val bitmapHeight = field!!.height.toFloat()
            var index = 0
            for (y in 0..HEIGHT) {
                val fy = bitmapHeight * y / HEIGHT
                for (x in 0..WIDTH) {
                    val fx = bitmapWidth * x / WIDTH
                    verts[index * 2 + 0] = fx
       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值