前言
Github上有不少高亮引導的庫,比如下面的。但是覺得他們的功能太多,不夠簡單,跟項目需求不太符合。原本打算直接使用HightLight(效果比較符合需求),但是發(fā)現(xiàn)issue上有不少bitmap的oom異常,以及停止更新維護了。所以自己嘗試實現(xiàn)一下。
思路
看了好幾篇文章,大體思路都是在DecorView上添加布局,在遮蓋Activity的布局。HiGuide也不例外。
- 首先兩個最關鍵的參數(shù),Activity,需要高亮的View。Activity用來獲取DecorView,高亮View用來計算高亮位置及區(qū)域。
- 獲取Activity的DecorView,并把遮蓋層布局GuideView添加進去,就可以實現(xiàn)遮擋效果。
- 在遮蓋層GuideView(FrameLayout)內,根據(jù)高亮View在屏幕的位置(View.getLocationOnSrceen()),及高亮View的寬高,及配置的形狀等參數(shù),使用使用Path進行計算(Path.op()可以進行多個path進行層疊區(qū)域計算,跟Paint的Xfermode一樣),得到鏤空的遮蓋區(qū)域,在畫布上繪制該Path就可以得到有高亮區(qū)域的遮蓋層。
- 想要添加其他的提示布局,Textview或Imageview,或者其他Layout容器,在GuideView內add就可以了,注意配置邊距參數(shù)以定位。
- 點擊效果通常分兩塊,一塊是背景層,也就是整個遮蓋層,另外一個就是高亮區(qū)域。高亮區(qū)域,可以在使用View在屏幕的位置,及寬高,就可以創(chuàng)建一個RectF矩形區(qū)域,把所有的高亮View的RectF用List保存起來。在GuideView的onTouch中,計算,如果點擊的坐標xy在某個高亮RectF矩形內,即為點擊了高亮區(qū)域。
關鍵代碼
- 添加遮蓋層
mRootView = (ViewGroup) ((Activity) mContext).getWindow().getDecorView();
if (mRootView instanceof FrameLayout) {
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mRootView.addView(mGuideView, mRootView.getChildCount(), lp);
} else {
FrameLayout frameLayout = new FrameLayout(mContext);
ViewGroup parent = (ViewGroup) mRootView.getParent();
parent.removeView(mRootView);
parent.addView(frameLayout, mRootView.getLayoutParams());
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frameLayout.addView(mRootView, lp);
}
- 移除遮蓋層
private void remove() {
ViewGroup parent = (ViewGroup) getParent();
if (parent instanceof RelativeLayout || parent instanceof FrameLayout) {
parent.removeView(this);
} else {
parent.removeView(this);
View origin = parent.getChildAt(0);
ViewGroup graParent = (ViewGroup) parent.getParent();
graParent.removeView(parent);
graParent.addView(origin, parent.getLayoutParams());
}
if (mRemoveCallback != null) {
mRemoveCallback.callback();
}
}
- 高亮區(qū)域繪制
@Override
protected void onDraw(Canvas canvas) {
mBgPath.reset();
mShapePath.reset();
mBgPath.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
for (HightLight hightLight : mOverlay.getHightLightList()) {
Path shapePath = calcHightLightShapePath(hightLight, mShapePath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mBgPath.op(shapePath, Path.Op.XOR);
}
}
canvas.drawPath(mBgPath, mPaint);
}
- 高亮形狀計算
private Path calcHightLightShapePath(HightLight hightLight, Path shapePath) {
shapePath.reset();
switch (hightLight.getShape()) {
case HiGuide.SHAPE_CIRCLE:
shapePath.addCircle(hightLight.getRectF().centerX(),
hightLight.getRectF().centerY(),
hightLight.getRadius(), Path.Direction.CW);
break;
case HiGuide.SHAPE_OVAL:
shapePath.addOval(hightLight.getRectF(), Path.Direction.CW);
break;
case HiGuide.SHAPE_RECT:
shapePath.addRect(hightLight.getRectF(), Path.Direction.CW);
break;
default:
break;
}
return shapePath;
}
- 高亮區(qū)域點擊
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
//點擊高亮
if (mOverlay.getOnClickHightLightListener() != null) {
float x = event.getX();
float y = event.getY();
for (RectF rectF : mOverlay.getHightLightAreas()) {
if (rectF.contains(x, y)) {
mOverlay.getOnClickHightLightListener().onClick(this);
remove();
return super.onTouchEvent(event); //點擊高亮后不做其他的處理了,直接返回
}
}
}
//全局點擊
if (mOverlay.getOnClickGuideViewListener() != null) {
mOverlay.getOnClickGuideViewListener().onClick(this);
}
if (mOverlay.isTouchDismiss()) {
remove();
}
}
return true;
}
- 添加提示布局
public void addTipsView(Tips tips) {
View tipsView = LayoutInflater.from(getContext()).inflate(tips.layoutRes, this, false);
LayoutParams lp = (LayoutParams) tipsView.getLayoutParams();
lp.leftMargin = tips.leftMargin;
lp.topMargin = tips.topMargin;
lp.rightMargin = tips.rightMargin;
lp.bottomMargin = tips.bottomMargin;
if (lp.rightMargin != 0) {
lp.gravity = Gravity.END;
} else {
lp.gravity = Gravity.START;
}
if (lp.bottomMargin != 0) {
lp.gravity |= Gravity.BOTTOM;
} else {
lp.gravity |= Gravity.TOP;
}
tipsView.setLayoutParams(lp);
addView(tipsView, lp);
}
效果

效果圖
后記
因為之前看了一篇文章了解到使用Path進行自定義view繪制,而HightLight項目又是使用Bitmap,被不少issue都提到這個,所以打算使用Path替代Bitmap,這個功能并不復雜,按照思路實現(xiàn)就ok。