本篇我们将通过另一个案例–刮刮乐 的实现来加深我们上节讲述的位图运算的理解。
我们先来分析下刮刮乐的实现思路:从技术实现上来说,刮刮乐有两个图层,一个是不会变化的中奖信息图层,一个是蒙上了一层灰色的图层。当用户手指在屏幕上涂抹时,需要将灰色抹掉,露出下面的中奖信息。
所以对于中奖信息来说,应该采用更简单的实现,可以在在图片上写上中奖信息后作为view的背景,当手指在屏幕上涂抹时就不需要考虑中奖信息的重绘了。然后在创建一个Bitmap对象,开始时是灰色的,手指在屏幕上移动时绘制线条,将线条与灰色做 Mode.CLEAR运算,相交的部分即被清除,变为透明效果,于是我们就能看到背景了。
以上过程总结为如下2个步骤
1,绘制背景
背景是一张图片,上面写上了中奖信息文字的。我们从本地资源中加载一张图片,是不能在向这张图片中添加信息的(没有设置可编辑标识),如果强制编辑,会报
java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor异常。
所以需要copy一份背景图片的bitmap,然后添加中奖文字后,调用view的public方法 setBackground将其设置为view的背景。
2,绘制遮挡中奖信息的蒙版,并处理手指移动逻辑
定义一个Bitmap,初始画上一层灰色,当手指在屏幕上移动时,绘制曲线,将曲线和灰色做Mode.CLEAR运算,实现清除的效果。
//1.绘制背景
private void drawBackground(){
//1.1 加载背景图片
Bitmap bgBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.background);
//1.2 copy背景图片并添加 Mutable为true标识
Bitmap bgBmpCopy = bgBmp.copy(Bitmap.Config.ARGB_8888,true);
Canvas bgCanvas = new Canvas(bgBmpCopy);
//1.3 获取随机的中奖信息
String text = PRIZE[random.nextInt(PRIZE.length)];
//1.4 获取中奖信息文字的宽度和高度
Rect rect = new Rect();
paint.getTextBounds(text,0,text.length(),rect);
//1.5 计算 中奖文字 绘制的开始位置(在背景图片的正中间)
int x = (bgBmpCopy.getWidth() - rect.width()) / 2;
int y = (bgBmpCopy.getHeight() - rect.height()) / 2 ;
//1.6 将 中奖文字 绘制到背景的图层上
bgCanvas.drawText(text,Math.max(x,0),Math.max(y,0),paint);
//1.7 将处理好的背景图 设置view的Background
setBackground(new BitmapDrawable(getResources(),bgBmpCopy));
}
private static final String PRIZE[] = {
"恭喜,你中了一等奖,奖金 1 亿元",
"恭喜,你中了二等奖,奖金 5000 万",
"恭喜,你中了三等奖,奖金 100 元",
"很遗憾,你没有中奖,继续加油哦"
};
//2.绘制灰色蒙版遮挡view背景上的中奖信息
private Bitmap bmpBuffer;
private Canvas bufferCanvas;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bmpBuffer, 0,0,null);//将灰色的bitmao绘制到view的canvas上
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bmpBuffer = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
bufferCanvas = new Canvas(bmpBuffer);
bufferCanvas.drawColor(Color.parseColor("#ff808080"));//灰色Bitmap
}
经过上述操作,view加载起来是如下效果:这块灰色区域就是我们自定义的view
下面就来处理在view上滑动绘制曲线,将灰色的蒙版清除,渐渐露出后面的中奖信息
//关键是要在view的构造方法中构造一个clearpaint对象,并设置该画笔的模式是Mode.CLEAR
private Paint paint, clearPaint;
private final int FINGER = 50;
public GugGuaLeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
random = new Random();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(50);
paint.setColor(Color.RED);
clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
clearPaint.setStrokeJoin(Paint.Join.ROUND);
clearPaint.setStrokeCap(Paint.Cap.ROUND);
clearPaint.setStrokeWidth(FINGER);
drawBackground();
}
//然后在view的onTouchevent中处理手指滑动绘制曲线的逻辑
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action){
case MotionEvent.ACTION_DOWN:
curtX = x;
curtY = y;
break;
case MotionEvent.ACTION_MOVE:
bufferCanvas.drawLine(curtX,curtY,x, y, clearPaint);//绘制手指滑动的路径,使用cleanPaint
invalidate();
curtX = x ;
curtY = y;
break;
case MotionEvent.ACTION_UP:
invalidate();
break;
}
return true;
}
现在我们随意在view上滑动,效果如下:可以看到我们view背景上的中奖信息随着手指滑动慢慢露出来了。
DONE
此系列后续持续更新。