在看代码之前,我们先来了解下PorterDuffXfermode。
PorterDuffXfermode
PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst是先画的图形,而src是后画的图形。
当然,这些模式也不是经常使用的,用的最多的是,使用一张图片作为另一张图片的遮罩层,通过控制遮罩层的图形,来控制下面被遮罩图形的显示效果。其中最常用的就是通过DST_IN、SRC_IN模式来实现。
我们的刮刮卡也是通过这个原理实现的。在初始状态下,上面的图层会将下面整个图层覆盖,当你用手刮上面的图层的时候,下面的图层会慢慢显示出来,这也类似很多画图工具中的橡皮擦效果。
下面具体来分析实现原理。
首先需要做一些初始化工作,例如背景图、遮罩图和我们画图的Paint的一些属性,代码如下:
mPaint = new Paint();
mPaint.setAlpha(0);
//设置混合模式为DST_IN
mPaint.setXfermode(
new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//设置画笔,让笔触和连接处更加圆滑
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPath = new Path();
//设置背景图
mBgBitmap = BitmapFactory.decodeResource(getResources(),
R.mipmap.beauty);
//设置遮罩图
mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(),
mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mFgBitmap);
mCanvas.drawColor(Color.GRAY);
在上面的代码中,给Paint设置一些属性,让他的笔触和连接处更加圆滑一点,即Paint.Join.ROUND和Paint.Cap.ROUND属性。
将Paint的透明度设为0,这样才能显示出橡皮擦的效果,这是因为在使用PorterDuffXfermode进行图层混合时,并不是简单的只进行图层的计算,同事也会去计算透明通道的值。
接下来,看一下如何获取用户手指滑动所产生的路径。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
break;
}
mCanvas.drawPath(mPath, mPaint);
invalidate();
return true;
}
好了,到这里代码估计就清晰了。首先初始化了一张背景图和一张灰色的图,并且混合模式为DST_IN,在滑动的时候,上层的灰色变透明,下层的和上层的合并,就出现了类似的效果。
最后看一下全部代码:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.example.cntvuser.myapplication.R;
public class XfermodeView extends View {
private Bitmap mBgBitmap, mFgBitmap;
private Paint mPaint;
private Canvas mCanvas;
private Path mPath;
public XfermodeView(Context context) {
super(context);
init();
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public XfermodeView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAlpha(0);
//设置混合模式为DST_IN
mPaint.setXfermode(
new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//设置画笔,让笔触和连接处更加圆滑
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPath = new Path();
//设置背景图
mBgBitmap = BitmapFactory.decodeResource(getResources(),
R.mipmap.beauty);
//设置遮罩图
mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(),
mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mFgBitmap);
mCanvas.drawColor(Color.GRAY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
break;
}
mCanvas.drawPath(mPath, mPaint);
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
//画背景
canvas.drawBitmap(mBgBitmap, 0, 0, null);
//画灰色
canvas.drawBitmap(mFgBitmap, 0, 0, null);
}
}