/**
* 蒙层组件
** /
public class IntroView extends RelativeLayout {
private static final String TAG = "IntroView";
/** 区域镂空 */
public static final int ERASER_TYPE_HOLLOW = 1;
/** 区域浮雕 */
public static final int ERASER_TYPE_CAMEO = 2;
private static final int DEFAULT_MASK_COLOR = 0x70000000;
private static final int DEFAULT_INFO_VIEW_MARGIN = 50; // in pixels
private Handler mHandler;
private boolean mIsReady;
private boolean mIsLayoutCompleted;
private int mWidth;
private int mHeight;
private Target mTarget;
private Target[] mServants;
private Paint mEraser;
private Bitmap mMaskBitmap;
private Canvas mMaskBitmapCanvas;
private int mMaskColor = DEFAULT_MASK_COLOR;
private View mInfoView;
private OnDismissListener mOnDismissListener;
private OnSkippedListener mOnSkippedListener;
private OnShowListener mOnShowListener;
private boolean mInterceptEvent = true;
public IntroView(Context context) {
super(context);
init(context);
}
public IntroView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public IntroView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public IntroView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(final Context context) {
setWillNotDraw(false);
setVisibility(INVISIBLE);
mHandler = new Handler();
mEraser = new Paint();
mEraser.setFlags(Paint.ANTI_ALIAS_FLAG);
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!mIsLayoutCompleted) {
setInfoLayout();
setSkipLayout();
mIsLayoutCompleted = true;
removeOnGlobalLayoutListener(IntroView.this, this);
}
}
});
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) {
if (Build.VERSION.SDK_INT < 16) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
} else {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mIsReady) return;
if (mMaskBitmap == null || canvas == null) {
if (mMaskBitmap != null) {
mMaskBitmap.recycle();
mMaskBitmap = null;
}
mMaskBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
mMaskBitmapCanvas = new Canvas(mMaskBitmap);
}
mMaskBitmapCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mMaskBitmapCanvas.drawColor(mMaskColor);
drawTarget(mTarget);
if (mServants != null) {
for (Target servant : mServants) {
drawTarget(servant);
}
}
if (canvas != null) canvas.drawBitmap(mMaskBitmap, 0, 0, null);
}
private void drawTarget(Target target) {
if (target == null || mMaskBitmapCanvas == null) return;
if (target.getEraserType() == ERASER_TYPE_CAMEO) {
mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
target.getView().setDrawingCacheEnabled(true);
Bitmap drawingCache = target.getView().getDrawingCache();
if (drawingCache != null) {
mMaskBitmapCanvas.setDensity(drawingCache.getDensity());
mMaskBitmapCanvas.drawBitmap(drawingCache, target.getRect().left, target.getRect().top, mEraser);
}
target.getView().setDrawingCacheEnabled(false);
} else if (target.getEraserType() == ERASER_TYPE_HOLLOW) {
mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mMaskBitmapCanvas.drawRect(target.getRect().left, target.getRect().top, target.getRect().right, target.getRect().bottom, mEraser);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mInterceptEvent) return super.onTouchEvent(event);
float curX = event.getX();
float curY = event.getY();
boolean isTouchOnFocus = false;
Target target = null;
if (mTarget.getRect().contains((int) curX, (int) curY)) {
isTouchOnFocus = true;
target = mTarget;
} else if (mServants != null) {
for (Target t : mServants) {
if (t.getRect().contains((int) curX, (int) curY)) {
isTouchOnFocus = true;
target = t;
break;
}
}
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isTouchOnFocus) {
if (target != null) {
target.getView().performClick();
if (target.isDismissOnClicked()) {
mHandler.post(new Runnable() {
@Override
public void run() {
dismiss();
}
});
}
}
return false;
}
return true;
default:
break;
}
return super.onTouchEvent(event);
}
private void show(Activity activity) {
((ViewGroup) activity.getWindow().getDecorView()).addView(this);
setReady(true);
mHandler.post(new Runnable() {
@Override
public void run() {
setVisibility(View.VISIBLE);
if (mOnShowListener != null) mOnShowListener.onShowed();
}
});
}
public void dismiss() {
setVisibility(GONE);
removeView();
if (mOnDismissListener != null) mOnDismissListener.onDismiss();
}
public boolean isDismissed() {
return !(getVisibility() == View.VISIBLE);
}
private void removeView(){
if (getParent() != null ) {
((ViewGroup) getParent()).removeView(this);
}
}
private void setInfoLayout() {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mInfoView == null) return;
if (mInfoView.getParent() != null) {
((ViewGroup) mInfoView.getParent()).removeView(mInfoView);
}
RelativeLayout infoViewContainer = new RelativeLayout(getContext());
infoViewContainer.addView(mInfoView);
LayoutParams infoViewContainerParams = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
if (mTarget.getPoint().y <= mHeight / 2) {
infoViewContainer.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL);
infoViewContainerParams.setMargins(0, mTarget.getRect().bottom + DEFAULT_INFO_VIEW_MARGIN, 0, 0);
} else {
infoViewContainer.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL);
infoViewContainerParams.setMargins(0, 0, 0, mHeight - mTarget.getRect().top + DEFAULT_INFO_VIEW_MARGIN);
}
infoViewContainer.setLayoutParams(infoViewContainerParams);
addView(infoViewContainer);
}
});
}
private void setSkipLayout() {
mHandler.post(new Runnable() {
@Override
public void run() {
ImageView skipView = new ImageView(getContext());
skipView.setImageResource(R.drawable.intro_skip_icon);
LayoutParams lp = new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_TOP|ALIGN_PARENT_RIGHT);
lp.setMargins(0, 100, 50, 0);
skipView.setLayoutParams(lp);
addView(skipView);
skipView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mOnSkippedListener != null) mOnSkippedListener.onSkipped();
dismiss();
}
});
}
});
}
private void setMaskColor(int maskColor) {
mMaskColor = maskColor;
}
private void setReady(boolean ready) {
mIsReady = ready;
}
private void setTarget(Target target) {
mTarget = target;
}
private void setServants(Target... servants) {
mServants = servants;
}
private void setInfoView(View infoView) {
mInfoView = infoView;
}
private void setOnDismissListener(OnDismissListener onDismissListener) {
mOnDismissListener = onDismissListener;
}
private void setOnSkippedListener(OnSkippedListener onSkippedListener) {
mOnSkippedListener = onSkippedListener;
}
private void setOnShowListener(OnShowListener onShowListener) {
mOnShowListener = onShowListener;
}
public void setInterceptEvent(boolean interceptEvent) {
mInterceptEvent = interceptEvent;
}
public interface OnDismissListener {
void onDismiss();
}
public interface OnSkippedListener {
void onSkipped();
}
public interface OnShowListener {
void onShowed();
}
private interface Target {
/**
* 获取目标组件的中心点
*
* @return
*/
Point getPoint();
/**
* 获取目标组件的矩形表示
*
* @return
*/
Rect getRect();
/**
* 获取目标组件
*
* @return
*/
View getView();
int getEraserType();
boolean isDismissOnClicked();
}
public static final class ViewTarget implements Target {
private View mView;
private int mEraserType = ERASER_TYPE_HOLLOW;
private boolean mDismissOnClicked = true;
public ViewTarget(View view) {
mView = view;
}
@Override
public Point getPoint() {
int[] location = new int[2];
mView.getLocationInWindow(location);
return new Point(location[0] + (mView.getWidth() / 2), location[1] + (mView.getHeight() / 2));
}
@Override
public Rect getRect() {
int[] location = new int[2];
mView.getLocationInWindow(location);
return new Rect(location[0], location[1], location[0] + mView.getWidth(), location[1] + mView.getHeight());
}
@Override
public View getView() {
return mView;
}
@Override
public int getEraserType() {
return mEraserType;
}
public void setEraserType(int eraserType) {
mEraserType = eraserType;
}
@Override
public boolean isDismissOnClicked() {
return mDismissOnClicked;
}
public void setDismissOnClicked(boolean dismissOnClicked) {
mDismissOnClicked = dismissOnClicked;
}
}
public static final class Builder {
private Activity mActivity;
private IntroView mIntroView;
public Builder(Activity activity) {
mActivity = activity;
mIntroView = new IntroView(activity);
}
public Builder setMaskColor(int maskColor) {
mIntroView.setMaskColor(maskColor);
return this;
}
public Builder setTarget(View view) {
mIntroView.setTarget(new ViewTarget(view));
return this;
}
public Builder setTarget(View view, int eraserType) {
ViewTarget target = new ViewTarget(view);
target.mEraserType = eraserType;
mIntroView.setTarget(target);
return this;
}
public Builder setTarget(ViewTarget target) {
mIntroView.setTarget(target);
return this;
}
public Builder setServants(View... views) {
if (views != null) {
Target[] servants = new Target[views.length];
for (int i = 0, l = views.length; i < l; i++) {
servants[i] = new ViewTarget(views[i]);
}
mIntroView.setServants(servants);
}
return this;
}
public Builder setServants(ViewTarget... targets) {
mIntroView.setServants(targets);
return this;
}
public Builder setInfoView(View view) {
mIntroView.setInfoView(view);
return this;
}
public Builder setOnDismissListener(OnDismissListener onDismissListener) {
mIntroView.setOnDismissListener(onDismissListener);
return this;
}
public Builder setOnSkippedListener(OnSkippedListener onSkippedListener) {
mIntroView.setOnSkippedListener(onSkippedListener);
return this;
}
public Builder setOnShowListener(OnShowListener onShowListener) {
mIntroView.setOnShowListener(onShowListener);
return this;
}
public Builder setInterceptEvent(boolean interceptEvent) {
mIntroView.setInterceptEvent(interceptEvent);
return this;
}
public IntroView build() {
return mIntroView;
}
public IntroView show() {
build().show(mActivity);
return mIntroView;
}
}
}