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.3 初识网格扭曲原理
现在,我们看一下网格扭曲需要的参数
public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight,
float[] verts, int vertOffset, int[] colors, int colorOffset,
Paint paint)
这里重点介绍meshWidth
、meshHeight
、verts
三个参数。
- meshWidth:网格的宽,这里指的是这个Bitmap横向被分割成多少份
- meshHeight:网格的高,这里指的是这个Bitmap被纵向分割成多少份
- verts:这是一个数组,里面存放的是需要显示的网格的坐标(后面详细介绍)
上述图片表示的是meshWidth=4;meshHeight=9,也就是说这张图片被划分成了36份,那么,verts里存放的又是什么?请看下图
这里所有红色圈圈住的(只标注了几个,其他的没有画,主要是怕密恐狗投诉),从这里看出verts里存放的是每一个分割线焦点的坐标,包括屏幕边缘。所以verts数组的大小为二倍的网格宽加一*网格高加一
当我们调用扭曲方法时,其会从verts中依次取出各个坐标值,与原始坐标值比对,假如有原始坐标值为(10,10),但是verts中对应位置(比如数组中的第10,11位)的坐标值为(20,20),那么其就会通过一定的方法将(20,20)坐标附近的像素扭曲到(10,10)坐标附近。如下图所示(画图略丑,轻吐槽)
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