From f6a9b760d482ea0e01307b456db6665f789874b4 Mon Sep 17 00:00:00 2001 From: Moustafa Elsaghier Date: Wed, 31 Aug 2022 16:25:00 +0200 Subject: [PATCH] create parameter for making the shape filled or not --- README.md | 23 +- .../eram/showcaseview/MainActivity.java | 4 +- showcaseviewlib/build.gradle | 2 +- .../ir/eram/showcaseviewlib/GuideView.java | 196 ++++++++++-------- .../src/main/res/values/styles.xml | 4 - 5 files changed, 131 insertions(+), 98 deletions(-) delete mode 100644 showcaseviewlib/src/main/res/values/styles.xml diff --git a/README.md b/README.md index 0680db35..ce021095 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ maven: com.github.mreram showcaseview - 1.4.0 + 1.4.1 ``` gradle: @@ -61,7 +61,7 @@ allprojects { ``` Step 2. Add the dependency ```groovy -implementation 'com.github.mreram:showcaseview:1.4.0' +implementation 'com.github.mreram:showcaseview:1.4.1' ``` ## Change type face ```java @@ -131,6 +131,17 @@ new GuideView.Builder(MainActivity.this) .show(); ``` +## Set shape/pointer type solid/filled +```java +new GuideView.Builder(this) + .setTitle("Guide Title Text") + .setContentText("Guide Description Text\n .....Guide Description Text\n .....Guide Description Text .....") + .setTargetView(view) + .isShapeSolid(false) //optional - default is false + .build() + .show(); +``` + ### DismissType Attribute | Type | Description | @@ -150,6 +161,14 @@ new GuideView.Builder(MainActivity.this) | none | Show no pointer or line towards targetView | +### isShapeSolid Attribute + +| Type | Description | +| ------ | ------ | +| true | Show shape pointer as solid/filled | +| false | Show shape pointer as not solid | + + ## Contribution :collision: Pull requests are welcome! :clap: diff --git a/app/src/main/java/ir/smartdevelop/eram/showcaseview/MainActivity.java b/app/src/main/java/ir/smartdevelop/eram/showcaseview/MainActivity.java index 144875b9..27719af1 100644 --- a/app/src/main/java/ir/smartdevelop/eram/showcaseview/MainActivity.java +++ b/app/src/main/java/ir/smartdevelop/eram/showcaseview/MainActivity.java @@ -1,5 +1,6 @@ package ir.smartdevelop.eram.showcaseview; +import android.annotation.SuppressLint; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; @@ -8,7 +9,6 @@ import smartdevelop.ir.eram.showcaseviewlib.config.DismissType; import smartdevelop.ir.eram.showcaseviewlib.config.Gravity; import smartdevelop.ir.eram.showcaseviewlib.config.PointerType; -import smartdevelop.ir.eram.showcaseviewlib.listener.GuideListener; public class MainActivity extends AppCompatActivity { @@ -21,6 +21,7 @@ public class MainActivity extends AppCompatActivity { private GuideView mGuideView; private GuideView.Builder builder; + @SuppressLint("NonConstantResourceId") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -39,6 +40,7 @@ protected void onCreate(Bundle savedInstanceState) { .setGravity(Gravity.center) .setDismissType(DismissType.anywhere) .setPointerType(PointerType.circle) + .isShapeSolid(false) .setTargetView(view1) .setGuideListener(view -> { switch (view.getId()) { diff --git a/showcaseviewlib/build.gradle b/showcaseviewlib/build.gradle index 90d5b75c..a01fb686 100644 --- a/showcaseviewlib/build.gradle +++ b/showcaseviewlib/build.gradle @@ -10,7 +10,7 @@ android { minSdkVersion 11 targetSdkVersion 30 versionCode 3 - versionName "1.4.0" + versionName "1.4.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/showcaseviewlib/src/main/java/smartdevelop/ir/eram/showcaseviewlib/GuideView.java b/showcaseviewlib/src/main/java/smartdevelop/ir/eram/showcaseviewlib/GuideView.java index 4945d6c5..8417ec8d 100644 --- a/showcaseviewlib/src/main/java/smartdevelop/ir/eram/showcaseviewlib/GuideView.java +++ b/showcaseviewlib/src/main/java/smartdevelop/ir/eram/showcaseviewlib/GuideView.java @@ -60,10 +60,11 @@ public class GuideView extends FrameLayout { private final Paint paintCircleInner = new Paint(); private final Paint targetPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Xfermode X_FER_MODE_CLEAR = new PorterDuffXfermode(PorterDuff.Mode.CLEAR); + private final Path arrowPath = new Path(); private final View target; private RectF targetRect; - private final Rect selfRect = new Rect(); + private final Rect backgroundRect = new Rect(); private final float density; private float stopY; @@ -88,6 +89,7 @@ public class GuideView extends FrameLayout { private DismissType dismissType; private PointerType pointerType; private final GuideMessageView mMessageView; + private boolean isShapeSolid; private GuideView(Context context, View view) { super(context); @@ -97,38 +99,23 @@ private GuideView(Context context, View view) { density = context.getResources().getDisplayMetrics().density; init(); - if (view instanceof Targetable) { - targetRect = ((Targetable) view).boundingRect(); - } else { - int[] locationTarget = new int[2]; - target.getLocationOnScreen(locationTarget); - targetRect = new RectF( - locationTarget[0], - locationTarget[1], - locationTarget[0] + target.getWidth(), - locationTarget[1] + target.getHeight() - ); - } - mMessageView = new GuideMessageView(getContext()); mMessageView.setPadding( - messageViewPadding, - messageViewPadding, - messageViewPadding, - messageViewPadding + messageViewPadding, + messageViewPadding, + messageViewPadding, + messageViewPadding ); mMessageView.setColor(Color.WHITE); addView( - mMessageView, - new LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + mMessageView, + new LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) ); - setMessageLocation(resolveMessageViewLocation()); - ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { @@ -138,33 +125,40 @@ public void onGlobalLayout() { getViewTreeObserver().removeGlobalOnLayoutListener(this); } - setMessageLocation(resolveMessageViewLocation()); - if (target instanceof Targetable) { targetRect = ((Targetable) target).boundingRect(); } else { int[] locationTarget = new int[2]; target.getLocationOnScreen(locationTarget); targetRect = new RectF( - locationTarget[0], - locationTarget[1], - locationTarget[0] + target.getWidth(), - locationTarget[1] + target.getHeight() + locationTarget[0], + locationTarget[1], + locationTarget[0] + target.getWidth(), + locationTarget[1] + target.getHeight() ); + if (isLandscape()) { + targetRect.offset(-getStatusBarHeight(), 0); + } } - selfRect.set( - getPaddingLeft(), - getPaddingTop(), - getWidth() - getPaddingRight(), - getHeight() - getPaddingBottom() + backgroundRect.set( + getPaddingLeft(), + getPaddingTop(), + getWidth() - getPaddingRight(), + getHeight() - getPaddingBottom() ); + if (isLandscape()) { + backgroundRect.offset(-getNavigationBarSize(), 0); + } else { + backgroundRect.offset(0, -getNavigationBarSize()); + } + isTop = !((targetRect.top + (indicatorHeight)) > getHeight() / 2f); marginGuide = (int) (isTop ? marginGuide : -marginGuide); + setMessageLocation(resolveMessageViewLocation()); startYLineAndCircle = (isTop ? targetRect.bottom : targetRect.top) + marginGuide; - stopY = yMessageView + indicatorHeight; + stopY = yMessageView + indicatorHeight + (isTop ? -marginGuide : marginGuide); startAnimationSize(); - getViewTreeObserver().addOnGlobalLayoutListener(this); } }; getViewTreeObserver().addOnGlobalLayoutListener(layoutListener); @@ -173,28 +167,22 @@ public void onGlobalLayout() { private void startAnimationSize() { if (!isPerformedAnimationSize) { final ValueAnimator circleSizeAnimator = ValueAnimator.ofFloat( - 0f, - circleIndicatorSizeFinal + 0f, + circleIndicatorSizeFinal ); - circleSizeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - circleIndicatorSize = (float) circleSizeAnimator.getAnimatedValue(); - circleInnerIndicatorSize = (float) circleSizeAnimator.getAnimatedValue() - density; - postInvalidate(); - } + circleSizeAnimator.addUpdateListener(valueAnimator -> { + circleIndicatorSize = (float) circleSizeAnimator.getAnimatedValue(); + circleInnerIndicatorSize = (float) circleSizeAnimator.getAnimatedValue() - density; + postInvalidate(); }); final ValueAnimator linePositionAnimator = ValueAnimator.ofFloat( - stopY, - startYLineAndCircle + stopY, + startYLineAndCircle ); - linePositionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - startYLineAndCircle = (float) linePositionAnimator.getAnimatedValue(); - postInvalidate(); - } + linePositionAnimator.addUpdateListener(valueAnimator -> { + startYLineAndCircle = (float) linePositionAnimator.getAnimatedValue(); + postInvalidate(); }); linePositionAnimator.setDuration(SIZE_ANIMATION_DURATION); @@ -234,15 +222,24 @@ private void init() { circleIndicatorSizeFinal = CIRCLE_INDICATOR_SIZE * density; } - private int getNavigationBarSize() { - Resources resources = getContext().getResources(); - int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); - if (resourceId > 0) { - return resources.getDimensionPixelSize(resourceId); + public int getNavigationBarSize() { + Resources resources = getResources(); + int id = resources.getIdentifier("navigation_bar_height_landscape", "dimen", "android"); + if (id > 0) { + return resources.getDimensionPixelSize(id); } return 0; } + public int getStatusBarHeight() { + int result = 0; + int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = getResources().getDimensionPixelSize(resourceId); + } + return result; + } + private boolean isLandscape() { int display_mode = getResources().getConfiguration().orientation; return display_mode != Configuration.ORIENTATION_PORTRAIT; @@ -256,46 +253,54 @@ protected void onDraw(final Canvas canvas) { selfPaint.setColor(BACKGROUND_COLOR); selfPaint.setStyle(Paint.Style.FILL); selfPaint.setAntiAlias(true); - canvas.drawRect(selfRect, selfPaint); + canvas.drawRect(backgroundRect, selfPaint); paintLine.setStyle(Paint.Style.FILL); paintLine.setColor(LINE_INDICATOR_COLOR); paintLine.setStrokeWidth(lineIndicatorWidthSize); paintLine.setAntiAlias(true); - paintCircle.setStyle(Paint.Style.STROKE); + if (isShapeSolid) + paintCircle.setStyle(Paint.Style.FILL); + else + paintCircle.setStyle(Paint.Style.STROKE); paintCircle.setColor(CIRCLE_INDICATOR_COLOR); paintCircle.setStrokeCap(Paint.Cap.ROUND); paintCircle.setStrokeWidth(strokeCircleWidth); paintCircle.setAntiAlias(true); - paintCircleInner.setStyle(Paint.Style.FILL); - paintCircleInner.setColor(CIRCLE_INNER_INDICATOR_COLOR); - paintCircleInner.setAntiAlias(true); - + if (!isShapeSolid) { + paintCircleInner.setStyle(Paint.Style.FILL); + paintCircleInner.setColor(CIRCLE_INNER_INDICATOR_COLOR); + paintCircleInner.setAntiAlias(true); + } final float x = (targetRect.left / 2 + targetRect.right / 2); switch (pointerType) { case circle: - canvas.drawLine(x,startYLineAndCircle,x,stopY,paintLine); + canvas.drawLine(x, startYLineAndCircle, x, stopY, paintLine); canvas.drawCircle(x, startYLineAndCircle, circleIndicatorSize, paintCircle); - canvas.drawCircle(x, startYLineAndCircle, circleInnerIndicatorSize, paintCircleInner); + // to remove solid & inner circle drawing + if (!isShapeSolid) + canvas.drawCircle( + x, + startYLineAndCircle, + circleInnerIndicatorSize, + paintCircleInner + ); break; case arrow: - canvas.drawLine(x,startYLineAndCircle,x,stopY,paintLine); - Path path = new Path(); + canvas.drawLine(x, startYLineAndCircle, x, stopY, paintLine); + arrowPath.reset(); if (isTop) { - path.moveTo(x, startYLineAndCircle - (circleIndicatorSize * 2)); - path.lineTo(x + circleIndicatorSize, startYLineAndCircle); - path.lineTo(x - circleIndicatorSize, startYLineAndCircle); - path.close(); + arrowPath.moveTo(x, startYLineAndCircle - (circleIndicatorSize * 2)); } else { - path.moveTo(x, startYLineAndCircle + (circleIndicatorSize * 2)); - path.lineTo(x + circleIndicatorSize, startYLineAndCircle); - path.lineTo(x - circleIndicatorSize, startYLineAndCircle); - path.close(); + arrowPath.moveTo(x, startYLineAndCircle + (circleIndicatorSize * 2)); } - canvas.drawPath(path, paintCircle); + arrowPath.lineTo(x + circleIndicatorSize, startYLineAndCircle); + arrowPath.lineTo(x - circleIndicatorSize, startYLineAndCircle); + arrowPath.close(); + canvas.drawPath(arrowPath, paintCircle); break; case none: //draw no line and no pointer @@ -308,10 +313,10 @@ protected void onDraw(final Canvas canvas) { canvas.drawPath(((Targetable) target).guidePath(), targetPaint); } else { canvas.drawRoundRect( - targetRect, - RADIUS_SIZE_TARGET_RECT, - RADIUS_SIZE_TARGET_RECT, - targetPaint + targetRect, + RADIUS_SIZE_TARGET_RECT, + RADIUS_SIZE_TARGET_RECT, + targetPaint ); } } @@ -362,7 +367,7 @@ public boolean onTouchEvent(MotionEvent event) { break; case outsideTargetAndMessage: - if(!(targetRect.contains(x, y) || isViewContains(mMessageView, x, y))){ + if (!(targetRect.contains(x, y) || isViewContains(mMessageView, x, y))) { dismiss(); } } @@ -401,7 +406,7 @@ private Point resolveMessageViewLocation() { xMessageView = (int) (targetRect.right) - mMessageView.getWidth(); } - if (isLandscape()) { + if (isLandscape() && (xMessageView + mMessageView.getWidth()) > backgroundRect.right) { xMessageView -= getNavigationBarSize(); } @@ -432,11 +437,10 @@ private Point resolveMessageViewLocation() { public void show() { this.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT )); this.setClickable(false); - ((ViewGroup) ((Activity) getContext()).getWindow().getDecorView()).addView(this); AlphaAnimation startAnimation = new AlphaAnimation(0.0f, 1.0f); startAnimation.setDuration(APPEARING_ANIMATION_DURATION); @@ -480,6 +484,7 @@ public static class Builder { private Gravity gravity; private DismissType dismissType; private PointerType pointerType; + private boolean isShapeSolid; private final Context context; private Spannable contentSpan; private Typeface titleTypeFace, contentTypeFace; @@ -662,11 +667,23 @@ public Builder setPointerType(PointerType pointerType) { this.pointerType = pointerType; return this; } + + /** + * this method defining the type of pointer + * + * @param isShapeSolid should be true/false to fill the shape arrow -> To be filled/solid you must set this parameter to true + */ + public Builder isShapeSolid(boolean isShapeSolid) { + this.isShapeSolid = isShapeSolid; + return this; + } + public GuideView build() { GuideView guideView = new GuideView(context, targetView); guideView.mGravity = gravity != null ? gravity : Gravity.auto; guideView.dismissType = dismissType != null ? dismissType : DismissType.targetView; guideView.pointerType = pointerType != null ? pointerType : PointerType.circle; + guideView.isShapeSolid = isShapeSolid; float density = context.getResources().getDisplayMetrics().density; guideView.setTitle(title); @@ -710,5 +727,4 @@ public GuideView build() { return guideView; } } -} - +} \ No newline at end of file diff --git a/showcaseviewlib/src/main/res/values/styles.xml b/showcaseviewlib/src/main/res/values/styles.xml deleted file mode 100644 index 0d2c4cc4..00000000 --- a/showcaseviewlib/src/main/res/values/styles.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file