1. 程式人生 > >Android-繪圖-半透明/蒙層引導使用者

Android-繪圖-半透明/蒙層引導使用者

一.使用半透明圖引導


// 同時新增兩個目標View,即同一層圖兩個高亮目標View
GuideUserView.show(MainActivity.this,
    new ViewEntity(目標View1, R.layout.guide1, Direction.BOTTOM),
    new ViewEntity(目標View2, R.layout.guide2, Direction.BOTTOM));

// 自定義佈局guide1.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <View android:layout_width="64dp" android:layout_height="64dp" android:background="@drawable/arrow"/> <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="使用說明"/> </LinearLayout>

二.GuideUserView類

1.GuideUserView類繼承相對佈局      
2.新增自定義佈局mCustomLayout,通過Margins設定mCustomLayout的位置  
3.用canvas在mTargetView周圍繪製高亮圓圈  
4.用mPaint.setXfermode(xx)去除高亮圓圈和半透明背景的交集  

import android.annotation.SuppressLint;
import
android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.RelativeLayout; import static com.wellcell.view.GuideUserView.Shape.CIRCULAR; import static com.wellcell.view.GuideUserView.Shape.ELLIPSE; import static com.wellcell.view.GuideUserView.Shape.RECTANGULAR; @SuppressLint("ViewConstructor") public class GuideUserView extends RelativeLayout implements ViewTreeObserver.OnGlobalLayoutListener { private final static String TAG = "GuideUserView"; private final static String PRI = "GuideUserViewID_"; private final ClickListener mClickListener; private Context mContent; private final Paint mPaint = new Paint(); private final RectF mRect = new RectF(); private ViewEntity[] mViews; // 目標View、自定義佈局等引數實體陣列 private boolean handleTouch = false; /** * 預設顯示一次 */ public static void show(Context context, ViewEntity... views) { GuideUserView.show(true, null, context, views); } /** * 預設顯示一次,監聽點選 */ public static void show(ClickListener clickListener, Context context, ViewEntity... views) { GuideUserView.show(true, clickListener, context, views); } public static void show(boolean showOne, ClickListener clickListener, Context context, ViewEntity... views) { try { if (showOne) { String id = PRI + views[0].mCustomLayoutID; if (context.getSharedPreferences(TAG, Context.MODE_PRIVATE).getBoolean(id, false)) return; context.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit().putBoolean(id, true).apply(); } new GuideUserView(context, clickListener, views); } catch (Exception e) { e.printStackTrace(); } } private GuideUserView(Context context, ClickListener clickListener, ViewEntity... views) { super(context); mContent = context; mClickListener = clickListener; mViews = views; mPaint.setAntiAlias(true); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); // 去除兩次繪圖的交集 setBackgroundColor(Color.TRANSPARENT); getViewTreeObserver().addOnGlobalLayoutListener(this); /* 在Activity上面新增半透明/蒙層的引導View,有兩種方法 方法一 ((FrameLayout) ((Activity) mContent).getWindow().getDecorView()).addView(this); ((FrameLayout) ((Activity) mContent).getWindow().getDecorView()).removeView(this); 使用DecorView().addView,引導蒙層介面會被PopupWindow,Dialog等彈窗遮擋 方法二 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.format = PixelFormat.TRANSLUCENT; ((Activity) mContent).getWindow().getWindowManager().addView(this, params); ((Activity) mContent).getWindow().getWindowManager().removeView(this); 該方法能覆蓋PopupWindow,Dialog等彈窗,但有時獲取目標View的座標偶爾會失效, 獲取目標View的座標方法:View.getLocationOnScreen, View.getLocationOnScreen, View.getGlobalVisibleRect, View.getLocalVisibleRect */ ((FrameLayout) ((Activity) mContent).getWindow().getDecorView()).addView(this); //新增蒙層View } @Override public boolean onTouchEvent(MotionEvent event) { if (!handleTouch) { handleTouch = true; if (mClickListener != null) mClickListener.onClick(); // ((Activity) mContent).getWindow().getWindowManager().removeView(this); ((FrameLayout) ((Activity) mContent).getWindow().getDecorView()).removeView(this);//移除蒙層View } return true; } @Override public void onGlobalLayout() {// 監聽全域性檢視樹開始佈局 getViewTreeObserver().removeOnGlobalLayoutListener(this); addCustomView(); } protected void addCustomView() {// 在目標view周圍新增自定義佈局 int statusBarHeight = 0; int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) statusBarHeight = getResources().getDimensionPixelSize(resourceId); for (final ViewEntity ve : mViews) { // 獲取targetView的中心座標 if (ve.mTargetView != null) { ve.targetW = ve.mTargetView.getWidth() / 2; ve.targetH = ve.mTargetView.getHeight() / 2; ve.mTargetView.getLocationOnScreen(ve.mCenter); ve.mCenter[0] += ve.targetW; ve.mCenter[1] += ve.targetH; } // 方位 View view = LayoutInflater.from(getContext()).inflate(ve.mCustomLayoutID, this, false); LayoutParams params = (LayoutParams) view.getLayoutParams(); if (ve.mDirection != null) { int width = getWidth(); int height = getHeight(); int left = ve.mCenter[0] - ve.targetW; int right = ve.mCenter[0] + ve.targetW; int top = ve.mCenter[1] - ve.targetH; int bottom = ve.mCenter[1] + ve.targetH; switch (ve.mDirection) { case TOP: params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params.setMargins(left, 0, 0, height - top); break; case LEFT: params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.setMargins(0, top, width - left, 0); break; case BOTTOM: params.setMargins(left, bottom, 0, 0); break; case RIGHT: params.setMargins(right, top, 0, 0); break; case LEFT_TOP: params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.setMargins(0, 0, width - left, height - top); break; case LEFT_BOTTOM: params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.setMargins(0, bottom, width - left, 0); break; case RIGHT_TOP: params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params.setMargins(right, 0, 0, height - top); break; case RIGHT_BOTTOM: params.setMargins(right, bottom, 0, 0); break; } } else { params.addRule(RelativeLayout.CENTER_IN_PARENT); } addViewInLayout(view, -1, params, true);// 新增view, 不會重新佈局 } requestLayout();// 統一重新佈局 } @Override protected void onDraw(Canvas canvas) { canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG); // 新建圖層 canvas.drawColor(0x77000000);// 繪製半透明背景 // 在目標View周圍裁剪出高亮圓圈 for (ViewEntity ve : mViews) { if (ve.targetW == 0 || ve.targetH == 0) return; if (ve.mShape == ELLIPSE) { // 橢圓 mRect.left = ve.mCenter[0] - ve.targetW; mRect.top = ve.mCenter[1] - ve.targetH; mRect.right = ve.mCenter[0] + ve.targetW; mRect.bottom = ve.mCenter[1] + ve.targetH; canvas.drawOval(mRect, mPaint); } else if (ve.mShape == RECTANGULAR) { // 矩形 mRect.left = ve.mCenter[0] - ve.targetW; mRect.top = ve.mCenter[1] - ve.targetH; mRect.right = ve.mCenter[0] + ve.targetW; mRect.bottom = ve.mCenter[1] + ve.targetH; canvas.drawRoundRect(mRect, 16, 16, mPaint); } else if (ve.mShape == CIRCULAR) { // 圓形 canvas.drawCircle(ve.mCenter[0], ve.mCenter[1], ve.targetW, mPaint); } } } public interface ClickListener { void onClick(); } /** * 目標View和自定義佈局等引數實體 */ public static class ViewEntity { private View mTargetView; // 目標wiew private int[] mCenter = new int[2]; // 目標wiew中心座標 private Shape mShape; // 目標wiew高亮圓圈形狀 private int targetW = -1; // 目標wiew高亮寬半徑 private int targetH = -1; // 目標wiew高亮高半徑 private int mCustomLayoutID; // 自定義佈局資源ID private Direction mDirection; // 自定義佈局相對於目標wiew的方向 public ViewEntity(View targetView, int customLayoutID, Direction direction) { this(targetView, null, Shape.RECTANGULAR, customLayoutID, direction); } public ViewEntity(View targetView, int[] targetSize, Shape shape, int customLayoutID, Direction direction) { this.mTargetView = targetView; this.mShape = shape; this.mCustomLayoutID = customLayoutID; this.mDirection = direction; if (targetSize != null) { targetW = targetSize[0] / 2; targetH = targetSize[1] / 2; } } } public enum Direction { // 相對於目標wiew的方位 LEFT, TOP, RIGHT, BOTTOM, LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM } public enum Shape { // 目標wiew的高亮圓圈形狀: 圓形,橢圓,圓角矩形 CIRCULAR, ELLIPSE, RECTANGULAR } }