Skip to content

Commit 9c2bdbb

Browse files
committed
[BUGFIX #6] Implement horizontal boundary check when text inside tooltip is too long
1 parent 7f6a92d commit 9c2bdbb

File tree

1 file changed

+83
-47
lines changed

1 file changed

+83
-47
lines changed

tourguide/src/main/java/tourguide/tourguide/TourGuide.java

+83-47
Original file line numberDiff line numberDiff line change
@@ -190,103 +190,137 @@ private void checking(){
190190

191191
}
192192
private void handleDisableClicking(FrameLayoutWithHole frameLayoutWithHole){
193-
// 1. if user provides an overlay listener, use that as 1st priority
194-
if (mOverlay != null && mOverlay.mOnClickListener!=null) {
195-
frameLayoutWithHole.setClickable(true);
196-
frameLayoutWithHole.setOnClickListener(mOverlay.mOnClickListener);
197-
}
198-
// 2. if overlay listener is not provided, check if it's disabled
199-
else if (mOverlay != null && mOverlay.mDisableClick) {
193+
if (mOverlay != null && mOverlay.mDisableClick) {
200194
frameLayoutWithHole.setViewHole(mHighlightedView);
201195
frameLayoutWithHole.setSoundEffectsEnabled(false);
202-
203-
//passing Overlay On-Click listener to frame layout
204196
frameLayoutWithHole.setOnClickListener(new View.OnClickListener() {
205197
@Override
206198
public void onClick(View view) {
207199
Log.d("tourguide", "disable, do nothing");
208200
}
209201
});
210-
211202
}
212-
213203
}
214204
private void setupToolTip(FrameLayoutWithHole frameLayoutWithHole){
215-
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
216-
// layoutParams.setGravity = Gravity.BOTTOM;
205+
final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
217206

218207
if (mToolTip != null) {
208+
/* inflate and get views */
219209
ViewGroup parent = (ViewGroup) mActivity.getWindow().getDecorView();
220210
LayoutInflater layoutInflater = mActivity.getLayoutInflater();
221211
mToolTipViewGroup = layoutInflater.inflate(R.layout.tooltip, null);
222212
View toolTipContainer = mToolTipViewGroup.findViewById(R.id.toolTip_container);
223213
TextView toolTipTitleTV = (TextView) mToolTipViewGroup.findViewById(R.id.title);
224214
TextView toolTipDescriptionTV = (TextView) mToolTipViewGroup.findViewById(R.id.description);
225215

216+
/* set tooltip attributes */
226217
toolTipContainer.setBackgroundColor(mToolTip.mBackgroundColor);
227218
toolTipTitleTV.setText(mToolTip.mTitle);
228219
toolTipDescriptionTV.setText(mToolTip.mDescription);
229220

230221
mToolTipViewGroup.startAnimation(mToolTip.mEnterAnimation);
231222

232-
// measure size of image to be placed
233-
mToolTipViewGroup.measure(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
234-
int width = mToolTipViewGroup.getMeasuredWidth();
235-
int height = mToolTipViewGroup.getMeasuredHeight();
236-
237-
Point point = null;
238-
if (width > parent.getWidth()){
239-
point = getXYForToolTip(mToolTip.mGravity, parent.getWidth(), height);
240-
} else {
241-
point = getXYForToolTip(mToolTip.mGravity, width, height);
242-
}
243-
layoutParams.setMargins(point.x, point.y, 0, 0);
244223
/* add setShadow if it's turned on */
245224
if (mToolTip.mShadow) {
246225
mToolTipViewGroup.setBackgroundDrawable(mActivity.getResources().getDrawable(R.drawable.drop_shadow));
247226
}
227+
228+
/* position and size calculation */
229+
int [] pos = new int[2];
230+
mHighlightedView.getLocationOnScreen(pos);
231+
int targetViewX = pos[0];
232+
final int targetViewY = pos[1];
233+
234+
// get measured size of tooltip
235+
mToolTipViewGroup.measure(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
236+
int toolTipMeasuredWidth = mToolTipViewGroup.getMeasuredWidth();
237+
int toolTipMeasuredHeight = mToolTipViewGroup.getMeasuredHeight();
238+
239+
Point resultPoint = new Point(); // this holds the final position of tooltip
240+
float density = mActivity.getResources().getDisplayMetrics().density;
241+
final float adjustment = 10 * density; //adjustment is that little overlapping area of tooltip and targeted button
242+
243+
// calculate x position, based on gravity, tooltipMeasuredWidth, parent max width, x position of target view, adjustment
244+
if (toolTipMeasuredWidth > parent.getWidth()){
245+
resultPoint.x = getXForTooTip(mToolTip.mGravity, parent.getWidth(), targetViewX, adjustment);
246+
} else {
247+
resultPoint.x = getXForTooTip(mToolTip.mGravity, toolTipMeasuredWidth, targetViewX, adjustment);
248+
}
249+
250+
resultPoint.y = getYForTooTip(mToolTip.mGravity, toolTipMeasuredHeight, targetViewY, adjustment);
251+
252+
// add view to parent
248253
// ((ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content)).addView(mToolTipViewGroup, layoutParams);
249254
parent.addView(mToolTipViewGroup, layoutParams);
250-
if (width > parent.getWidth()){
251-
mToolTipViewGroup.getLayoutParams().width = parent.getWidth();
252255

256+
// 1. width < screen check
257+
if (toolTipMeasuredWidth > parent.getWidth()){
258+
mToolTipViewGroup.getLayoutParams().width = parent.getWidth();
259+
toolTipMeasuredWidth = parent.getWidth();
253260
}
261+
// 2. x left boundary check
262+
if (resultPoint.x < 0){
263+
mToolTipViewGroup.getLayoutParams().width = toolTipMeasuredWidth + resultPoint.x; //since point.x is negative, use plus
264+
resultPoint.x = 0;
265+
}
266+
// 3. x right boundary check
267+
int tempRightX = resultPoint.x + toolTipMeasuredWidth;
268+
if ( tempRightX > parent.getWidth()){
269+
mToolTipViewGroup.getLayoutParams().width = parent.getWidth() - resultPoint.x; //since point.x is negative, use plus
270+
}
271+
272+
// TODO: no boundary check for height yet, this is a unlikely case though
273+
// height boundary can be fixed by user changing the gravity to the other size, since there are plenty of space vertically compared to horizontally
274+
275+
// this needs an viewTreeObserver, that's because TextView measurement of it's vertical height is not accurate (didn't take into account of multiple lines yet) before it's rendered
276+
// re-calculate height again once it's rendered
277+
final ViewTreeObserver viewTreeObserver = mToolTipViewGroup.getViewTreeObserver();
278+
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
279+
@Override
280+
public void onGlobalLayout() {
281+
mToolTipViewGroup.getViewTreeObserver().removeGlobalOnLayoutListener(this);// make sure this only run once
282+
283+
int fixedY;
284+
int toolTipHeightAfterLayouted = mToolTipViewGroup.getHeight();
285+
fixedY = getYForTooTip(mToolTip.mGravity, toolTipHeightAfterLayouted, targetViewY, adjustment);
286+
layoutParams.setMargins((int)mToolTipViewGroup.getX(),fixedY,0,0);
287+
}
288+
});
289+
290+
// set the position using setMargins on the left and top
291+
layoutParams.setMargins(resultPoint.x, resultPoint.y, 0, 0);
254292
}
255293

256294
}
257-
private Point getXYForToolTip(int gravity, int width, int height) {
258-
Point point = new Point();
259-
int [] pos = new int[2];
260-
mHighlightedView.getLocationOnScreen(pos);
261-
int x = pos[0];
262-
int y = pos[1];
263-
float density = mActivity.getResources().getDisplayMetrics().density;
264-
float adjustment = 10 * density;
265-
// x calculation
295+
296+
private int getXForTooTip(int gravity, int toolTipMeasuredWidth, int targetViewX, float adjustment){
297+
int x;
266298
if ((gravity & Gravity.LEFT) == Gravity.LEFT){
267-
point.x = x - width + (int)adjustment;
299+
x = targetViewX - toolTipMeasuredWidth + (int)adjustment;
268300
} else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
269-
point.x = x + mHighlightedView.getWidth() - (int)adjustment;
301+
x = targetViewX + mHighlightedView.getWidth() - (int)adjustment;
270302
} else {
271-
point.x = x + mHighlightedView.getWidth() / 2 - width / 2;
303+
x = targetViewX + mHighlightedView.getWidth() / 2 - toolTipMeasuredWidth / 2;
272304
}
273-
274-
// y calculation
305+
return x;
306+
}
307+
private int getYForTooTip(int gravity, int toolTipMeasuredHeight, int targetViewY, float adjustment){
308+
int y;
275309
if ((gravity & Gravity.TOP) == Gravity.TOP) {
276310

277311
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
278-
point.y = y - height + (int)adjustment;
312+
y = targetViewY - toolTipMeasuredHeight + (int)adjustment;
279313
} else {
280-
point.y = y - height - (int)adjustment;
314+
y = targetViewY - toolTipMeasuredHeight - (int)adjustment;
281315
}
282316
} else { // this is center
283317
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
284-
point.y = y + mHighlightedView.getHeight() - (int) adjustment;
318+
y = targetViewY + mHighlightedView.getHeight() - (int) adjustment;
285319
} else {
286-
point.y = y + mHighlightedView.getHeight() + (int) adjustment;
320+
y = targetViewY + mHighlightedView.getHeight() + (int) adjustment;
287321
}
288322
}
289-
return point;
323+
return y;
290324
}
291325

292326
private FloatingActionButton setupAndAddFABToFrameLayout(final FrameLayoutWithHole frameLayoutWithHole){
@@ -509,8 +543,10 @@ private int getScreenWidth(){
509543
return 0;
510544
}
511545
}
512-
513546
public FrameLayoutWithHole getOverlay(){
514547
return mFrameLayout;
515548
}
549+
public View getToolTip(){
550+
return mToolTipViewGroup;
551+
}
516552
}

0 commit comments

Comments
 (0)