759 | * False by default. 760 | */ 761 | public Builder setTargetTouchable(boolean targetTouchable) { 762 | showcaseView.setTargetTouchable(targetTouchable); 763 | return this; 764 | } 765 | 766 | /** 767 | * Set whether or not the showcase should dismiss when the target is touched. 768 | *
769 | * True by default.
770 | */
771 | public Builder setDismissOnTargetTouch(boolean dismissOnTargetTouch) {
772 | showcaseView.setDismissOnTargetTouch(dismissOnTargetTouch);
773 | return this;
774 | }
775 |
776 | public Builder setDismissOnTouch(boolean dismissOnTouch) {
777 | showcaseView.setDismissOnTouch(dismissOnTouch);
778 | return this;
779 | }
780 |
781 | public Builder setMaskColour(int maskColour) {
782 | showcaseView.setMaskColour(maskColour);
783 | return this;
784 | }
785 |
786 | public Builder setTitleTextColor(int textColour) {
787 | showcaseView.setTitleTextColor(textColour);
788 | return this;
789 | }
790 |
791 | public Builder setContentTextColor(int textColour) {
792 | showcaseView.setContentTextColor(textColour);
793 | return this;
794 | }
795 |
796 | public Builder setDismissTextColor(int textColour) {
797 | showcaseView.setDismissTextColor(textColour);
798 | return this;
799 | }
800 |
801 | public Builder setDelay(int delayInMillis) {
802 | showcaseView.setDelay(delayInMillis);
803 | return this;
804 | }
805 |
806 | public Builder setFadeDuration(int fadeDurationInMillis) {
807 | showcaseView.setFadeDuration(fadeDurationInMillis);
808 | return this;
809 | }
810 |
811 | public Builder setListener(IShowcaseListener listener) {
812 | showcaseView.addShowcaseListener(listener);
813 | return this;
814 | }
815 |
816 | public Builder singleUse(String showcaseID) {
817 | showcaseView.singleUse(showcaseID);
818 | return this;
819 | }
820 |
821 | public Builder setShape(Shape shape) {
822 | showcaseView.setShape(shape);
823 | return this;
824 | }
825 |
826 | public Builder withCircleShape() {
827 | shapeType = CIRCLE_SHAPE;
828 | return this;
829 | }
830 |
831 | public Builder withOvalShape() {
832 | shapeType = OVAL_SHAPE;
833 | return this;
834 | }
835 |
836 | public Builder withoutShape() {
837 | shapeType = NO_SHAPE;
838 | return this;
839 | }
840 |
841 | public Builder setShapePadding(int padding) {
842 | showcaseView.setShapePadding(padding);
843 | return this;
844 | }
845 |
846 | public Builder setTooltipMargin(int margin) {
847 | showcaseView.setTooltipMargin(margin);
848 | return this;
849 | }
850 |
851 | public Builder withRectangleShape() {
852 | return withRectangleShape(false);
853 | }
854 |
855 | public Builder withRectangleShape(boolean fullWidth) {
856 | this.shapeType = RECTANGLE_SHAPE;
857 | this.fullWidth = fullWidth;
858 | return this;
859 | }
860 |
861 | public Builder renderOverNavigationBar() {
862 | // Note: This only has an effect in Lollipop or above.
863 | showcaseView.setRenderOverNavigationBar(true);
864 | return this;
865 | }
866 |
867 | public Builder useFadeAnimation() {
868 | showcaseView.setUseFadeAnimation(true);
869 | return this;
870 | }
871 |
872 | public MaterialShowcaseView build() {
873 | if (showcaseView.mShape == null) {
874 | switch (shapeType) {
875 | case RECTANGLE_SHAPE: {
876 | showcaseView.setShape(new RectangleShape(showcaseView.mTarget.getBounds(), fullWidth));
877 | break;
878 | }
879 | default:
880 | case CIRCLE_SHAPE: {
881 | showcaseView.setShape(new CircleShape(showcaseView.mTarget));
882 | break;
883 | }
884 | case NO_SHAPE: {
885 | showcaseView.setShape(new NoShape());
886 | break;
887 | }
888 | case OVAL_SHAPE: {
889 | showcaseView.setShape(new OvalShape(showcaseView.mTarget));
890 | break;
891 | }
892 | }
893 | }
894 |
895 | if (showcaseView.mAnimationFactory == null) {
896 | // create our animation factory
897 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !showcaseView.mUseFadeAnimation) {
898 | showcaseView.setAnimationFactory(new CircularRevealAnimationFactory());
899 | } else {
900 | showcaseView.setAnimationFactory(new FadeAnimationFactory());
901 | }
902 | }
903 |
904 | showcaseView.mShape.setPadding(showcaseView.mShapePadding);
905 |
906 | return showcaseView;
907 | }
908 |
909 | public MaterialShowcaseView show() {
910 | build().show(activity);
911 | return showcaseView;
912 | }
913 | }
914 |
915 | private void singleUse(String showcaseID) {
916 | mSingleUse = true;
917 | mPrefsManager = new PrefsManager(getContext(), showcaseID);
918 | }
919 |
920 | public void removeFromWindow() {
921 | if (getParent() != null && getParent() instanceof ViewGroup) {
922 | ((ViewGroup) getParent()).removeView(this);
923 | }
924 |
925 | if (mBitmap != null) {
926 | mBitmap.recycle();
927 | mBitmap = null;
928 | }
929 |
930 | mEraser = null;
931 | mAnimationFactory = null;
932 | mCanvas = null;
933 | mHandler = null;
934 |
935 | getViewTreeObserver().removeGlobalOnLayoutListener(mLayoutListener);
936 | mLayoutListener = null;
937 |
938 | if (mPrefsManager != null)
939 | mPrefsManager.close();
940 |
941 | mPrefsManager = null;
942 |
943 |
944 | }
945 |
946 |
947 | /**
948 | * Reveal the showcaseview. Returns a boolean telling us whether we actually did show anything
949 | *
950 | * @param activity
951 | * @return
952 | */
953 | public boolean show(final Activity activity) {
954 |
955 | /**
956 | * if we're in single use mode and have already shot our bolt then do nothing
957 | */
958 | if (mSingleUse) {
959 | if (mPrefsManager.hasFired()) {
960 | return false;
961 | } else {
962 | mPrefsManager.setFired();
963 | }
964 | }
965 |
966 | ((ViewGroup) activity.getWindow().getDecorView()).addView(this);
967 |
968 | setShouldRender(true);
969 |
970 |
971 | if (toolTip != null) {
972 |
973 | if (!(mTarget instanceof ViewTarget)) {
974 | throw new RuntimeException("The target must be of type: " + ViewTarget.class.getCanonicalName());
975 | }
976 |
977 | ViewTarget viewTarget = (ViewTarget) mTarget;
978 |
979 | toolTip.configureTarget(this, viewTarget.getView());
980 |
981 | }
982 |
983 |
984 | mHandler = new Handler();
985 | mHandler.postDelayed(new Runnable() {
986 | @Override
987 | public void run() {
988 | boolean attached;
989 | // taken from https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/core/src/main/java/androidx/core/view/ViewCompat.java#3310
990 | if (Build.VERSION.SDK_INT >= 19) {
991 | attached = isAttachedToWindow();
992 | } else {
993 | attached = getWindowToken() != null;
994 | }
995 | if (mShouldAnimate && attached) {
996 | fadeIn();
997 | } else {
998 | setVisibility(VISIBLE);
999 | notifyOnDisplayed();
1000 | }
1001 | }
1002 | }, mDelayInMillis);
1003 |
1004 | updateDismissButton();
1005 |
1006 | return true;
1007 | }
1008 |
1009 |
1010 | public void hide() {
1011 |
1012 | /**
1013 | * This flag is used to indicate to onDetachedFromWindow that the showcase view was dismissed purposefully (by the user or programmatically)
1014 | */
1015 | mWasDismissed = true;
1016 |
1017 | if (mShouldAnimate) {
1018 | animateOut();
1019 | } else {
1020 | removeFromWindow();
1021 | }
1022 | }
1023 |
1024 |
1025 | public void skip() {
1026 |
1027 | /**
1028 | * This flag is used to indicate to onDetachedFromWindow that the showcase view was skipped purposefully (by the user or programmatically)
1029 | */
1030 | mWasSkipped = true;
1031 |
1032 | if (mShouldAnimate) {
1033 | animateOut();
1034 | } else {
1035 | removeFromWindow();
1036 | }
1037 | }
1038 |
1039 | public void fadeIn() {
1040 | setVisibility(INVISIBLE);
1041 | mAnimationFactory.animateInView(this, mTarget.getPoint(), mFadeDurationInMillis,
1042 | new IAnimationFactory.AnimationStartListener() {
1043 | @Override
1044 | public void onAnimationStart() {
1045 | setVisibility(View.VISIBLE);
1046 | notifyOnDisplayed();
1047 | }
1048 | }
1049 | );
1050 | }
1051 |
1052 | public void animateOut() {
1053 |
1054 | mAnimationFactory.animateOutView(this, mTarget.getPoint(), mFadeDurationInMillis, new IAnimationFactory.AnimationEndListener() {
1055 | @Override
1056 | public void onAnimationEnd() {
1057 | setVisibility(INVISIBLE);
1058 | removeFromWindow();
1059 | }
1060 | });
1061 | }
1062 |
1063 | public void resetSingleUse() {
1064 | if (mSingleUse && mPrefsManager != null) mPrefsManager.resetShowcase();
1065 | }
1066 |
1067 | /**
1068 | * Static helper method for resetting single use flag
1069 | *
1070 | * @param context
1071 | * @param showcaseID
1072 | */
1073 | public static void resetSingleUse(Context context, String showcaseID) {
1074 | PrefsManager.resetShowcase(context, showcaseID);
1075 | }
1076 |
1077 | /**
1078 | * Static helper method for resetting all single use flags
1079 | *
1080 | * @param context
1081 | */
1082 | public static void resetAll(Context context) {
1083 | PrefsManager.resetAll(context);
1084 | }
1085 |
1086 |
1087 | public int getSoftButtonsBarSizePort() {
1088 |
1089 | int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
1090 | if (resourceId > 0) {
1091 | return getResources().getDimensionPixelSize(resourceId);
1092 | }
1093 |
1094 | return 0;
1095 |
1096 | }
1097 |
1098 | private void setRenderOverNavigationBar(boolean mRenderOverNav) {
1099 | this.mRenderOverNav = mRenderOverNav;
1100 | }
1101 | }
1102 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/PrefsManager.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 |
7 | public class PrefsManager {
8 |
9 | public static int SEQUENCE_NEVER_STARTED = 0;
10 | public static int SEQUENCE_FINISHED = -1;
11 |
12 |
13 | private static final String PREFS_NAME = "material_showcaseview_prefs";
14 | private static final String STATUS = "status_";
15 | private String showcaseID = null;
16 | private Context context;
17 |
18 | public PrefsManager(Context context, String showcaseID) {
19 | this.context = context;
20 | this.showcaseID = showcaseID;
21 | }
22 |
23 |
24 | /***
25 | * METHODS FOR INDIVIDUAL SHOWCASE VIEWS
26 | */
27 | boolean hasFired() {
28 | int status = getSequenceStatus();
29 | return (status == SEQUENCE_FINISHED);
30 | }
31 |
32 | void setFired() {
33 | setSequenceStatus(SEQUENCE_FINISHED);
34 | }
35 |
36 | /***
37 | * METHODS FOR SHOWCASE SEQUENCES
38 | */
39 | int getSequenceStatus() {
40 | return context
41 | .getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
42 | .getInt(STATUS + showcaseID, SEQUENCE_NEVER_STARTED);
43 |
44 | }
45 |
46 | void setSequenceStatus(int status) {
47 | SharedPreferences internal = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
48 | internal.edit().putInt(STATUS + showcaseID, status).apply();
49 | }
50 |
51 |
52 | public void resetShowcase() {
53 | resetShowcase(context, showcaseID);
54 | }
55 |
56 | static void resetShowcase(Context context, String showcaseID) {
57 | SharedPreferences internal = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
58 | internal.edit().putInt(STATUS + showcaseID, SEQUENCE_NEVER_STARTED).apply();
59 | }
60 |
61 | public static void resetAll(Context context) {
62 | SharedPreferences internal = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
63 | internal.edit().clear().apply();
64 | }
65 |
66 | public void close() {
67 | context = null;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.Typeface;
5 |
6 | import uk.co.deanwild.materialshowcaseview.shape.CircleShape;
7 | import uk.co.deanwild.materialshowcaseview.shape.Shape;
8 |
9 |
10 | public class ShowcaseConfig {
11 |
12 | public static final String DEFAULT_MASK_COLOUR = "#dd335075";
13 |
14 | private long mDelay = -1;
15 | private int mMaskColour;
16 | private Typeface mDismissTextStyle;
17 |
18 | private int mContentTextColor;
19 | private int mDismissTextColor;
20 | private long mFadeDuration = -1;
21 | private Shape mShape = null;
22 | private int mShapePadding = -1;
23 | private Boolean renderOverNav;
24 |
25 | public ShowcaseConfig() {
26 | mMaskColour = Color.parseColor(ShowcaseConfig.DEFAULT_MASK_COLOUR);
27 | mContentTextColor = Color.parseColor("#ffffff");
28 | mDismissTextColor = Color.parseColor("#ffffff");
29 | }
30 |
31 | public long getDelay() {
32 | return mDelay;
33 | }
34 |
35 | public void setDelay(long delay) {
36 | this.mDelay = delay;
37 | }
38 |
39 | public int getMaskColor() {
40 | return mMaskColour;
41 | }
42 |
43 | public void setMaskColor(int maskColor) {
44 | mMaskColour = maskColor;
45 | }
46 |
47 | public int getContentTextColor() {
48 | return mContentTextColor;
49 | }
50 |
51 | public void setContentTextColor(int mContentTextColor) {
52 | this.mContentTextColor = mContentTextColor;
53 | }
54 |
55 | public int getDismissTextColor() {
56 | return mDismissTextColor;
57 | }
58 |
59 | public void setDismissTextColor(int dismissTextColor) {
60 | this.mDismissTextColor = dismissTextColor;
61 | }
62 |
63 | public Typeface getDismissTextStyle() {
64 | return mDismissTextStyle;
65 | }
66 |
67 | public void setDismissTextStyle(Typeface dismissTextStyle) {
68 | this.mDismissTextStyle = dismissTextStyle;
69 | }
70 |
71 | public long getFadeDuration() {
72 | return mFadeDuration;
73 | }
74 |
75 | public void setFadeDuration(long fadeDuration) {
76 | this.mFadeDuration = fadeDuration;
77 | }
78 |
79 | public Shape getShape() {
80 | return mShape;
81 | }
82 |
83 | public void setShape(Shape shape) {
84 | this.mShape = shape;
85 | }
86 |
87 | public void setShapePadding(int padding) {
88 | this.mShapePadding = padding;
89 | }
90 |
91 | public int getShapePadding() {
92 | return mShapePadding;
93 | }
94 |
95 | public Boolean getRenderOverNavigationBar() {
96 | return renderOverNav;
97 | }
98 |
99 | public void setRenderOverNavigationBar(boolean renderOverNav) {
100 | this.renderOverNav = renderOverNav;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseTooltip.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview;
2 |
3 |
4 | import android.animation.Animator;
5 | import android.animation.AnimatorListenerAdapter;
6 | import android.app.Activity;
7 | import android.app.DialogFragment;
8 | import android.app.Fragment;
9 | import android.content.Context;
10 | import android.content.ContextWrapper;
11 | import android.graphics.Canvas;
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.graphics.Path;
15 | import android.graphics.Point;
16 | import android.graphics.Rect;
17 | import android.graphics.RectF;
18 | import android.graphics.Typeface;
19 |
20 |
21 | import android.text.Html;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.view.ViewTreeObserver;
25 | import android.view.Window;
26 | import android.widget.FrameLayout;
27 | import android.widget.TextView;
28 |
29 | import java.util.Arrays;
30 |
31 | /**
32 | * Base on original code by florentchampigny
33 | * https://github.com/florent37/ViewTooltip
34 | */
35 |
36 | public class ShowcaseTooltip {
37 |
38 | private View rootView;
39 | private View view;
40 | private TooltipView tooltip_view;
41 |
42 |
43 | private ShowcaseTooltip(Context context){
44 | MyContext myContext = new MyContext(getActivityContext(context));
45 | this.tooltip_view = new TooltipView(myContext.getContext());
46 | }
47 |
48 | public static ShowcaseTooltip build(Context context) {
49 | return new ShowcaseTooltip(context);
50 | }
51 |
52 | public void configureTarget(ViewGroup rootView, View view) {
53 | this.rootView = rootView;
54 | this.view = view;
55 | }
56 |
57 | private static Activity getActivityContext(Context context) {
58 | while (context instanceof ContextWrapper) {
59 | if (context instanceof Activity) {
60 | return (Activity) context;
61 | }
62 | context = ((ContextWrapper) context).getBaseContext();
63 | }
64 | return null;
65 | }
66 |
67 | public ShowcaseTooltip position(Position position) {
68 | this.tooltip_view.setPosition(position);
69 | return this;
70 | }
71 |
72 | public ShowcaseTooltip customView(View customView) {
73 | this.tooltip_view.setCustomView(customView);
74 | return this;
75 | }
76 |
77 | public ShowcaseTooltip customView(int viewId) {
78 | this.tooltip_view.setCustomView(((Activity) view.getContext()).findViewById(viewId));
79 | return this;
80 | }
81 |
82 | public ShowcaseTooltip arrowWidth(int arrowWidth) {
83 | this.tooltip_view.setArrowWidth(arrowWidth);
84 | return this;
85 | }
86 |
87 | public ShowcaseTooltip arrowHeight(int arrowHeight) {
88 | this.tooltip_view.setArrowHeight(arrowHeight);
89 | return this;
90 | }
91 |
92 | public ShowcaseTooltip arrowSourceMargin(int arrowSourceMargin) {
93 | this.tooltip_view.setArrowSourceMargin(arrowSourceMargin);
94 | return this;
95 | }
96 |
97 | public ShowcaseTooltip arrowTargetMargin(int arrowTargetMargin) {
98 | this.tooltip_view.setArrowTargetMargin(arrowTargetMargin);
99 | return this;
100 | }
101 |
102 | public ShowcaseTooltip align(ALIGN align) {
103 | this.tooltip_view.setAlign(align);
104 | return this;
105 | }
106 |
107 | public TooltipView show(final int margin) {
108 | final Context activityContext = tooltip_view.getContext();
109 | if (activityContext != null && activityContext instanceof Activity) {
110 | final ViewGroup decorView = rootView != null ?
111 | (ViewGroup) rootView :
112 | (ViewGroup) ((Activity) activityContext).getWindow().getDecorView();
113 |
114 | view.postDelayed(new Runnable() {
115 | @Override
116 | public void run() {
117 | final Rect rect = new Rect();
118 | view.getGlobalVisibleRect(rect);
119 |
120 | final Rect rootGlobalRect = new Rect();
121 | final Point rootGlobalOffset = new Point();
122 | decorView.getGlobalVisibleRect(rootGlobalRect, rootGlobalOffset);
123 |
124 | int[] location = new int[2];
125 | view.getLocationOnScreen(location);
126 | rect.left = location[0];
127 | if (rootGlobalOffset != null) {
128 | rect.top -= rootGlobalOffset.y;
129 | rect.bottom -= rootGlobalOffset.y;
130 | rect.left -= rootGlobalOffset.x;
131 | rect.right -= rootGlobalOffset.x;
132 | }
133 |
134 | // fixes bottom mode
135 | rect.top -= margin;
136 |
137 | // fixes top mode
138 | rect.bottom += margin;
139 |
140 | decorView.addView(tooltip_view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
141 |
142 | tooltip_view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
143 | @Override
144 | public boolean onPreDraw() {
145 |
146 | tooltip_view.setup(rect, decorView.getWidth());
147 |
148 | tooltip_view.getViewTreeObserver().removeOnPreDrawListener(this);
149 |
150 | return false;
151 | }
152 | });
153 | }
154 | }, 100);
155 | }
156 | return tooltip_view;
157 | }
158 |
159 | public ShowcaseTooltip color(int color) {
160 | this.tooltip_view.setColor(color);
161 | return this;
162 | }
163 |
164 | public ShowcaseTooltip color(Paint paint) {
165 | this.tooltip_view.setPaint(paint);
166 | return this;
167 | }
168 |
169 | public ShowcaseTooltip onDisplay(ListenerDisplay listener) {
170 | this.tooltip_view.setListenerDisplay(listener);
171 | return this;
172 | }
173 |
174 | public ShowcaseTooltip padding(int left, int top, int right, int bottom) {
175 | this.tooltip_view.paddingTop = top;
176 | this.tooltip_view.paddingBottom = bottom;
177 | this.tooltip_view.paddingLeft = left;
178 | this.tooltip_view.paddingRight = right;
179 | return this;
180 | }
181 |
182 | public ShowcaseTooltip animation(TooltipAnimation tooltipAnimation) {
183 | this.tooltip_view.setTooltipAnimation(tooltipAnimation);
184 | return this;
185 | }
186 |
187 | public ShowcaseTooltip text(String text) {
188 | this.tooltip_view.setText(text);
189 | return this;
190 | }
191 |
192 | public ShowcaseTooltip text(int text) {
193 | this.tooltip_view.setText(text);
194 | return this;
195 | }
196 |
197 | public ShowcaseTooltip corner(int corner) {
198 | this.tooltip_view.setCorner(corner);
199 | return this;
200 | }
201 |
202 | public ShowcaseTooltip textColor(int textColor) {
203 | this.tooltip_view.setTextColor(textColor);
204 | return this;
205 | }
206 |
207 | public ShowcaseTooltip textTypeFace(Typeface typeface) {
208 | this.tooltip_view.setTextTypeFace(typeface);
209 | return this;
210 | }
211 |
212 | public ShowcaseTooltip textSize(int unit, float textSize) {
213 | this.tooltip_view.setTextSize(unit, textSize);
214 | return this;
215 | }
216 |
217 | public ShowcaseTooltip setTextGravity(int textGravity) {
218 | this.tooltip_view.setTextGravity(textGravity);
219 | return this;
220 | }
221 |
222 | public ShowcaseTooltip distanceWithView(int distance) {
223 | this.tooltip_view.setDistanceWithView(distance);
224 | return this;
225 | }
226 |
227 | public ShowcaseTooltip border(int color, float width) {
228 | Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
229 | borderPaint.setColor(color);
230 | borderPaint.setStyle(Paint.Style.STROKE);
231 | borderPaint.setStrokeWidth(width);
232 | this.tooltip_view.setBorderPaint(borderPaint);
233 | return this;
234 | }
235 |
236 | public enum Position {
237 | LEFT,
238 | RIGHT,
239 | TOP,
240 | BOTTOM,
241 | }
242 |
243 | public enum ALIGN {
244 | START,
245 | CENTER,
246 | END
247 | }
248 |
249 | public interface TooltipAnimation {
250 | void animateEnter(View view, Animator.AnimatorListener animatorListener);
251 |
252 | void animateExit(View view, Animator.AnimatorListener animatorListener);
253 | }
254 |
255 | public interface ListenerDisplay {
256 | void onDisplay(View view);
257 | }
258 |
259 | public static class FadeTooltipAnimation implements TooltipAnimation {
260 |
261 | private long fadeDuration = 400;
262 |
263 | public FadeTooltipAnimation() {
264 | }
265 |
266 | public FadeTooltipAnimation(long fadeDuration) {
267 | this.fadeDuration = fadeDuration;
268 | }
269 |
270 | @Override
271 | public void animateEnter(View view, Animator.AnimatorListener animatorListener) {
272 | view.setAlpha(0);
273 | view.animate().alpha(1).setDuration(fadeDuration).setListener(animatorListener);
274 | }
275 |
276 | @Override
277 | public void animateExit(View view, Animator.AnimatorListener animatorListener) {
278 | view.animate().alpha(0).setDuration(fadeDuration).setListener(animatorListener);
279 | }
280 | }
281 |
282 | public static class TooltipView extends FrameLayout {
283 |
284 | private static final int MARGIN_SCREEN_BORDER_TOOLTIP = 30;
285 | private int arrowHeight = 15;
286 | private int arrowWidth = 15;
287 | private int arrowSourceMargin = 0;
288 | private int arrowTargetMargin = 0;
289 | protected View childView;
290 | private int color = Color.parseColor("#FFFFFF");
291 | private Path bubblePath;
292 | private Paint bubblePaint;
293 | private Paint borderPaint;
294 | private Position position = Position.BOTTOM;
295 | private ALIGN align = ALIGN.CENTER;
296 |
297 | private ListenerDisplay listenerDisplay;
298 |
299 | private TooltipAnimation tooltipAnimation = new FadeTooltipAnimation();
300 |
301 | private int corner = 30;
302 |
303 | private int paddingTop = 20;
304 | private int paddingBottom = 30;
305 | private int paddingRight = 60;
306 | private int paddingLeft = 60;
307 |
308 | private Rect viewRect;
309 | private int distanceWithView = 0;
310 |
311 | public TooltipView(Context context) {
312 | super(context);
313 |
314 | setWillNotDraw(false);
315 |
316 | this.childView = new TextView(context);
317 | ((TextView) childView).setTextColor(Color.BLACK);
318 | addView(childView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
319 | childView.setPadding(0, 0, 0, 0);
320 |
321 | bubblePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
322 | bubblePaint.setColor(color);
323 | bubblePaint.setStyle(Paint.Style.FILL);
324 |
325 | borderPaint = null;
326 |
327 | setLayerType(LAYER_TYPE_SOFTWARE, bubblePaint);
328 |
329 | }
330 |
331 | public void setCustomView(View customView) {
332 | this.removeView(childView);
333 | this.childView = customView;
334 | addView(childView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
335 | }
336 |
337 | public void setColor(int color) {
338 | this.color = color;
339 | bubblePaint.setColor(color);
340 | postInvalidate();
341 | }
342 |
343 | public void setPaint(Paint paint) {
344 | bubblePaint = paint;
345 | setLayerType(LAYER_TYPE_SOFTWARE, paint);
346 | postInvalidate();
347 | }
348 |
349 | public void setPosition(Position position) {
350 | this.position = position;
351 | switch (position) {
352 | case TOP:
353 | setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom + arrowHeight);
354 | break;
355 | case BOTTOM:
356 | setPadding(paddingLeft, paddingTop + arrowHeight, paddingRight, paddingBottom);
357 | break;
358 | case LEFT:
359 | setPadding(paddingLeft, paddingTop, paddingRight + arrowHeight, paddingBottom);
360 | break;
361 | case RIGHT:
362 | setPadding(paddingLeft + arrowHeight, paddingTop, paddingRight, paddingBottom);
363 | break;
364 | }
365 | postInvalidate();
366 | }
367 |
368 | public void setAlign(ALIGN align) {
369 | this.align = align;
370 | postInvalidate();
371 | }
372 |
373 | public void setText(String text) {
374 | if (childView instanceof TextView) {
375 | ((TextView) this.childView).setText(Html.fromHtml(text));
376 | }
377 | postInvalidate();
378 | }
379 |
380 | public void setText(int text) {
381 | if (childView instanceof TextView) {
382 | ((TextView) this.childView).setText(text);
383 | }
384 | postInvalidate();
385 | }
386 |
387 | public void setTextColor(int textColor) {
388 | if (childView instanceof TextView) {
389 | ((TextView) this.childView).setTextColor(textColor);
390 | }
391 | postInvalidate();
392 | }
393 |
394 | public int getArrowHeight() {
395 | return arrowHeight;
396 | }
397 |
398 | public void setArrowHeight(int arrowHeight) {
399 | this.arrowHeight = arrowHeight;
400 | postInvalidate();
401 | }
402 |
403 | public int getArrowWidth() {
404 | return arrowWidth;
405 | }
406 |
407 | public void setArrowWidth(int arrowWidth) {
408 | this.arrowWidth = arrowWidth;
409 | postInvalidate();
410 | }
411 |
412 | public int getArrowSourceMargin() {
413 | return arrowSourceMargin;
414 | }
415 |
416 | public void setArrowSourceMargin(int arrowSourceMargin) {
417 | this.arrowSourceMargin = arrowSourceMargin;
418 | postInvalidate();
419 | }
420 |
421 | public int getArrowTargetMargin() {
422 | return arrowTargetMargin;
423 | }
424 |
425 | public void setArrowTargetMargin(int arrowTargetMargin) {
426 | this.arrowTargetMargin = arrowTargetMargin;
427 | postInvalidate();
428 | }
429 |
430 | public void setTextTypeFace(Typeface textTypeFace) {
431 | if (childView instanceof TextView) {
432 | ((TextView) this.childView).setTypeface(textTypeFace);
433 | }
434 | postInvalidate();
435 | }
436 |
437 | public void setTextSize(int unit, float size) {
438 | if (childView instanceof TextView) {
439 | ((TextView) this.childView).setTextSize(unit, size);
440 | }
441 | postInvalidate();
442 | }
443 |
444 | public void setTextGravity(int textGravity) {
445 | if (childView instanceof TextView) {
446 | ((TextView) this.childView).setGravity(textGravity);
447 | }
448 | postInvalidate();
449 | }
450 |
451 | public void setCorner(int corner) {
452 | this.corner = corner;
453 | }
454 |
455 | @Override
456 | protected void onSizeChanged(int width, int height, int oldw, int oldh) {
457 | super.onSizeChanged(width, height, oldw, oldh);
458 |
459 | bubblePath = drawBubble(new RectF(0, 0, width, height), corner, corner, corner, corner);
460 | }
461 |
462 | @Override
463 | protected void onDraw(Canvas canvas) {
464 | super.onDraw(canvas);
465 |
466 | if (bubblePath != null) {
467 | canvas.drawPath(bubblePath, bubblePaint);
468 | if (borderPaint != null) {
469 | canvas.drawPath(bubblePath, borderPaint);
470 | }
471 | }
472 | }
473 |
474 | public void setListenerDisplay(ListenerDisplay listener) {
475 | this.listenerDisplay = listener;
476 | }
477 |
478 | public void setTooltipAnimation(TooltipAnimation tooltipAnimation) {
479 | this.tooltipAnimation = tooltipAnimation;
480 | }
481 |
482 | protected void startEnterAnimation() {
483 | tooltipAnimation.animateEnter(this, new AnimatorListenerAdapter() {
484 | @Override
485 | public void onAnimationEnd(Animator animation) {
486 | super.onAnimationEnd(animation);
487 | if (listenerDisplay != null) {
488 | listenerDisplay.onDisplay(TooltipView.this);
489 | }
490 | }
491 | });
492 | }
493 |
494 | public void setupPosition(Rect rect) {
495 |
496 | int x, y;
497 |
498 | if (position == Position.LEFT || position == Position.RIGHT) {
499 | if (position == Position.LEFT) {
500 | x = rect.left - getWidth() - distanceWithView;
501 | } else {
502 | x = rect.right + distanceWithView;
503 | }
504 | y = rect.top + getAlignOffset(getHeight(), rect.height());
505 | } else {
506 | if (position == Position.BOTTOM) {
507 | y = rect.bottom + distanceWithView;
508 | } else { // top
509 | y = rect.top - getHeight() - distanceWithView;
510 | }
511 | x = rect.left + getAlignOffset(getWidth(), rect.width());
512 | }
513 |
514 | setTranslationX(x);
515 | setTranslationY(y);
516 | }
517 |
518 | private int getAlignOffset(int myLength, int hisLength) {
519 | switch (align) {
520 | case END:
521 | return hisLength - myLength;
522 | case CENTER:
523 | return (hisLength - myLength) / 2;
524 | }
525 | return 0;
526 | }
527 |
528 | private Path drawBubble(RectF myRect, float topLeftDiameter, float topRightDiameter, float bottomRightDiameter, float bottomLeftDiameter) {
529 | final Path path = new Path();
530 |
531 | if (viewRect == null)
532 | return path;
533 |
534 | topLeftDiameter = topLeftDiameter < 0 ? 0 : topLeftDiameter;
535 | topRightDiameter = topRightDiameter < 0 ? 0 : topRightDiameter;
536 | bottomLeftDiameter = bottomLeftDiameter < 0 ? 0 : bottomLeftDiameter;
537 | bottomRightDiameter = bottomRightDiameter < 0 ? 0 : bottomRightDiameter;
538 |
539 | float spacingLeft = 30;
540 | final float spacingTop = this.position == Position.BOTTOM ? arrowHeight : 0;
541 | float spacingRight = 30;
542 | final float spacingBottom = this.position == Position.TOP ? arrowHeight : 0;
543 |
544 | final float left = spacingLeft + myRect.left;
545 | final float top = spacingTop + myRect.top;
546 | final float right = myRect.right - spacingRight;
547 | final float bottom = myRect.bottom - spacingBottom;
548 | final float centerX = viewRect.centerX() - getX();
549 |
550 | final float arrowSourceX = (Arrays.asList(Position.TOP, Position.BOTTOM).contains(this.position))
551 | ? centerX + arrowSourceMargin
552 | : centerX;
553 | final float arrowTargetX = (Arrays.asList(Position.TOP, Position.BOTTOM).contains(this.position))
554 | ? centerX + arrowTargetMargin
555 | : centerX;
556 | final float arrowSourceY = (Arrays.asList(Position.RIGHT, Position.LEFT).contains(this.position))
557 | ? bottom / 2f - arrowSourceMargin
558 | : bottom / 2f;
559 | final float arrowTargetY = (Arrays.asList(Position.RIGHT, Position.LEFT).contains(this.position))
560 | ? bottom / 2f - arrowTargetMargin
561 | : bottom / 2f;
562 |
563 | path.moveTo(left + topLeftDiameter / 2f, top);
564 | //LEFT, TOP
565 |
566 | if (position == Position.BOTTOM) {
567 | path.lineTo(arrowSourceX - arrowWidth, top);
568 | path.lineTo(arrowTargetX, myRect.top);
569 | path.lineTo(arrowSourceX + arrowWidth, top);
570 | }
571 | path.lineTo(right - topRightDiameter / 2f, top);
572 |
573 | path.quadTo(right, top, right, top + topRightDiameter / 2);
574 | //RIGHT, TOP
575 |
576 | if (position == Position.LEFT) {
577 | path.lineTo(right, arrowSourceY - arrowWidth);
578 | path.lineTo(myRect.right, arrowTargetY);
579 | path.lineTo(right, arrowSourceY + arrowWidth);
580 | }
581 | path.lineTo(right, bottom - bottomRightDiameter / 2);
582 |
583 | path.quadTo(right, bottom, right - bottomRightDiameter / 2, bottom);
584 | //RIGHT, BOTTOM
585 |
586 | if (position == Position.TOP) {
587 | path.lineTo(arrowSourceX + arrowWidth, bottom);
588 | path.lineTo(arrowTargetX, myRect.bottom);
589 | path.lineTo(arrowSourceX - arrowWidth, bottom);
590 | }
591 | path.lineTo(left + bottomLeftDiameter / 2, bottom);
592 |
593 | path.quadTo(left, bottom, left, bottom - bottomLeftDiameter / 2);
594 | //LEFT, BOTTOM
595 |
596 | if (position == Position.RIGHT) {
597 | path.lineTo(left, arrowSourceY + arrowWidth);
598 | path.lineTo(myRect.left, arrowTargetY);
599 | path.lineTo(left, arrowSourceY - arrowWidth);
600 | }
601 | path.lineTo(left, top + topLeftDiameter / 2);
602 |
603 | path.quadTo(left, top, left + topLeftDiameter / 2, top);
604 |
605 | path.close();
606 |
607 | return path;
608 | }
609 |
610 | public boolean adjustSize(Rect rect, int screenWidth) {
611 |
612 | final Rect r = new Rect();
613 | getGlobalVisibleRect(r);
614 |
615 | boolean changed = false;
616 | final ViewGroup.LayoutParams layoutParams = getLayoutParams();
617 | if (position == Position.LEFT && getWidth() > rect.left) {
618 | layoutParams.width = rect.left - MARGIN_SCREEN_BORDER_TOOLTIP - distanceWithView;
619 | changed = true;
620 | } else if (position == Position.RIGHT && rect.right + getWidth() > screenWidth) {
621 | layoutParams.width = screenWidth - rect.right - MARGIN_SCREEN_BORDER_TOOLTIP - distanceWithView;
622 | changed = true;
623 | } else if (position == Position.TOP || position == Position.BOTTOM) {
624 | int adjustedLeft = rect.left;
625 | int adjustedRight = rect.right;
626 |
627 | if ((rect.centerX() + getWidth() / 2f) > screenWidth) {
628 | float diff = (rect.centerX() + getWidth() / 2f) - screenWidth;
629 |
630 | adjustedLeft -= diff;
631 | adjustedRight -= diff;
632 |
633 | setAlign(ALIGN.CENTER);
634 | changed = true;
635 | } else if ((rect.centerX() - getWidth() / 2f) < 0) {
636 | float diff = -(rect.centerX() - getWidth() / 2f);
637 |
638 | adjustedLeft += diff;
639 | adjustedRight += diff;
640 |
641 | setAlign(ALIGN.CENTER);
642 | changed = true;
643 | }
644 |
645 | if (adjustedLeft < 0) {
646 | adjustedLeft = 0;
647 | }
648 |
649 | if (adjustedRight > screenWidth) {
650 | adjustedRight = screenWidth;
651 | }
652 |
653 | rect.left = adjustedLeft;
654 | rect.right = adjustedRight;
655 | }
656 |
657 | setLayoutParams(layoutParams);
658 | postInvalidate();
659 | return changed;
660 | }
661 |
662 | private void onSetup(Rect myRect) {
663 | setupPosition(myRect);
664 | bubblePath = drawBubble(new RectF(0, 0, getWidth(), getHeight()), corner, corner, corner, corner);
665 | startEnterAnimation();
666 | }
667 |
668 | public void setup(final Rect viewRect, int screenWidth) {
669 | this.viewRect = new Rect(viewRect);
670 | final Rect myRect = new Rect(viewRect);
671 |
672 | final boolean changed = adjustSize(myRect, screenWidth);
673 | if (!changed) {
674 | onSetup(myRect);
675 | } else {
676 | getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
677 | @Override
678 | public boolean onPreDraw() {
679 | onSetup(myRect);
680 | getViewTreeObserver().removeOnPreDrawListener(this);
681 | return false;
682 | }
683 | });
684 | }
685 | }
686 |
687 | public void removeNow() {
688 | if (getParent() != null) {
689 | final ViewGroup parent = ((ViewGroup) getParent());
690 | parent.removeView(TooltipView.this);
691 | }
692 | }
693 |
694 | public void closeNow() {
695 | removeNow();
696 | }
697 |
698 | public void setDistanceWithView(int distanceWithView) {
699 | this.distanceWithView = distanceWithView;
700 | }
701 |
702 | public void setBorderPaint(Paint borderPaint) {
703 | this.borderPaint = borderPaint;
704 | postInvalidate();
705 | }
706 | }
707 |
708 | public static class MyContext {
709 | private Fragment fragment;
710 | private Context context;
711 | private Activity activity;
712 |
713 | public MyContext(Activity activity) {
714 | this.activity = activity;
715 | }
716 |
717 | public MyContext(Fragment fragment) {
718 | this.fragment = fragment;
719 | }
720 |
721 | public MyContext(Context context) {
722 | this.context = context;
723 | }
724 |
725 | public Context getContext() {
726 | if (activity != null) {
727 | return activity;
728 | } else {
729 | return ((Context) fragment.getActivity());
730 | }
731 | }
732 |
733 | public Activity getActivity() {
734 | if (activity != null) {
735 | return activity;
736 | } else {
737 | return fragment.getActivity();
738 | }
739 | }
740 |
741 |
742 | public Window getWindow() {
743 | if (activity != null) {
744 | return activity.getWindow();
745 | } else {
746 | if (fragment instanceof DialogFragment) {
747 | return ((DialogFragment) fragment).getDialog().getWindow();
748 | }
749 | return fragment.getActivity().getWindow();
750 | }
751 | }
752 | }
753 | }
754 |
755 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.shape;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Rect;
6 |
7 | import uk.co.deanwild.materialshowcaseview.target.Target;
8 |
9 | /**
10 | * Circular shape for target.
11 | */
12 | public class CircleShape implements Shape {
13 |
14 | private int radius = 200;
15 | private boolean adjustToTarget = true;
16 | private int padding;
17 |
18 | public CircleShape() {
19 | }
20 |
21 | public CircleShape(int radius) {
22 | this.radius = radius;
23 | }
24 |
25 | public CircleShape(Rect bounds) {
26 | this(getPreferredRadius(bounds));
27 | }
28 |
29 | public CircleShape(Target target) {
30 | this(target.getBounds());
31 | }
32 |
33 | public void setAdjustToTarget(boolean adjustToTarget) {
34 | this.adjustToTarget = adjustToTarget;
35 | }
36 |
37 | public boolean isAdjustToTarget() {
38 | return adjustToTarget;
39 | }
40 |
41 | public int getRadius() {
42 | return radius;
43 | }
44 |
45 | public void setRadius(int radius) {
46 | this.radius = radius;
47 | }
48 |
49 | @Override
50 | public void draw(Canvas canvas, Paint paint, int x, int y) {
51 | if (radius > 0) {
52 | canvas.drawCircle(x, y, radius + padding, paint);
53 | }
54 | }
55 |
56 | @Override
57 | public void updateTarget(Target target) {
58 | if (adjustToTarget)
59 | radius = getPreferredRadius(target.getBounds());
60 | }
61 |
62 | @Override
63 | public int getTotalRadius() {
64 | return radius + padding;
65 | }
66 |
67 | @Override
68 | public void setPadding(int padding) {
69 | this.padding = padding;
70 | }
71 |
72 | @Override
73 | public int getWidth() {
74 | return radius * 2;
75 | }
76 |
77 | @Override
78 | public int getHeight() {
79 | return radius * 2;
80 | }
81 |
82 | public static int getPreferredRadius(Rect bounds) {
83 | return Math.max(bounds.width(), bounds.height()) / 2;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/NoShape.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.shape;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 |
6 | import uk.co.deanwild.materialshowcaseview.target.Target;
7 |
8 | /**
9 | * A Shape implementation that draws nothing.
10 | */
11 | public class NoShape implements Shape {
12 |
13 | @Override
14 | public void updateTarget(Target target) {
15 | // do nothing
16 | }
17 |
18 | @Override
19 | public int getTotalRadius() {
20 | return 0;
21 | }
22 |
23 | @Override
24 | public void setPadding(int padding) {
25 | // do nothing
26 | }
27 |
28 | @Override
29 | public void draw(Canvas canvas, Paint paint, int x, int y) {
30 | // do nothing
31 | }
32 |
33 | @Override
34 | public int getWidth() {
35 | return 0;
36 | }
37 |
38 | @Override
39 | public int getHeight() {
40 | return 0;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/OvalShape.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.shape;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Rect;
6 | import android.graphics.RectF;
7 |
8 | import uk.co.deanwild.materialshowcaseview.target.Target;
9 |
10 | public class OvalShape implements Shape {
11 | private int radius;
12 | private boolean adjustToTarget;
13 | private int padding;
14 |
15 | public OvalShape() {
16 | this.radius = 200;
17 | this.adjustToTarget = true;
18 | }
19 |
20 | public OvalShape(int radius) {
21 | this.radius = 200;
22 | this.adjustToTarget = true;
23 | this.radius = radius;
24 | }
25 |
26 | public OvalShape(Rect bounds) {
27 | this(getPreferredRadius(bounds));
28 | }
29 |
30 | public OvalShape(Target target) {
31 | this(target.getBounds());
32 | }
33 |
34 | public static int getPreferredRadius(Rect bounds) {
35 | return Math.max(bounds.width(), bounds.height()) / 2;
36 | }
37 |
38 | public boolean isAdjustToTarget() {
39 | return this.adjustToTarget;
40 | }
41 |
42 | public void setAdjustToTarget(boolean adjustToTarget) {
43 | this.adjustToTarget = adjustToTarget;
44 | }
45 |
46 | public int getRadius() {
47 | return this.radius;
48 | }
49 |
50 | public void setRadius(int radius) {
51 | this.radius = radius;
52 | }
53 |
54 | public void draw(Canvas canvas, Paint paint, int x, int y) {
55 | if (this.radius > 0) {
56 | float rad = (float) (this.radius + padding);
57 | RectF rectF = new RectF(x - rad, y - rad / 2, x + rad, y + rad / 2);
58 | canvas.drawOval(rectF, paint);
59 | }
60 |
61 | }
62 |
63 | public void updateTarget(Target target) {
64 | if (this.adjustToTarget) {
65 | this.radius = getPreferredRadius(target.getBounds());
66 | }
67 |
68 | }
69 |
70 | @Override
71 | public int getTotalRadius() {
72 | return radius + padding;
73 | }
74 |
75 | @Override
76 | public void setPadding(int padding) {
77 | this.padding = padding;
78 | }
79 |
80 | public int getWidth() {
81 | return this.radius * 2;
82 | }
83 |
84 | public int getHeight() {
85 | return this.radius;
86 | }
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.shape;
2 |
3 |
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 |
8 | import uk.co.deanwild.materialshowcaseview.target.Target;
9 |
10 | public class RectangleShape implements Shape {
11 |
12 | private boolean fullWidth = false;
13 |
14 | private int width = 0;
15 | private int height = 0;
16 | private boolean adjustToTarget = true;
17 |
18 | private Rect rect;
19 | private int padding;
20 |
21 | public RectangleShape(int width, int height) {
22 | this.width = width;
23 | this.height = height;
24 | init();
25 | }
26 |
27 | public RectangleShape(Rect bounds) {
28 | this(bounds, false);
29 | }
30 |
31 | public RectangleShape(Rect bounds, boolean fullWidth) {
32 | this.fullWidth = fullWidth;
33 | height = bounds.height();
34 | if (fullWidth)
35 | width = Integer.MAX_VALUE;
36 | else width = bounds.width();
37 | init();
38 | }
39 |
40 | public boolean isAdjustToTarget() {
41 | return adjustToTarget;
42 | }
43 |
44 | public void setAdjustToTarget(boolean adjustToTarget) {
45 | this.adjustToTarget = adjustToTarget;
46 | }
47 |
48 | private void init() {
49 | rect = new Rect(-width / 2, -height / 2, width / 2, height / 2);
50 | }
51 |
52 | @Override
53 | public void draw(Canvas canvas, Paint paint, int x, int y) {
54 | if (!rect.isEmpty()) {
55 | canvas.drawRect(
56 | rect.left + x - padding,
57 | rect.top + y - padding,
58 | rect.right + x + padding,
59 | rect.bottom + y + padding,
60 | paint
61 | );
62 | }
63 | }
64 |
65 | @Override
66 | public void updateTarget(Target target) {
67 | if (adjustToTarget) {
68 | Rect bounds = target.getBounds();
69 | height = bounds.height();
70 | if (fullWidth)
71 | width = Integer.MAX_VALUE;
72 | else width = bounds.width();
73 | init();
74 | }
75 | }
76 |
77 | @Override
78 | public int getTotalRadius() {
79 | return (height / 2) + padding;
80 | }
81 |
82 | @Override
83 | public void setPadding(int padding) {
84 | this.padding = padding;
85 | }
86 |
87 | @Override
88 | public int getWidth() {
89 | return width;
90 | }
91 |
92 | @Override
93 | public int getHeight() {
94 | return height;
95 | }
96 | }
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.shape;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 |
6 | import uk.co.deanwild.materialshowcaseview.target.Target;
7 |
8 | /**
9 | * Specifies a shape of the target (e.g circle, rectangle).
10 | * Implementations of this interface will be responsible to draw the shape
11 | * at specified center point (x, y).
12 | */
13 | public interface Shape {
14 |
15 | /**
16 | * Draw shape on the canvas with the center at (x, y) using Paint object provided.
17 | */
18 | void draw(Canvas canvas, Paint paint, int x, int y);
19 |
20 | /**
21 | * Get width of the shape.
22 | */
23 | int getWidth();
24 |
25 | /**
26 | * Get height of the shape.
27 | */
28 | int getHeight();
29 |
30 | /**
31 | * Update shape bounds if necessary
32 | */
33 | void updateTarget(Target target);
34 |
35 | int getTotalRadius();
36 |
37 | void setPadding(int padding);
38 | }
39 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/target/Target.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.target;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.Rect;
5 |
6 |
7 | public interface Target {
8 | Target NONE = new Target() {
9 | @Override
10 | public Point getPoint() {
11 | return new Point(1000000, 1000000);
12 | }
13 |
14 | @Override
15 | public Rect getBounds() {
16 | Point p = getPoint();
17 | return new Rect(p.x - 190, p.y - 190, p.x + 190, p.y + 190);
18 | }
19 | };
20 |
21 | Point getPoint();
22 |
23 | Rect getBounds();
24 | }
25 |
--------------------------------------------------------------------------------
/library/src/main/java/uk/co/deanwild/materialshowcaseview/target/ViewTarget.java:
--------------------------------------------------------------------------------
1 | package uk.co.deanwild.materialshowcaseview.target;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Point;
5 | import android.graphics.Rect;
6 | import android.view.View;
7 |
8 |
9 | public class ViewTarget implements Target {
10 |
11 | private final View mView;
12 |
13 | public ViewTarget(View view) {
14 | mView = view;
15 | }
16 |
17 | public ViewTarget(int viewId, Activity activity) {
18 | mView = activity.findViewById(viewId);
19 | }
20 |
21 | @Override
22 | public Point getPoint() {
23 | int[] location = new int[2];
24 | mView.getLocationInWindow(location);
25 | int x = location[0] + mView.getWidth() / 2;
26 | int y = location[1] + mView.getHeight() / 2;
27 | return new Point(x, y);
28 | }
29 |
30 | @Override
31 | public Rect getBounds() {
32 | int[] location = new int[2];
33 | mView.getLocationInWindow(location);
34 | return new Rect(
35 | location[0],
36 | location[1],
37 | location[0] + mView.getMeasuredWidth(),
38 | location[1] + mView.getMeasuredHeight()
39 | );
40 | }
41 |
42 | public View getView() {
43 | return mView;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/showcase_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is a very long sentence to test how this tooltip behaves with longer strings.
Tap anywhere to continue");
77 |
78 |
79 | sequence.addSequenceItem(
80 |
81 | new MaterialShowcaseView.Builder(this)
82 | .setTarget(toolbar)
83 | .setToolTip(toolTip1)
84 | .withRectangleShape()
85 | .setTooltipMargin(30)
86 | .setShapePadding(50)
87 | .setDismissOnTouch(true)
88 | .setMaskColour(getResources().getColor(R.color.tooltip_mask))
89 | .build()
90 | );
91 |
92 |
93 | ShowcaseTooltip toolTip2 = ShowcaseTooltip.build(this)
94 | .corner(30)
95 | .textColor(Color.parseColor("#007686"))
96 | .text("This is another very funky tooltip");
97 |
98 | sequence.addSequenceItem(
99 | new MaterialShowcaseView.Builder(this)
100 | .setTarget(fab)
101 | .setToolTip(toolTip2)
102 | .setTooltipMargin(30)
103 | .setShapePadding(50)
104 | .setDismissOnTouch(true)
105 | .setMaskColour(getResources().getColor(R.color.tooltip_mask))
106 | .build()
107 | );
108 |
109 | sequence.start();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/ic_android_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deano2390/MaterialShowcaseView/528080516c72c2c440ed58675a6be30096a458d7/sample/src/main/res/drawable-xxhdpi/ic_android_white_24dp.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/ic_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deano2390/MaterialShowcaseView/528080516c72c2c440ed58675a6be30096a458d7/sample/src/main/res/drawable-xxhdpi/ic_edit.png
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_custom_example.xml:
--------------------------------------------------------------------------------
1 |