Android自定义View-BitmapShader实现无规则图形

一、背景

这个特效来源于有一天逛网站的时候,一家网站实现了这样的一个效果:在你向下滚动页面的时候,他的背景的图片随着你的滑动,以窗口的方式展示着图片的某一部分。浏览完就想着以Android的方式能不能实现,也就演变成了以下的效果。

二、知识点BitmapShader

【官方】Shader用于绘制位图作为纹理。位图可以设置模式为重复或镜像或平铺。

简单来说,Shader可以用来实现一些渐变,反转,镜像,重复等效果。本文的效果核心使用的是Shader中的BitmapShader——位图Shader。

BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

从构造方法可以看出,第一个参数为位图bitmap,第二个和第三个参数分别为横方向和纵方向的平铺模式。
平铺模式有如下几种模式:

  • CLAMP :拉伸,水平拉伸图片左右方向最后一个像素,,垂直拉伸图片上下方向的最后一个像素。
  • MIRROR:镜像,水平和垂直方向重复图像,交替镜像,使相邻的图像始终相连。
  • REPEAT:重复,水平和垂直方向不断重复图片。

    三、基本思路

    1、创建一张图片,将画笔设置为带有图像填充功能;
    2、根据获取手指所点击位置的坐标,根据获得的坐标绘制圆形或者其他图形。

    四、实现

    从上述基本思路来看,步骤还是很简单,那么接下来分析实现的代码。
    1
    2
    mPaint = new Paint();
    mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.photo2);

首先就是初始化画笔和创建一张位图。接下来为核心内容。

1
2
mPaint.setShader(new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
canvas.drawCircle(400,400,300,mPaint);

BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)新创建一个BitmapShader,并将水平和垂直方向的平铺模式设置为CLAMP拉伸模式,将BitmapShader属性设置给画笔,使画笔具有图像填充功能,这是后面画笔可以绘制出一个带有图片的圆形的原因。接着利用Canvas在坐标(400,400)处绘制出一个半径为300的圆形。
目前基本的绘制已经结束,运行代码可以看出会有圆形图片在界面显示,如下图:

从上面的图片来看,是不是发现其实这就是一个圆形的头像,到这里我们可以想到前面有一篇Android自定义菱形图片使用PorterDuffXfermode的相关属性绘制菱形的头像,其实这里我们也可以使用BitmapShader进行操作,代码相比较更加简洁。
目前实现的是静态的图像,接下来实现随着手指动态移动的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private float mTouchEventY = -1;
private float mTouchEventX = -1;
......

@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchEventX = event.getX();
mTouchEventY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
mTouchEventX = event.getX();
mTouchEventY = event.getY();
postInvalidate();
break;
}
postInvalidate();
return super.onTouchEvent(event);
}

首先我们创建两个全局变量mTouchEventX,mTouchEventY。在onTouchEvent事件中,手指MotionEvent.ACTION_DOWN的时候,动态获取手指的位置,在ACTION_DOWN的代码中记得return true,这涉及到事件的分发机制,当return false时,代表手指的down事件未完成,那么事件将不会继续向下传递,也就是说ACTION_DOWN动作后的ACTION_MOVE是不会触发的。
同样的,我们在MotionEvent.ACTION_MOVE中也动态获取手指的位置,也就是随着手指动态调整圆形的位置。
最后在Canvas绘制圆形的时候,将获取到的位置设置进去,随着手指的不断移动,不断获取到位置值,也就实现了动态绘制图像的效果。

1
canvas.drawCircle(mTouchEventX,mTouchEventY,300,mPaint);

五、最后

在上述的过程中,发现BitmapShader可以很容易的实现不规则头像。另外,上述也只使用了BitmapShader的拉伸模式,还有镜像和重复模式可以实现更多的特效,有待发掘。

项目地址:https://github.com/fuusy/ShaderTelescope.git