一、说在前面的话
我们在做新手引导时,经常会遇到凸显某一块功能时需求,类似于下图:
![](https://img-blog.csdnimg.cn/20201201163150223.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzYxODM1NA==,size_16,color_FFFFFF,t_70#pic_center)
看到这个功能点可能会有点头大,不过好在Android为我们提供一个美好的工具:PorterDuffXfermode
至于它的功能介绍和简单使用,大家可以参考这篇文章
今天我们就用到其中一个模式:DST_OUT,它的解释:只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤
至于怎么使用它呢,下面开始吧
1、我们需要把镂空的Bitmap给绘制出来和灰色的背景进行叠加绘制。也可以把从View中获取Bitmap:
public static Bitmap buildViewDrawCache(View target, @ColorInt int color) {
Bitmap bitmap = null;
if (target == null) {
return null;
}
try {
target.destroyDrawingCache();
target.setDrawingCacheBackgroundColor(color);
boolean isEnable = target.isDrawingCacheEnabled();
if (!isEnable) {
target.setDrawingCacheEnabled(true);
}
bitmap = target.getDrawingCache();
} catch (Throwable error) {
}
return bitmap;
}
具体绘制:
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
int layoutId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawColor(mBackgroundColor);
canvas.drawBitmap(bitmap, null, hollowingInfo.rectF, mPaint);
canvas.restoreToCount(layoutId);
2、设置使用PorterDuffXfermode的模式
public class HomeBgGuideView extends View {
private int mBackgroundColor;
private PorterDuffXfermode mBitmapfermode;
private Paint mPaint;
public HomeBgGuideView(Context context) {
super(context);
initLayout();
}
public HomeBgGuideView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initLayout();
}
private void initLayout() {
if (DEBUG) {
Log.d(TAG, "initLayout() called");
}
Resources resources = getResources();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundColor = resources.getColor(R.color.black_alpha_80);
mBitmapfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
int layoutId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawColor(mBackgroundColor);
mPaint.setXfermode(mBitmapfermode);
canvas.drawBitmap(bitmap, null, rectF, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layoutId);
}
...
}
3、确定图形镂空的定位,我们可以通过getLocationOnScreen方法获取当前需要镂空View的位置
int[] location = new int[2];
anchorView.getLocationOnScreen(location);
float x = location[0];
float y = location[1];
mAnchorRect.set(x, y, x + anchorView.getWidth() * 2, y + anchorView.getHeight() * 2);
至此 整个不规则镂空的使用方式介绍完毕
完成代码如下:
public class HomeBgGuideView extends View {
private static final boolean DEBUG = BuildConfig.DEBUG;
private static final String TAG = DEBUG ? "BarracksGuideView" : "";
private int mBackgroundColor;
private PorterDuffXfermode mXfermode;
private PorterDuffXfermode mBitmapfermode;
private Paint mPaint;
private List<HollowingInfo> mHollowingOutList = new ArrayList<>();
private float mRadius;
public HomeBgGuideView(Context context) {
super(context);
initLayout();
}
public HomeBgGuideView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initLayout();
}
private void initLayout() {
if (DEBUG) {
Log.d(TAG, "initLayout() called");
}
Resources resources = getResources();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundColor = resources.getColor(R.color.black_alpha_80);
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
mBitmapfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
mRadius = getResources().getDimension(R.dimen.dp_10);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
int layoutId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawColor(mBackgroundColor);
if (mHollowingOutList != null) {
for (HollowingInfo hollowingInfo : mHollowingOutList) {
if (hollowingInfo.hollowingState == HollowingState.SHAPE) {
mPaint.setXfermode(mXfermode);
float radius = hollowingInfo.mRadius == 0 ? mRadius : hollowingInfo.mRadius;
canvas.drawRoundRect(hollowingInfo.rectF, radius, radius, mPaint);
}
else if (hollowingInfo.bitmap != null && hollowingInfo.bitmap.get() != null &&
hollowingInfo.hollowingState == HollowingState.BITMAP) {
mPaint.setXfermode(mBitmapfermode);
canvas.drawBitmap(hollowingInfo.bitmap.get(), null, hollowingInfo.rectF, mPaint);
}
}
}
mPaint.setXfermode(null);
canvas.restoreToCount(layoutId);
}
public void setRadius(float radius) {
if (DEBUG) {
Log.d(TAG, "setRadius() called with: radius = [" + radius + "]");
}
mRadius = radius;
postInvalidate();
}
public void setShapeDataList(List<RectF> rectList) {
if (DEBUG) {
Log.d(TAG, "setDataList() called with: rectList = [" + rectList + "]");
}
if (rectList == null || rectList.isEmpty()) {
return;
}
List<HollowingInfo> dataList = new ArrayList<>(rectList.size());
for (RectF rectF : rectList) {
HollowingInfo hollowingInfo = new HollowingInfo();
hollowingInfo.rectF = rectF;
dataList.add(hollowingInfo);
}
setDataList(dataList);
}
public void setDataList(List<HollowingInfo> rectList) {
if (DEBUG) {
Log.d(TAG, "setDataList() called with: rectList = [" + rectList + "]");
}
if (rectList == null || rectList.isEmpty()) {
return;
}
mHollowingOutList.clear();
mHollowingOutList.addAll(rectList);
postInvalidate();
}
public void clearHollowingOutList() {
if (mHollowingOutList == null) {
return;
}
for (HollowingInfo hollowingInfo : mHollowingOutList) {
hollowingInfo.rectF = null;
if (hollowingInfo.bitmap != null) {
hollowingInfo.bitmap.clear();
hollowingInfo.bitmap = null;
}
}
mHollowingOutList.clear();
postInvalidate();
}
public static class HollowingInfo {
public RectF rectF;
public float mRadius;
public SoftReference<Bitmap> bitmap;
public HollowingState hollowingState = HollowingState.SHAPE;
}
public enum HollowingState {
BITMAP, SHAPE
}
}
在这里大家可以有一个疑问,为什么绘制镂空的不规则图形时使用的是DST_OUT,而使用绘制规则图形时使用CLEAR, 大家可以评论区讨论
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)