原理就是用一个蒙层覆盖在界面之上,这里所说的界面从下面获取:
activity.getWindow().getDecorView()
高亮部分原理使用Xfermode图像混合模式,参考Android进阶——Xfermode图像混合模式
工具类:
/**
* 高亮引导工具类
*
*/
public class HighlightUtil {
/**
*
* @param activity
* @param guideViews 要高亮提示的view数组 (TODO 目前只至此一个view,待扩充)
*/
public static void setHighlight(Activity activity, View[] guideViews) {
if (activity == null || guideViews == null || guideViews.length <= 0) {
return;
}
View highlightView = new HighlightView(activity, guideViews);
View rootView = activity.getWindow().getDecorView();
if (rootView instanceof FrameLayout) {
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
((FrameLayout) rootView).addView(highlightView,
((FrameLayout) rootView).getChildCount(), lp);
} else {
FrameLayout frameLayout = new FrameLayout(activity);
ViewGroup parent = (ViewGroup) rootView.getParent();
parent.removeView(rootView);
parent.addView(frameLayout, rootView.getLayoutParams());
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frameLayout.addView(rootView, lp);
lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frameLayout.addView(highlightView, lp);
}
}
}
自定义蒙层:
/**
* 自定义高亮引导view
*
*/
public class HighlightView extends View {
private View[] guideViews;
private int padding = 20;
private int[] guideLocation; // left right top bottom
private Bitmap arrow;
private Bitmap dst;
private Bitmap src;
public HighlightView(Context context, View[] guideViews) {
super(context);
this.guideViews = guideViews;
// 绘制完成后初始化
getViewTreeObserver().addOnGlobalLayoutListener(mListener);
}
private ViewTreeObserver.OnGlobalLayoutListener mListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// 移除监听,只初始化一次
getViewTreeObserver().removeOnGlobalLayoutListener(mListener);
arrow = BitmapFactory.decodeResource(getResources(), R.drawable.highlight_arrow);
dst = createDstBitmap(guideViews[0]);
src = createSrcBitmap();
}
};
@Override
protected void onDraw(Canvas canvas) {
if (guideViews == null || guideViews.length <= 0) {
return;
}
//禁用硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
//使用离屏绘制
int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
Paint paint = new Paint();
canvas.drawBitmap(dst, 0, 0 ,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(src, 0, 0 ,paint);
paint.setXfermode(null);
canvas.drawBitmap(arrow, guideLocation[0] - arrow.getWidth(), guideLocation[3], paint);
paint.setColor(0xFFFFFFFF);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(40);
canvas.drawText("点击扫描登录", guideLocation[0] - arrow.getWidth(),
guideLocation[3] + arrow.getHeight() + 3 * padding, paint);
canvas.restoreToCount(layerID);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// 关闭高亮指引
setVisibility(GONE);
}
return true;
}
public Bitmap createDstBitmap(View view) {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scrPaint.setColor(0xFFFFFFFF);
int[] location = new int[2];
view.getLocationInWindow(location);
int[] location2 = new int[2];
getLocationInWindow(location2); // 用于计算状态栏的高度
guideLocation = new int[4];
guideLocation[0] = location[0] - padding;
guideLocation[1] = location[1] - location2[1] - padding;
guideLocation[2] = location[0] + view.getWidth() + padding;
guideLocation[3] = location[1] - location2[1] + view.getHeight() + padding;
canvas.drawOval(new RectF(guideLocation[0], guideLocation[1], guideLocation[2],
guideLocation[3]),scrPaint);
return bitmap;
}
public Bitmap createSrcBitmap() {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(0x99000000);
canvas.drawRect(new Rect(0, 0, getWidth(), getHeight()), dstPaint);
return bitmap;
}
}
效果如下:
后期需要补充功能:
- 版本号控制,只在某个版本号显示
- 只显示一次
- 高亮提示的view数组