MyGuideCaseView.java 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. package com.xunao.effectdemo.view;
  2. import android.animation.Animator;
  3. import android.annotation.TargetApi;
  4. import android.app.Activity;
  5. import android.content.Context;
  6. import android.content.SharedPreferences;
  7. import android.os.Build;
  8. import android.text.Spanned;
  9. import android.util.AttributeSet;
  10. import android.util.DisplayMetrics;
  11. import android.view.Gravity;
  12. import android.view.MotionEvent;
  13. import android.view.View;
  14. import android.view.ViewAnimationUtils;
  15. import android.view.ViewGroup;
  16. import android.view.ViewTreeObserver;
  17. import android.view.animation.Animation;
  18. import android.view.animation.AnimationUtils;
  19. import android.view.animation.TranslateAnimation;
  20. import android.widget.FrameLayout;
  21. import android.widget.ImageView;
  22. import android.widget.TextView;
  23. import androidx.annotation.AttrRes;
  24. import androidx.annotation.LayoutRes;
  25. import androidx.annotation.NonNull;
  26. import androidx.annotation.Nullable;
  27. import androidx.annotation.RequiresApi;
  28. import androidx.annotation.StyleRes;
  29. import com.xuexiang.xui.XUI;
  30. //import com.xuexiang.xui.widget.guidview.Calculator;
  31. import com.xuexiang.xui.widget.guidview.DismissListener;
  32. import com.xuexiang.xui.widget.guidview.FocusShape;
  33. //import com.xuexiang.xui.widget.guidview.GuideImageView;
  34. import com.xuexiang.xui.widget.guidview.OnViewInflateListener;
  35. //import com.xuexiang.xui.widget.guidview.Utils;
  36. import com.xunao.effectdemo.R;
  37. /**
  38. * author : 程中强
  39. * e-mail : 740479946@qq.com
  40. * date : 2022/8/215:52
  41. * desc :
  42. * version: 1.0
  43. */
  44. public class MyGuideCaseView extends FrameLayout implements ViewTreeObserver.OnGlobalLayoutListener {
  45. // public MyGuideCaseView(Builder builder) {
  46. // super(builder);
  47. // }
  48. MyGuideCaseView(@NonNull Context context) {
  49. super(context);
  50. }
  51. MyGuideCaseView(@NonNull Context context, @Nullable AttributeSet attrs) {
  52. super(context, attrs);
  53. }
  54. MyGuideCaseView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
  55. super(context, attrs, defStyleAttr);
  56. }
  57. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  58. MyGuideCaseView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
  59. super(context, attrs, defStyleAttr, defStyleRes);
  60. }
  61. // Tag for container view
  62. private static final String CONTAINER_TAG = "ShowCaseViewTag";
  63. // SharedPreferences name
  64. private static final String PREF_NAME = "PrefShowCaseView";
  65. /**
  66. * 设置已经显示
  67. *
  68. * @param context
  69. * @param id
  70. */
  71. public static void setShowOnce(Context context, String id) {
  72. SharedPreferences sharedPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
  73. sharedPrefs.edit().putBoolean(id, true).apply();
  74. }
  75. /**
  76. * 是否已显示
  77. *
  78. * @param context
  79. * @param id
  80. * @return
  81. */
  82. public static boolean isShowOnce(Context context, String id) {
  83. SharedPreferences sharedPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
  84. return sharedPrefs.getBoolean(id, false);
  85. }
  86. /**
  87. * Resets the show once flag
  88. *
  89. * @param context context that should be used to create the shared preference instance
  90. * @param id id of the show once flag that should be reset
  91. */
  92. public static void resetShowOnce(Context context, String id) {
  93. SharedPreferences sharedPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
  94. sharedPrefs.edit().remove(id).apply();
  95. }
  96. /**
  97. * Resets all show once flags
  98. *
  99. * @param context context that should be used to create the shared preference instance
  100. */
  101. public static void resetAllShowOnce(Context context) {
  102. SharedPreferences sharedPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
  103. sharedPrefs.edit().clear().apply();
  104. }
  105. /**
  106. * Builder parameters
  107. */
  108. private Activity mActivity;
  109. private String mTitle;
  110. private Spanned mSpannedTitle;
  111. private String mId;
  112. private double mFocusCircleRadiusFactor;
  113. private View mView;
  114. private int mBackgroundColor;
  115. private int mFocusBorderColor;
  116. private int mTitleGravity;
  117. private int mTitleStyle;
  118. private int mTitleSize;
  119. private int mTitleSizeUnit;
  120. private int mPictureResId;
  121. private int mPictureWidth;
  122. private int mPictureHeight;
  123. private int mPictureGravity;
  124. private int mPictureOffsetX;
  125. private int mPictureOffsetY;
  126. private int mCustomViewRes;
  127. private int mFocusBorderSize;
  128. private int mRoundRectRadius;
  129. private OnViewInflateListener mViewInflateListener;
  130. private Animation mEnterAnimation, mExitAnimation;
  131. private boolean mCloseOnTouch;
  132. private boolean mFitSystemWindows;
  133. private int mAdjustHeight;
  134. private int mFocusOffsetX;
  135. private FocusShape mFocusShape;
  136. private DismissListener mDismissListener;
  137. private int mAnimationDuration = 800;
  138. private int mFocusAnimationMaxValue;
  139. private int mFocusAnimationStep;
  140. private int mCenterX, mCenterY;
  141. private ViewGroup mRoot;
  142. private SharedPreferences mSharedPreferences;
  143. private Calculator mCalculator;
  144. private int mFocusPositionX, mFocusPositionY, mFocusCircleRadius, mFocusRectangleWidth, mFocusRectangleHeight;
  145. private boolean mFocusAnimationEnabled;
  146. public MyGuideCaseView(Builder builder) {
  147. this(builder.mActivity, builder.mView, builder.mId, builder.mTitle, builder.mSpannedTitle, builder.mTitleGravity, builder.mTitleStyle, builder.mTitleSize, builder.mTitleSizeUnit,
  148. builder.mFocusCircleRadiusFactor, builder.mBackgroundColor, builder.mFocusBorderColor, builder.mFocusBorderSize, builder.mCustomViewRes, builder.mViewInflateListener,
  149. builder.mEnterAnimation, builder.mExitAnimation, builder.mCloseOnTouch, builder.mFitSystemWindows, builder.mAdjustHeight, builder.mFocusOffSetX, builder.mFocusShape, builder.mDismissListener, builder.mRoundRectRadius,
  150. builder.mPictureResId, builder.mPictureWidth, builder.mPictureHeight, builder.mPictureGravity, builder.mPictureOffSetX, builder.mPictureOffSetY, builder.mFocusPositionX, builder.mFocusPositionY, builder.mFocusCircleRadius, builder.mFocusRectangleWidth,
  151. builder.mFocusRectangleHeight, builder.mFocusAnimationEnabled, builder.mFocusAnimationMaxValue, builder.mFocusAnimationStep);
  152. }
  153. /**
  154. * Constructor for GuideCaseView
  155. *
  156. * @param activity Activity to show GuideCaseView in
  157. * @param view view to focus
  158. * @param id unique identifier for GuideCaseView
  159. * @param title title text
  160. * @param spannedTitle title text if spanned text should be used
  161. * @param titleGravity title gravity
  162. * @param titleStyle title text style
  163. * @param titleSize title text size
  164. * @param titleSizeUnit title text size unit
  165. * @param focusCircleRadiusFactor focus circle radius factor (default value = 1)
  166. * @param backgroundColor background color of GuideCaseView
  167. * @param focusBorderColor focus border color of GuideCaseView
  168. * @param focusBorderSize focus border size of GuideCaseView
  169. * @param customViewRes custom view layout resource
  170. * @param viewInflateListener inflate listener for custom view
  171. * @param enterAnimation enter animation for GuideCaseView
  172. * @param exitAnimation exit animation for GuideCaseView
  173. * @param closeOnTouch closes on touch if enabled
  174. * @param fitSystemWindows should be the same value of root view's fitSystemWindows value
  175. * @param focusShape shape of focus, can be circle or rounded rectangle
  176. * @param dismissListener listener that gets notified when showcase is dismissed
  177. * @param roundRectRadius round rectangle radius
  178. * @param focusPositionX focus at specific position X coordinate
  179. * @param focusPositionY focus at specific position Y coordinate
  180. * @param focusCircleRadius focus at specific position circle radius
  181. * @param focusRectangleWidth focus at specific position rectangle width
  182. * @param focusRectangleHeight focus at specific position rectangle height
  183. * @param animationEnabled flag to enable/disable animation
  184. */
  185. private MyGuideCaseView(Activity activity, View view, String id, String title, Spanned spannedTitle,
  186. int titleGravity, int titleStyle, int titleSize, int titleSizeUnit, double focusCircleRadiusFactor,
  187. int backgroundColor, int focusBorderColor, int focusBorderSize, int customViewRes,
  188. OnViewInflateListener viewInflateListener, Animation enterAnimation,
  189. Animation exitAnimation, boolean closeOnTouch, boolean fitSystemWindows, int adjustHeight, int focusOffsetX,
  190. FocusShape focusShape, DismissListener dismissListener, int roundRectRadius, int pictureResId, int pictureWidth, int pictureHeight, int pictureGravity, int pictureOffsetX, int pictureOffsetY,
  191. int focusPositionX, int focusPositionY, int focusCircleRadius, int focusRectangleWidth, int focusRectangleHeight,
  192. final boolean animationEnabled, int focusAnimationMaxValue, int focusAnimationStep) {
  193. super(activity);
  194. mId = id;
  195. mActivity = activity;
  196. mView = view;
  197. mTitle = title;
  198. mSpannedTitle = spannedTitle;
  199. mFocusCircleRadiusFactor = focusCircleRadiusFactor;
  200. mBackgroundColor = backgroundColor;
  201. mFocusBorderColor = focusBorderColor;
  202. mFocusBorderSize = focusBorderSize;
  203. mTitleGravity = titleGravity;
  204. mTitleStyle = titleStyle;
  205. mTitleSize = titleSize;
  206. mTitleSizeUnit = titleSizeUnit;
  207. mRoundRectRadius = roundRectRadius;
  208. mPictureResId = pictureResId;
  209. mPictureWidth = pictureWidth;
  210. mPictureHeight = pictureHeight;
  211. mPictureGravity = pictureGravity;
  212. mPictureOffsetX = pictureOffsetX;
  213. mPictureOffsetY = pictureOffsetY;
  214. mCustomViewRes = customViewRes;
  215. mViewInflateListener = viewInflateListener;
  216. mEnterAnimation = enterAnimation;
  217. mExitAnimation = exitAnimation;
  218. mCloseOnTouch = closeOnTouch;
  219. mFitSystemWindows = fitSystemWindows;
  220. mAdjustHeight = adjustHeight;
  221. mFocusOffsetX = focusOffsetX;
  222. mFocusShape = focusShape;
  223. mDismissListener = dismissListener;
  224. mFocusPositionX = focusPositionX;
  225. mFocusPositionY = focusPositionY;
  226. mFocusCircleRadius = focusCircleRadius;
  227. mFocusRectangleWidth = focusRectangleWidth;
  228. mFocusRectangleHeight = focusRectangleHeight;
  229. mFocusAnimationEnabled = animationEnabled;
  230. mFocusAnimationMaxValue = focusAnimationMaxValue;
  231. mFocusAnimationStep = focusAnimationStep;
  232. initializeParameters();
  233. }
  234. /**
  235. * Calculates and set initial parameters
  236. */
  237. private void initializeParameters() {
  238. mBackgroundColor = mBackgroundColor != 0 ? mBackgroundColor :
  239. mActivity.getResources().getColor(R.color.default_guide_case_view_background_color);
  240. mTitleGravity = mTitleGravity >= 0 ? mTitleGravity : Gravity.CENTER;
  241. mTitleStyle = mTitleStyle != 0 ? mTitleStyle : R.style.DefaultGuideCaseTitleStyle;
  242. DisplayMetrics displayMetrics = new DisplayMetrics();
  243. mActivity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
  244. int deviceWidth = displayMetrics.widthPixels;
  245. int deviceHeight = displayMetrics.heightPixels;
  246. mCenterX = deviceWidth / 2;
  247. mCenterY = deviceHeight / 2;
  248. mSharedPreferences = mActivity.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
  249. }
  250. /**
  251. * Shows GuideCaseView
  252. */
  253. public void show() {
  254. if (mActivity == null || (mId != null && isShownBefore())) {
  255. if (mDismissListener != null) {
  256. mDismissListener.onSkipped(mId);
  257. }
  258. return;
  259. }
  260. if (mView != null) {
  261. // if view is not laid out get width/height values in onGlobalLayout
  262. if (mView.getWidth() == 0 && mView.getHeight() == 0) {
  263. mView.getViewTreeObserver().addOnGlobalLayoutListener(this);
  264. } else {
  265. focus();
  266. }
  267. } else {
  268. focus();
  269. }
  270. }
  271. private GuideImageView guideImageView;
  272. private void focus() {
  273. mCalculator = new Calculator(mActivity, mFocusShape, mView, mFocusCircleRadiusFactor,
  274. mFitSystemWindows, mAdjustHeight, mFocusOffsetX);
  275. ViewGroup androidContent = mActivity.findViewById(android.R.id.content);
  276. mRoot = (ViewGroup) androidContent.getParent().getParent();
  277. MyGuideCaseView visibleView = mRoot.findViewWithTag(CONTAINER_TAG);
  278. setClickable(true);
  279. if (visibleView == null) {
  280. setTag(CONTAINER_TAG);
  281. if (mCloseOnTouch) {
  282. setOnClickListener(new OnClickListener() {
  283. @Override
  284. public void onClick(View view) {
  285. // hide();
  286. }
  287. });
  288. }
  289. setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  290. ViewGroup.LayoutParams.MATCH_PARENT));
  291. mRoot.addView(this);
  292. guideImageView = new GuideImageView(mActivity);
  293. guideImageView.setFocusAnimationParameters(mFocusAnimationMaxValue, mFocusAnimationStep);
  294. if (mCalculator.hasFocus()) {
  295. mCenterX = mCalculator.getCircleCenterX();
  296. mCenterY = mCalculator.getCircleCenterY();
  297. }
  298. guideImageView.setParameters(mBackgroundColor, mCalculator);
  299. if (mFocusRectangleWidth > 0 && mFocusRectangleHeight > 0) {
  300. mCalculator.setRectPosition(mFocusPositionX, mFocusPositionY, mFocusRectangleWidth, mFocusRectangleHeight);
  301. }
  302. if (mFocusCircleRadius > 0) {
  303. mCalculator.setCirclePosition(mFocusPositionX, mFocusPositionY, mFocusCircleRadius);
  304. }
  305. setOnTouchListener(new OnTouchListener() {
  306. @Override
  307. public boolean onTouch(View view, MotionEvent motionEvent) {
  308. switch (motionEvent.getAction()){
  309. case MotionEvent.ACTION_UP:
  310. if(Math.abs(motionEvent.getX()-mCenterX)<40&&Math.abs(motionEvent.getY()-mCenterY)<40){
  311. hide();
  312. }
  313. // Log.e("MyTag","坐标:"+motionEvent.getX()+":"+motionEvent.getY());
  314. break;
  315. }
  316. return false;
  317. }
  318. });
  319. // guideImageView.setAnimationEnabled(mFocusAnimationEnabled);
  320. LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  321. ViewGroup.LayoutParams.MATCH_PARENT);
  322. guideImageView.setLayoutParams(params);
  323. if (mFocusBorderColor != 0 && mFocusBorderSize > 0) {
  324. guideImageView.setBorderParameters(mFocusBorderColor, mFocusBorderSize);
  325. }
  326. if (mRoundRectRadius > 0) {
  327. guideImageView.setRoundRectRadius(mRoundRectRadius);
  328. }
  329. addView(guideImageView);
  330. if (mCustomViewRes == 0) {
  331. if (mPictureResId == 0) {
  332. inflateTitleView();
  333. } else {
  334. inflatePicture();
  335. }
  336. } else {
  337. inflateCustomView(mCustomViewRes, mViewInflateListener);
  338. }
  339. startEnterAnimation();
  340. writeShown();
  341. }
  342. }
  343. /**
  344. * Check is GuideCaseView visible
  345. *
  346. * @param activity should be used to find GuideCaseView inside it
  347. */
  348. public static Boolean isVisible(Activity activity) {
  349. ViewGroup androidContent = activity.findViewById(android.R.id.content);
  350. ViewGroup mRoot = (ViewGroup) androidContent.getParent().getParent();
  351. MyGuideCaseView mContainer = mRoot.findViewWithTag(CONTAINER_TAG);
  352. return mContainer != null;
  353. }
  354. /**
  355. * Hide GuideCaseView
  356. *
  357. * @param activity should be used to hide GuideCaseView inside it
  358. */
  359. public static void hideCurrent(Activity activity) {
  360. ViewGroup androidContent = activity.findViewById(android.R.id.content);
  361. ViewGroup mRoot = (ViewGroup) androidContent.getParent().getParent();
  362. MyGuideCaseView mContainer = mRoot.findViewWithTag(CONTAINER_TAG);
  363. mContainer.hide();
  364. }
  365. /**
  366. * Starts enter animation of GuideCaseView
  367. */
  368. private void startEnterAnimation() {
  369. if (mEnterAnimation != null) {
  370. startAnimation(mEnterAnimation);
  371. } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  372. // doCircularEnterAnimation();
  373. Animation aset_4 = new TranslateAnimation(0, 0, -20, 20);
  374. aset_4.setDuration(800);//每次时间
  375. aset_4.setRepeatCount(-1);//重复次数
  376. /**倒序重复REVERSE 正序重复RESTART**/
  377. aset_4.setRepeatMode(Animation.REVERSE);
  378. mImageView.startAnimation(aset_4);
  379. } else {
  380. Animation fadeInAnimation = AnimationUtils.loadAnimation(mActivity, R.anim.gcv_fade_in);
  381. fadeInAnimation.setFillAfter(true);
  382. startAnimation(fadeInAnimation);
  383. }
  384. }
  385. /**
  386. * Hides GuideCaseView with animation
  387. */
  388. public void hide() {
  389. if (mExitAnimation != null) {
  390. startAnimation(mExitAnimation);
  391. } else if (Utils.shouldShowCircularAnimation()) {
  392. doCircularExitAnimation();
  393. } else {
  394. Animation fadeOut = AnimationUtils.loadAnimation(mActivity, R.anim.gcv_fade_out);
  395. fadeOut.setAnimationListener(new Animation.AnimationListener() {
  396. @Override
  397. public void onAnimationStart(Animation animation) {
  398. }
  399. @Override
  400. public void onAnimationEnd(Animation animation) {
  401. removeView();
  402. }
  403. @Override
  404. public void onAnimationRepeat(Animation animation) {
  405. }
  406. });
  407. fadeOut.setFillAfter(true);
  408. startAnimation(fadeOut);
  409. }
  410. }
  411. private View cusView;
  412. /**
  413. * Inflates custom view
  414. *
  415. * @param layout layout for custom view
  416. * @param viewInflateListener inflate listener for custom view
  417. */
  418. private void inflateCustomView(@LayoutRes int layout, OnViewInflateListener viewInflateListener) {
  419. cusView = mActivity.getLayoutInflater().inflate(layout, this, false);
  420. this.addView(cusView);
  421. if (viewInflateListener != null) {
  422. viewInflateListener.onViewInflated(cusView);
  423. }
  424. }
  425. /**
  426. * Inflates title view layout
  427. */
  428. private void inflateTitleView() {
  429. inflateCustomView(R.layout.gcv_layout_title, new OnViewInflateListener() {
  430. @Override
  431. public void onViewInflated(View view) {
  432. TextView textView = view.findViewById(R.id.gcv_title);
  433. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  434. textView.setTextAppearance(mTitleStyle);
  435. } else {
  436. textView.setTextAppearance(mActivity, mTitleStyle);
  437. }
  438. if (mTitleSize != -1) {
  439. textView.setTextSize(mTitleSizeUnit, mTitleSize);
  440. }
  441. textView.setGravity(mTitleGravity);
  442. textView.setTypeface(XUI.getDefaultTypeface());
  443. if (mSpannedTitle != null) {
  444. textView.setText(mSpannedTitle);
  445. } else {
  446. textView.setText(mTitle);
  447. }
  448. }
  449. });
  450. }
  451. private ImageView mImageView;
  452. /**
  453. * Inflates picture view layout
  454. */
  455. private void inflatePicture() {
  456. inflateCustomView(R.layout.gcv_layout_image, new OnViewInflateListener() {
  457. @Override
  458. public void onViewInflated(View view) {
  459. mImageView = view.findViewById(R.id.gcv_img);
  460. mImageView.setImageResource(mPictureResId);
  461. FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mImageView.getLayoutParams();
  462. // params.gravity = mPictureGravity;
  463. params.leftMargin = mCenterX-2*mView.getWidth()/3;
  464. params.topMargin = mCenterY+mView.getHeight()/2;
  465. if (mPictureWidth != 0) {
  466. params.width = mPictureWidth;
  467. }
  468. if (mPictureHeight != 0) {
  469. params.height = mPictureHeight;
  470. }
  471. if (mPictureOffsetY > 0) {
  472. params.topMargin = mPictureOffsetY;
  473. } else {
  474. params.bottomMargin = -mPictureOffsetY;
  475. }
  476. if (mPictureOffsetX > 0) {
  477. params.leftMargin = mPictureOffsetX;
  478. } else {
  479. params.rightMargin = -mPictureOffsetX;
  480. }
  481. mImageView.setLayoutParams(params);
  482. }
  483. });
  484. }
  485. /**
  486. * Circular reveal enter animation
  487. */
  488. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  489. private void doCircularEnterAnimation() {
  490. getViewTreeObserver().addOnPreDrawListener(
  491. new ViewTreeObserver.OnPreDrawListener() {
  492. @Override
  493. public boolean onPreDraw() {
  494. getViewTreeObserver().removeOnPreDrawListener(this);
  495. final int revealRadius = (int) Math.hypot(
  496. getWidth(), getHeight());
  497. int startRadius = 0;
  498. if (mView != null) {
  499. startRadius = mView.getWidth() / 2;
  500. } else if (mFocusCircleRadius > 0 || mFocusRectangleWidth > 0 || mFocusRectangleHeight > 0) {
  501. mCenterX = mFocusPositionX;
  502. mCenterY = mFocusPositionY;
  503. }
  504. Animator enterAnimator = ViewAnimationUtils.createCircularReveal(MyGuideCaseView.this,
  505. mCenterX, mCenterY , startRadius,revealRadius);
  506. enterAnimator.setDuration(mAnimationDuration);
  507. enterAnimator.setInterpolator(AnimationUtils.loadInterpolator(mActivity,
  508. android.R.interpolator.decelerate_cubic));
  509. enterAnimator.start();
  510. return false;
  511. }
  512. });
  513. }
  514. /**
  515. * Circular reveal exit animation
  516. */
  517. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  518. private void doCircularExitAnimation() {
  519. // final int revealRadius = (int) Math.hypot(getWidth(), getHeight());
  520. // Animator exitAnimator = ViewAnimationUtils.createCircularReveal(this,
  521. // mCenterX, mCenterY, 0f,revealRadius);
  522. // exitAnimator.setDuration(mAnimationDuration);
  523. // exitAnimator.setInterpolator(AnimationUtils.loadInterpolator(mActivity,
  524. // android.R.interpolator.decelerate_cubic));
  525. // exitAnimator.addListener(new AnimatorListenerAdapter() {
  526. // @Override
  527. // public void onAnimationEnd(Animator animation) {
  528. // removeView();
  529. //
  530. // }
  531. // });
  532. // exitAnimator.start();
  533. // guideImageView.setOnFinishListener(()->{
  534. // removeView();
  535. // });
  536. // guideImageView.setDraw();//新增代码,用来扩散动画
  537. removeView();
  538. }
  539. /**
  540. * Saves the GuideCaseView id to SharedPreferences that is shown once
  541. */
  542. private void writeShown() {
  543. SharedPreferences.Editor editor = mSharedPreferences.edit();
  544. editor.putBoolean(mId, true);
  545. editor.apply();
  546. }
  547. /**
  548. * Returns if GuideCaseView with given id is shown before
  549. *
  550. * @return true if show before
  551. */
  552. public boolean isShownBefore() {
  553. return mSharedPreferences.getBoolean(mId, false);
  554. }
  555. /**
  556. * Removes GuideCaseView view from activity root view
  557. */
  558. public void removeView() {
  559. mRoot.removeView(this);
  560. if (mDismissListener != null) {
  561. mDismissListener.onDismiss(mId);
  562. }
  563. }
  564. protected DismissListener getDismissListener() {
  565. return mDismissListener;
  566. }
  567. protected void setDismissListener(DismissListener dismissListener) {
  568. mDismissListener = dismissListener;
  569. }
  570. @Override
  571. public void onGlobalLayout() {
  572. mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
  573. focus();
  574. }
  575. public int getFocusCenterX() {
  576. return mCalculator.getCircleCenterX();
  577. }
  578. public int getFocusCenterY() {
  579. return mCalculator.getCircleCenterY();
  580. }
  581. public float getFocusRadius() {
  582. return FocusShape.CIRCLE.equals(mFocusShape) ? mCalculator.circleRadius(0, 1) : 0;
  583. }
  584. public int getFocusWidth() {
  585. return mCalculator.getFocusWidth();
  586. }
  587. public int getFocusHeight() {
  588. return mCalculator.getFocusHeight();
  589. }
  590. /**
  591. * Builder class for {@link MyGuideCaseView}
  592. */
  593. public static class Builder{
  594. private Activity mActivity;
  595. private View mView;
  596. private String mId;
  597. private String mTitle;
  598. private Spanned mSpannedTitle;
  599. private double mFocusCircleRadiusFactor = 1;
  600. private int mBackgroundColor;
  601. private int mFocusBorderColor;
  602. private int mTitleGravity = -1;
  603. private int mTitleSize = -1;
  604. private int mTitleSizeUnit = -1;
  605. private int mTitleStyle;
  606. private int mPictureResId;
  607. private int mPictureWidth;
  608. private int mPictureHeight;
  609. private int mPictureGravity = Gravity.CENTER;
  610. private int mPictureOffSetX;
  611. private int mPictureOffSetY;
  612. private int mCustomViewRes;
  613. private int mRoundRectRadius;
  614. private OnViewInflateListener mViewInflateListener;
  615. private Animation mEnterAnimation, mExitAnimation;
  616. private boolean mCloseOnTouch = true;
  617. private boolean mFitSystemWindows;
  618. private int mAdjustHeight = -1;
  619. private int mFocusOffSetX;
  620. private FocusShape mFocusShape = FocusShape.CIRCLE;
  621. private DismissListener mDismissListener = null;
  622. private int mFocusBorderSize;
  623. private int mFocusPositionX, mFocusPositionY, mFocusCircleRadius, mFocusRectangleWidth, mFocusRectangleHeight;
  624. private boolean mFocusAnimationEnabled = true;
  625. private int mFocusAnimationMaxValue = 20;
  626. private int mFocusAnimationStep = 1;
  627. /**
  628. * Constructor for Builder class
  629. *
  630. * @param activity Activity to show GuideCaseView in
  631. */
  632. public Builder(Activity activity) {
  633. mActivity = activity;
  634. }
  635. /**
  636. * 设置标题文字
  637. *
  638. * @param title title text
  639. * @return Builder
  640. */
  641. public Builder title(String title) {
  642. mTitle = title;
  643. mSpannedTitle = null;
  644. return this;
  645. }
  646. /**
  647. * @param title title text
  648. * @return Builder
  649. */
  650. public Builder title(Spanned title) {
  651. mSpannedTitle = title;
  652. mTitle = null;
  653. return this;
  654. }
  655. /**
  656. * 设置图片资源
  657. *
  658. * @param pictureResId 图片资源Id
  659. * @return Builder
  660. */
  661. public Builder picture(int pictureResId) {
  662. mPictureResId = pictureResId;
  663. return this;
  664. }
  665. /**
  666. * @param pictureResId 图片资源Id
  667. * @param width 图片资源Id
  668. * @param height 图片资源Id
  669. * @return Builder
  670. */
  671. public Builder picture(int pictureResId, int width, int height) {
  672. mPictureResId = pictureResId;
  673. mPictureWidth = width;
  674. mPictureHeight = height;
  675. return this;
  676. }
  677. /**
  678. * @param pictureGravity picture gravity
  679. * @return Builder
  680. */
  681. public Builder pictureGravity(int pictureGravity) {
  682. mPictureGravity = pictureGravity;
  683. return this;
  684. }
  685. /**
  686. * @param pictureGravity picture gravity
  687. * @return Builder
  688. */
  689. public Builder pictureGravity(int pictureGravity, int offsetX, int offsetY) {
  690. mPictureGravity = pictureGravity;
  691. mPictureOffSetX = offsetX;
  692. mPictureOffSetY = offsetY;
  693. return this;
  694. }
  695. /**
  696. * @return Builder
  697. */
  698. public Builder pictureOffSet(int offsetX, int offsetY) {
  699. mPictureOffSetX = offsetX;
  700. mPictureOffSetY = offsetY;
  701. return this;
  702. }
  703. /**
  704. * @return Builder
  705. */
  706. public Builder pictureOffSetX(int offsetX) {
  707. mPictureOffSetX = offsetX;
  708. return this;
  709. }
  710. /**
  711. * @return Builder
  712. */
  713. public Builder pictureOffSetY(int offsetY) {
  714. mPictureOffSetY = offsetY;
  715. return this;
  716. }
  717. /**
  718. * @param style title text style
  719. * @param titleGravity title gravity
  720. * @return Builder
  721. */
  722. public Builder titleStyle(@StyleRes int style, int titleGravity) {
  723. mTitleGravity = titleGravity;
  724. mTitleStyle = style;
  725. return this;
  726. }
  727. /**
  728. * 设置聚焦边框的颜色
  729. *
  730. * @param focusBorderColor Border color for focus shape
  731. * @return Builder
  732. */
  733. public Builder focusBorderColor(int focusBorderColor) {
  734. mFocusBorderColor = focusBorderColor;
  735. return this;
  736. }
  737. /**
  738. * 设置聚焦边框的粗细
  739. *
  740. * @param focusBorderSize Border size for focus shape
  741. * @return Builder
  742. */
  743. public Builder focusBorderSize(int focusBorderSize) {
  744. mFocusBorderSize = focusBorderSize;
  745. return this;
  746. }
  747. /**
  748. * @param titleGravity title gravity
  749. * @return Builder
  750. */
  751. public Builder titleGravity(int titleGravity) {
  752. mTitleGravity = titleGravity;
  753. return this;
  754. }
  755. /**
  756. * the defined text size overrides any defined size in the default or provided style
  757. *
  758. * @param titleSize title size
  759. * @param unit title text unit
  760. * @return Builder
  761. */
  762. public Builder titleSize(int titleSize, int unit) {
  763. mTitleSize = titleSize;
  764. mTitleSizeUnit = unit;
  765. return this;
  766. }
  767. /**
  768. * @param id unique identifier for GuideCaseView
  769. * @return Builder
  770. */
  771. public Builder showOnce(String id) {
  772. mId = id;
  773. return this;
  774. }
  775. /**
  776. * 设置聚焦的控件
  777. *
  778. * @param view view to focus
  779. * @return Builder
  780. */
  781. public Builder focusOn(View view) {
  782. mView = view;
  783. return this;
  784. }
  785. /**
  786. * 设置引导朦层的背景颜色
  787. *
  788. * @param backgroundColor background color of GuideCaseView
  789. * @return Builder
  790. */
  791. public Builder backgroundColor(int backgroundColor) {
  792. mBackgroundColor = backgroundColor;
  793. return this;
  794. }
  795. /**
  796. * @param focusCircleRadiusFactor focus circle radius factor (default value = 1)
  797. * @return Builder
  798. */
  799. public Builder focusCircleRadiusFactor(double focusCircleRadiusFactor) {
  800. mFocusCircleRadiusFactor = focusCircleRadiusFactor;
  801. return this;
  802. }
  803. /**
  804. * 设置自定义引导朦层布局
  805. *
  806. * @param layoutResource custom view layout resource
  807. * @param listener inflate listener for custom view
  808. * @return Builder
  809. */
  810. public Builder customView(@LayoutRes int layoutResource, @Nullable OnViewInflateListener listener) {
  811. mCustomViewRes = layoutResource;
  812. mViewInflateListener = listener;
  813. return this;
  814. }
  815. /**
  816. * 设置进入动画
  817. *
  818. * @param enterAnimation enter animation for GuideCaseView
  819. * @return Builder
  820. */
  821. public Builder enterAnimation(Animation enterAnimation) {
  822. mEnterAnimation = enterAnimation;
  823. return this;
  824. }
  825. /**
  826. * 设置退出动画
  827. *
  828. * @param exitAnimation exit animation for GuideCaseView
  829. * @return Builder
  830. */
  831. public Builder exitAnimation(Animation exitAnimation) {
  832. mExitAnimation = exitAnimation;
  833. return this;
  834. }
  835. /**
  836. * @param closeOnTouch closes on touch if enabled
  837. * @return Builder
  838. */
  839. public Builder closeOnTouch(boolean closeOnTouch) {
  840. mCloseOnTouch = closeOnTouch;
  841. return this;
  842. }
  843. /**
  844. * This should be the same as root view's fitSystemWindows value
  845. *
  846. * @param fitSystemWindows fitSystemWindows value
  847. * @return Builder
  848. */
  849. public Builder fitSystemWindows(boolean fitSystemWindows) {
  850. mFitSystemWindows = fitSystemWindows;
  851. return this;
  852. }
  853. /**
  854. * 自动适应屏幕高度
  855. *
  856. * @return
  857. */
  858. public Builder fitWindowsAuto() {
  859. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  860. mFitSystemWindows = true;
  861. } else {
  862. adjustHeight(0);
  863. }
  864. return this;
  865. }
  866. /**
  867. * 调整的高度
  868. *
  869. * @param adjustHeight
  870. * @return
  871. */
  872. public Builder adjustHeight(int adjustHeight) {
  873. mAdjustHeight = adjustHeight;
  874. return this;
  875. }
  876. /**
  877. * 调整聚焦的X偏移
  878. *
  879. * @return
  880. */
  881. public Builder setFocusOffSetX(int offSetX) {
  882. mFocusOffSetX = offSetX;
  883. return this;
  884. }
  885. /**
  886. * 设置聚焦的形状,默认是圆形
  887. *
  888. * @param focusShape
  889. * @return
  890. */
  891. public Builder focusShape(FocusShape focusShape) {
  892. mFocusShape = focusShape;
  893. return this;
  894. }
  895. /**
  896. * Focus round rectangle at specific position
  897. * 自定义聚焦的矩形区域
  898. *
  899. * @param positionX focus at specific position Y coordinate
  900. * @param positionY focus at specific position circle radius
  901. * @param positionWidth focus at specific position rectangle width
  902. * @param positionHeight focus at specific position rectangle height
  903. * @return Builder
  904. */
  905. public Builder focusRectAtPosition(int positionX, int positionY, int positionWidth, int positionHeight) {
  906. mFocusPositionX = positionX;
  907. mFocusPositionY = positionY;
  908. mFocusRectangleWidth = positionWidth;
  909. mFocusRectangleHeight = positionHeight;
  910. return this;
  911. }
  912. /**
  913. * Focus circle at specific position
  914. * 自定义聚焦的环形区域
  915. *
  916. * @param positionX focus at specific position Y coordinate
  917. * @param positionY focus at specific position circle radius
  918. * @param radius focus at specific position circle radius
  919. * @return Builder
  920. */
  921. public Builder focusCircleAtPosition(int positionX, int positionY, int radius) {
  922. mFocusPositionX = positionX;
  923. mFocusPositionY = positionY;
  924. mFocusCircleRadius = radius;
  925. return this;
  926. }
  927. /**
  928. * 设置引导朦层消失的监听
  929. *
  930. * @param dismissListener the dismiss listener
  931. * @return Builder
  932. */
  933. public Builder dismissListener(DismissListener dismissListener) {
  934. mDismissListener = dismissListener;
  935. return this;
  936. }
  937. public Builder roundRectRadius(int roundRectRadius) {
  938. mRoundRectRadius = roundRectRadius;
  939. return this;
  940. }
  941. /**
  942. * disable Focus Animation
  943. *
  944. * @return Builder
  945. */
  946. public Builder disableFocusAnimation() {
  947. mFocusAnimationEnabled = false;
  948. return this;
  949. }
  950. public Builder focusAnimationMaxValue(int focusAnimationMaxValue) {
  951. mFocusAnimationMaxValue = focusAnimationMaxValue;
  952. return this;
  953. }
  954. public Builder focusAnimationStep(int focusAnimationStep) {
  955. mFocusAnimationStep = focusAnimationStep;
  956. return this;
  957. }
  958. /**
  959. * builds the builder
  960. *
  961. * @return {@link MyGuideCaseView} with given parameters
  962. */
  963. public MyGuideCaseView build() {
  964. return new MyGuideCaseView(this);
  965. }
  966. /**
  967. * 显示
  968. */
  969. public void show() {
  970. build().show();
  971. }
  972. }
  973. }