From 1143f833337b91b7afa2609df30de47e41e2e348 Mon Sep 17 00:00:00 2001 From: Anton Danshin Date: Thu, 27 Aug 2015 16:05:01 +0300 Subject: [PATCH 1/7] Added support for rectangular and custom shapes. Updates versions of support library and android sdk Added example with menu --- library/build.gradle | 6 +- .../materialshowcaseview/CircleShape.java | 39 ++++++++ .../MaterialShowcaseView.java | 94 +++++++++++------- .../materialshowcaseview/RectangleShape.java | 51 ++++++++++ .../deanwild/materialshowcaseview/Shape.java | 15 +++ .../materialshowcaseview/ShowcaseConfig.java | 1 - .../deanwild/materialshowcaseview/Target.java | 8 +- .../materialshowcaseview/ViewTarget.java | 26 ++--- sample/build.gradle | 8 +- .../CustomExample.java | 27 ++++- .../MainActivity.java | 4 +- .../SequenceExample.java | 20 ++-- .../SimpleSingleExample.java | 4 +- .../drawable-xxhdpi/ic_android_white_24dp.png | Bin 0 -> 676 bytes .../main/res/menu/activity_custom_example.xml | 12 +++ 15 files changed, 236 insertions(+), 79 deletions(-) create mode 100644 library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java create mode 100644 library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java create mode 100644 library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java create mode 100644 sample/src/main/res/drawable-xxhdpi/ic_android_white_24dp.png create mode 100644 sample/src/main/res/menu/activity_custom_example.xml diff --git a/library/build.gradle b/library/build.gradle index 8e6d313e..8a509c61 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23" defaultConfig { minSdkVersion 14 - targetSdkVersion 22 + targetSdkVersion 23 versionCode 1 versionName "1.0" } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java new file mode 100644 index 00000000..d5000d80 --- /dev/null +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java @@ -0,0 +1,39 @@ +package uk.co.deanwild.materialshowcaseview; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; + +/** + * Circular shape for target. + */ +public class CircleShape implements Shape { + + private int radius = 210; + + public CircleShape(int radius) { + this.radius = radius; + } + + public CircleShape(Rect bounds) { + this(getPreferredRadius(bounds)); + } + + public CircleShape(Target target) { + this(target.getBounds()); + } + + @Override + public void draw(Canvas canvas, Paint paint, int x, int y) { + canvas.drawCircle(x, y, radius, paint); + } + + @Override + public void updateTarget(Target target) { + radius = getPreferredRadius(target.getBounds()); + } + + public static int getPreferredRadius(Rect bounds) { + return Math.max(bounds.width(), bounds.height()) / 2 + 10; + } +} diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java index 88571d6e..6270763e 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java @@ -10,6 +10,7 @@ import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; import android.os.Build; import android.os.Handler; import android.util.AttributeSet; @@ -38,12 +39,11 @@ public class MaterialShowcaseView extends FrameLayout implements View.OnTouchLis private Canvas mCanvas; private Paint mEraser; private Target mTarget; + private Shape mShape; private int mXPosition; private int mYPosition; private boolean mWasDismissed = false; - private int mRadius = ShowcaseConfig.DEFAULT_RADIUS; - private boolean mUseAutoRadius = true; private View mContentBox; private TextView mContentTextView; private TextView mDismissButton; @@ -153,14 +153,16 @@ protected void onDraw(Canvas canvas) { // draw solid background mCanvas.drawColor(mMaskColour); - // Erase a circle + // Prepare eraser Paint if needed if (mEraser == null) { mEraser = new Paint(); mEraser.setColor(0xFFFFFFFF); mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); mEraser.setFlags(Paint.ANTI_ALIAS_FLAG); } - mCanvas.drawCircle(mXPosition, mYPosition, mRadius, mEraser); + + // draw (erase) shape + mShape.draw(mCanvas, mEraser, mXPosition, mYPosition); // Draw the bitmap on our views canvas. canvas.drawBitmap(mBitmap, 0, 0, null); @@ -252,26 +254,26 @@ public void setTarget(Target target) { // apply the target position Point targetPoint = mTarget.getPoint(); + Rect targetBounds = mTarget.getBounds(); + if (mShape != null) + mShape.updateTarget(mTarget); setPosition(targetPoint); - // apply auto radius - if (mUseAutoRadius) { - setRadius(mTarget.getRadius()); - } - // now figure out whether to put content above or below it int height = getMeasuredHeight(); int midPoint = height / 2; int yPos = targetPoint.y; + int radius = Math.max(targetBounds.height(), targetBounds.width()) / 2 + 10; + if (yPos > midPoint) { // target is in lower half of screen, we'll sit above it mContentTopMargin = 0; - mContentBottomMargin = (height - yPos) + mRadius; + mContentBottomMargin = (height - yPos) + radius; mGravity = Gravity.BOTTOM; } else { // target is in upper half of screen, we'll sit below it - mContentTopMargin = yPos + mRadius; + mContentTopMargin = yPos + radius; mContentBottomMargin = 0; mGravity = Gravity.TOP; } @@ -351,14 +353,6 @@ private void setDismissOnTouch(boolean dismissOnTouch) { mDismissOnTouch = dismissOnTouch; } - private void setUseAutoRadius(boolean useAutoRadius) { - mUseAutoRadius = useAutoRadius; - } - - private void setRadius(int radius) { - mRadius = radius; - - } private void setShouldRender(boolean shouldRender) { mShouldRender = shouldRender; @@ -390,6 +384,9 @@ void setDetachedListener(IDetachedListener detachedListener) { mDetachedListener = detachedListener; } + public void setShape(Shape mShape) { + this.mShape = mShape; + } /** * Set properties based on a config object @@ -421,6 +418,12 @@ public void onGlobalLayout() { * Gives us a builder utility class with a fluent API for eaily configuring showcase views */ public static class Builder { + private static final int CIRCLE_SHAPE = 0; + private static final int RECTANGLE_SHAPE = 1; + + private boolean fullWidth = false; + private int shapeType = CIRCLE_SHAPE; + final MaterialShowcaseView showcaseView; private final Activity activity; @@ -467,24 +470,6 @@ public Builder setContentText(CharSequence text) { } - /** - * Use auto radius, if true then the showcase circle will auto size based on the target view - * Defaults to true - */ - public Builder setUseAutoRadius(boolean useAutoRadius) { - showcaseView.setUseAutoRadius(useAutoRadius); - return this; - } - - /** - * Manually define a radius in pixels - should set setUseAutoRadius to false - * Defaults to 200 pixels - */ - public Builder setRadius(int radius) { - showcaseView.setRadius(radius); - return this; - } - public Builder setDismissOnTouch(boolean dismissOnTouch) { showcaseView.setDismissOnTouch(dismissOnTouch); return this; @@ -525,12 +510,45 @@ public Builder singleUse(String showcaseID) { return this; } + public Builder setShape(Shape shape) { + showcaseView.setShape(shape); + return this; + } + + public Builder withCircleShape() { + shapeType = CIRCLE_SHAPE; + return this; + } + + public Builder withRectangleShape() { + return withRectangleShape(false); + } + + public Builder withRectangleShape(boolean fullWidth) { + this.shapeType = RECTANGLE_SHAPE; + this.fullWidth = fullWidth; + return this; + } + public MaterialShowcaseView build() { + if (showcaseView.mShape == null) { + switch (shapeType) { + case RECTANGLE_SHAPE: { + showcaseView.setShape(new RectangleShape(showcaseView.mTarget.getBounds(), activity, fullWidth)); + break; + } + case CIRCLE_SHAPE: { + showcaseView.mShape = new CircleShape(showcaseView.mTarget); + break; + } + default: throw new IllegalArgumentException("Usupported shape type: " + shapeType); + } + } return showcaseView; } public MaterialShowcaseView show() { - showcaseView.show(activity); + build().show(activity); return showcaseView; } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java new file mode 100644 index 00000000..01a0391f --- /dev/null +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java @@ -0,0 +1,51 @@ +package uk.co.deanwild.materialshowcaseview; + + +import android.app.Activity; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.util.DisplayMetrics; + +public class RectangleShape implements Shape { + + private Activity activity; + private Rect rect; + private boolean fullWidth; + + + public RectangleShape(Rect bounds) { + this(bounds, null, false); + } + + public RectangleShape(Rect bounds, Activity activity, boolean fullWidth) { + this.activity = activity; + this.fullWidth = fullWidth; + this.rect = getPreferredBounds(bounds, null, false); + } + + @Override + public void draw(Canvas canvas, Paint paint, int x, int y) { + canvas.drawRect(rect, paint); + } + + @Override + public void updateTarget(Target target) { + rect = getPreferredBounds(target.getBounds(), activity, fullWidth); + } + + public static Rect getPreferredBounds(Rect bounds, Activity activity, boolean fullWidth) { + Rect rect = new Rect(bounds.left - 10, bounds.top - 10, bounds.right + 10, bounds.bottom + 10); + if (activity != null && fullWidth) { + rect.left = 0; + rect.right = getDisplayMetrics(activity).widthPixels; + } + return rect; + } + + public static DisplayMetrics getDisplayMetrics(Activity context) { + DisplayMetrics displaymetrics = new DisplayMetrics(); + context.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics); + return displaymetrics; + } +} \ No newline at end of file diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java new file mode 100644 index 00000000..3deb801f --- /dev/null +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java @@ -0,0 +1,15 @@ +package uk.co.deanwild.materialshowcaseview; + +import android.graphics.Canvas; +import android.graphics.Paint; + +/** + * Specifies a shape of the target (e.g circle, rectangle) + */ +public interface Shape { + + void updateTarget(Target target); + + void draw(Canvas canvas, Paint paint, int x, int y); + +} diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java index bc3c8aa4..580f8eb0 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java @@ -10,7 +10,6 @@ public class ShowcaseConfig { public static final String DEFAULT_MASK_COLOUR = "#dd335075"; public static final long DEFAULT_FADE_TIME = 300; public static final long DEFAULT_DELAY = 0; - public static final int DEFAULT_RADIUS = 200; private long mDelay = DEFAULT_DELAY; private int mMaskColour; diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java index eacc197c..73306c57 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java @@ -1,6 +1,7 @@ package uk.co.deanwild.materialshowcaseview; import android.graphics.Point; +import android.graphics.Rect; /** * Created by deanwild on 04/08/15. @@ -13,12 +14,13 @@ public Point getPoint() { } @Override - public int getRadius() { - return 200; + public Rect getBounds() { + Point p = getPoint(); + return new Rect(p.x - 190, p.y - 190, p.x + 190, p.y + 190); } }; Point getPoint(); - int getRadius(); + Rect getBounds(); } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java index d855b0d1..1d17f6a8 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.graphics.Point; +import android.graphics.Rect; import android.view.View; /** @@ -29,21 +30,14 @@ public Point getPoint() { } @Override - public int getRadius() { - - int radius = 200; - - if (mView != null) { - - if (mView.getMeasuredHeight() > mView.getMeasuredWidth()) { - radius = mView.getMeasuredHeight() / 2; - }else{ - radius = mView.getMeasuredWidth() / 2; - } - - radius += 10; // add a 10 pixel padding to circle - } - - return radius; + public Rect getBounds() { + int[] location = new int[2]; + mView.getLocationInWindow(location); + return new Rect( + location[0], + location[1], + location[0] + mView.getMeasuredWidth(), + location[1] + mView.getMeasuredHeight() + ); } } diff --git a/sample/build.gradle b/sample/build.gradle index d71a32b1..1cb825e0 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23" defaultConfig { applicationId "uk.co.deanwild.materialshowcaseviewsample" minSdkVersion 14 - targetSdkVersion 22 + targetSdkVersion 23 versionCode 1 versionName "1.0" } @@ -21,6 +21,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:22.2.0' + compile 'com.android.support:appcompat-v7:23.0.0' compile project(':library') } diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java index 1404803e..d368c021 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java @@ -1,7 +1,7 @@ package uk.co.deanwild.materialshowcaseviewsample; -import android.support.v7.app.ActionBarActivity; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -11,7 +11,7 @@ import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; -public class CustomExample extends ActionBarActivity implements View.OnClickListener { +public class CustomExample extends AppCompatActivity implements View.OnClickListener { private Button mButtonShow; private Button mButtonReset; @@ -32,6 +32,29 @@ protected void onCreate(Bundle savedInstanceState) { presentShowcaseView(1000); // one second delay } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.activity_custom_example, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + if (item.getItemId() == R.id.menu_sample_action) { + View view = findViewById(R.id.menu_sample_action); + new MaterialShowcaseView.Builder(this) + .setTarget(view) + .setDismissText("GOT IT") + .setContentText("Example of how to show a showcase view for menu items in action bar.") + .setContentTextColor(getResources().getColor(R.color.green)) + .setMaskColour(getResources().getColor(R.color.purple)) + .show(); + } + + return super.onOptionsItemSelected(item); + } + @Override public void onClick(View v) { diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/MainActivity.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/MainActivity.java index 61c7842a..8f0a3ca8 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/MainActivity.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/MainActivity.java @@ -3,14 +3,14 @@ import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; -public class MainActivity extends ActionBarActivity implements View.OnClickListener { +public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java index 29d2a52d..cecc9bca 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java @@ -1,7 +1,7 @@ package uk.co.deanwild.materialshowcaseviewsample; -import android.support.v7.app.ActionBarActivity; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; @@ -11,7 +11,7 @@ import uk.co.deanwild.materialshowcaseview.ShowcaseConfig; -public class SequenceExample extends ActionBarActivity implements View.OnClickListener { +public class SequenceExample extends AppCompatActivity implements View.OnClickListener { private Button mButtonOne; private Button mButtonTwo; @@ -65,14 +65,18 @@ private void presentShowcaseSequence() { sequence.setConfig(config); - sequence.addSequenceItem(mButtonOne, - "This is button one", "GOT IT"); + sequence.addSequenceItem(mButtonOne, "This is button one", "GOT IT"); - sequence.addSequenceItem(mButtonTwo, - "This is button two", "GOT IT"); + sequence.addSequenceItem( + new MaterialShowcaseView.Builder(this) + .setTarget(mButtonTwo) + .setDismissText("GOT IT") + .setContentText("This is button two") + .withRectangleShape(true) + .build() + ); - sequence.addSequenceItem(mButtonThree, - "This is button three", "GOT IT"); + sequence.addSequenceItem(mButtonThree, "This is button three", "GOT IT"); sequence.start(); diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SimpleSingleExample.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SimpleSingleExample.java index bca95d71..222c43b7 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SimpleSingleExample.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SimpleSingleExample.java @@ -1,7 +1,7 @@ package uk.co.deanwild.materialshowcaseviewsample; -import android.support.v7.app.ActionBarActivity; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; @@ -9,7 +9,7 @@ import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; -public class SimpleSingleExample extends ActionBarActivity implements View.OnClickListener { +public class SimpleSingleExample extends AppCompatActivity implements View.OnClickListener { private Button mButtonShow; private Button mButtonReset; diff --git a/sample/src/main/res/drawable-xxhdpi/ic_android_white_24dp.png b/sample/src/main/res/drawable-xxhdpi/ic_android_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..068248edcac444c8df26bac3908fc6b557fba80c GIT binary patch literal 676 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2V9NG%aSW-r_4c-<_YnsX_77i= zExvpxhkkECLZ zC*R!uTkenipWSCv-n*NnsCyohn4EY*=V`Bz^U8!8x5**G0ivn8*A67D`N?-mA+@>4vGU~Y zZ!?ShBA0&&zV+$Y)u3(3#r}nVdyIRovv+Ti%04nLa_qn2lNTmet1!!QG2EKUbtUmh z$ZnSfjE)X}g&ME1GOqPtu@Yt}(Gt|q=6DgPbfH~gLAmc~*;ULe3?G?HgN|u_pW=T? zHdQC;+zFO@lEvX3`hE^&hZZwg+y6;UI_a=)uLWbTy3BzNC58KOa~j?WtL+VCd2+j3 zI6yD)(j16j*G`teyaguWr3{Q54h&2J4Gb&_7|ei|4I47%Gn7m>c-aflc1%c3HVkam zgj$*C7i&X*?+xDzmWq$tAbk7yrf(-7yO(J3iO&cynvk!rvwpt~=b@8Qjqlmdaxl%B z#t|Z|n0mVAnUDMNMbCIQUop>YOlCx}_^!js@Xt)gS223+oh5D&#vAbJ>Yf7|vl@cd oKV$6+{kk_|)zv*pJ-p|{FE(j(=xHpN2TXkop00i_>zopr01z-5fB*mh literal 0 HcmV?d00001 diff --git a/sample/src/main/res/menu/activity_custom_example.xml b/sample/src/main/res/menu/activity_custom_example.xml new file mode 100644 index 00000000..3540da6e --- /dev/null +++ b/sample/src/main/res/menu/activity_custom_example.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file From 1441ada9c94455040238006946b4ec54bb8bd67e Mon Sep 17 00:00:00 2001 From: Anton Danshin Date: Mon, 31 Aug 2015 15:25:39 +0300 Subject: [PATCH 2/7] Refactoring: - Dismiss button is GONE when text its title is empty - Shape refactoring - updated examples - added JRebel --- build.gradle | 5 ++ .../MaterialShowcaseView.java | 37 +++++++++-- .../materialshowcaseview/RectangleShape.java | 51 --------------- .../{ => shape}/CircleShape.java | 18 +++++- .../shape/RectangleShape.java | 62 +++++++++++++++++++ .../{ => shape}/Shape.java | 8 ++- .../src/main/res/layout/showcase_content.xml | 4 +- sample/build.gradle | 2 + .../CustomExample.java | 2 +- .../SequenceExample.java | 9 ++- 10 files changed, 135 insertions(+), 63 deletions(-) delete mode 100644 library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java rename library/src/main/java/uk/co/deanwild/materialshowcaseview/{ => shape}/CircleShape.java (68%) create mode 100644 library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java rename library/src/main/java/uk/co/deanwild/materialshowcaseview/{ => shape}/Shape.java (63%) diff --git a/build.gradle b/build.gradle index 9405f3fd..62e1a982 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,14 @@ buildscript { repositories { jcenter() + maven { + url 'https://repos.zeroturnaround.com/nexus/content/repositories/zt-public-releases' + } } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' + // This does not break the build when Android Studio is missing the JRebel for Android plugin. + classpath 'com.zeroturnaround.jrebel.android:jr-android-gradle:0.8.+' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java index 6270763e..9129ac60 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java @@ -13,6 +13,7 @@ import android.graphics.Rect; import android.os.Build; import android.os.Handler; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; @@ -27,6 +28,10 @@ import java.util.ArrayList; import java.util.List; +import uk.co.deanwild.materialshowcaseview.shape.CircleShape; +import uk.co.deanwild.materialshowcaseview.shape.RectangleShape; +import uk.co.deanwild.materialshowcaseview.shape.Shape; + /** * Created by deanwild on 04/08/15. @@ -239,6 +244,9 @@ public void onClick(View v) { public void setTarget(Target target) { mTarget = target; + // update dismiss button state + updateDismissButton(); + if (mTarget != null) { /** @@ -248,15 +256,13 @@ public void setTarget(Target target) { mBottomMargin = getSoftButtonsBarSizePort((Activity) getContext()); FrameLayout.LayoutParams contentLP = (LayoutParams) getLayoutParams(); - if (contentLP!=null && contentLP.bottomMargin != mBottomMargin) + if (contentLP != null && contentLP.bottomMargin != mBottomMargin) contentLP.bottomMargin = mBottomMargin; } // apply the target position Point targetPoint = mTarget.getPoint(); Rect targetBounds = mTarget.getBounds(); - if (mShape != null) - mShape.updateTarget(mTarget); setPosition(targetPoint); // now figure out whether to put content above or below it @@ -265,6 +271,10 @@ public void setTarget(Target target) { int yPos = targetPoint.y; int radius = Math.max(targetBounds.height(), targetBounds.width()) / 2 + 10; + if (mShape != null) { + mShape.updateTarget(mTarget); + radius = mShape.getHeight() / 2 + 10; + } if (yPos > midPoint) { // target is in lower half of screen, we'll sit above it @@ -334,6 +344,8 @@ private void setContentText(CharSequence contentText) { private void setDismissText(CharSequence dismissText) { if (mDismissButton != null) { mDismissButton.setText(dismissText); + + updateDismissButton(); } } @@ -401,6 +413,17 @@ public void setConfig(ShowcaseConfig config) { setMaskColour(config.getMaskColor()); } + private void updateDismissButton() { + // hide or show button + if (mDismissButton != null) { + if (TextUtils.isEmpty(mDismissButton.getText())) { + mDismissButton.setVisibility(GONE); + } else { + mDismissButton.setVisibility(VISIBLE); + } + } + } + /** * REDRAW LISTENER - this ensures we redraw after activity finishes laying out */ @@ -534,16 +557,18 @@ public MaterialShowcaseView build() { if (showcaseView.mShape == null) { switch (shapeType) { case RECTANGLE_SHAPE: { - showcaseView.setShape(new RectangleShape(showcaseView.mTarget.getBounds(), activity, fullWidth)); + showcaseView.setShape(new RectangleShape(showcaseView.mTarget.getBounds(), fullWidth)); break; } case CIRCLE_SHAPE: { showcaseView.mShape = new CircleShape(showcaseView.mTarget); break; } - default: throw new IllegalArgumentException("Usupported shape type: " + shapeType); + default: + throw new IllegalArgumentException("Unsupported shape type: " + shapeType); } } + return showcaseView; } @@ -623,6 +648,8 @@ public void run() { } }, mDelayInMillis); + updateDismissButton(); + return true; } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java deleted file mode 100644 index 01a0391f..00000000 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/RectangleShape.java +++ /dev/null @@ -1,51 +0,0 @@ -package uk.co.deanwild.materialshowcaseview; - - -import android.app.Activity; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.util.DisplayMetrics; - -public class RectangleShape implements Shape { - - private Activity activity; - private Rect rect; - private boolean fullWidth; - - - public RectangleShape(Rect bounds) { - this(bounds, null, false); - } - - public RectangleShape(Rect bounds, Activity activity, boolean fullWidth) { - this.activity = activity; - this.fullWidth = fullWidth; - this.rect = getPreferredBounds(bounds, null, false); - } - - @Override - public void draw(Canvas canvas, Paint paint, int x, int y) { - canvas.drawRect(rect, paint); - } - - @Override - public void updateTarget(Target target) { - rect = getPreferredBounds(target.getBounds(), activity, fullWidth); - } - - public static Rect getPreferredBounds(Rect bounds, Activity activity, boolean fullWidth) { - Rect rect = new Rect(bounds.left - 10, bounds.top - 10, bounds.right + 10, bounds.bottom + 10); - if (activity != null && fullWidth) { - rect.left = 0; - rect.right = getDisplayMetrics(activity).widthPixels; - } - return rect; - } - - public static DisplayMetrics getDisplayMetrics(Activity context) { - DisplayMetrics displaymetrics = new DisplayMetrics(); - context.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics); - return displaymetrics; - } -} \ No newline at end of file diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java similarity index 68% rename from library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java rename to library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java index d5000d80..38c9f7aa 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/CircleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java @@ -1,15 +1,17 @@ -package uk.co.deanwild.materialshowcaseview; +package uk.co.deanwild.materialshowcaseview.shape; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; +import uk.co.deanwild.materialshowcaseview.Target; + /** * Circular shape for target. */ public class CircleShape implements Shape { - private int radius = 210; + private int radius = 200; public CircleShape(int radius) { this.radius = radius; @@ -33,7 +35,17 @@ public void updateTarget(Target target) { radius = getPreferredRadius(target.getBounds()); } + @Override + public int getWidth() { + return radius * 2; + } + + @Override + public int getHeight() { + return radius * 2; + } + public static int getPreferredRadius(Rect bounds) { - return Math.max(bounds.width(), bounds.height()) / 2 + 10; + return Math.max(bounds.width(), bounds.height()) / 2; } } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java new file mode 100644 index 00000000..e37a178d --- /dev/null +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java @@ -0,0 +1,62 @@ +package uk.co.deanwild.materialshowcaseview.shape; + + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; + +import uk.co.deanwild.materialshowcaseview.Target; + +public class RectangleShape implements Shape { + + private boolean fullWidth; + + private int width = 0; + private int height = 0; + + private Rect rect; + + public RectangleShape(Rect bounds) { + this(bounds, false); + } + + public RectangleShape(Rect bounds, boolean fullWidth) { + this.fullWidth = fullWidth; + height = bounds.height(); + if (fullWidth) + width = Integer.MAX_VALUE; + else width = bounds.width(); + init(); + } + + private void init() { + rect = new Rect(- width / 2, - height / 2, width / 2, height / 2); + } + + @Override + public void draw(Canvas canvas, Paint paint, int x, int y) { + rect.offset(x, y); + canvas.drawRect(rect, paint); + rect.offset(-x, -y); + } + + @Override + public void updateTarget(Target target) { + Rect bounds = target.getBounds(); + height = bounds.height(); + if (fullWidth) + width = Integer.MAX_VALUE; + else width = bounds.width(); + init(); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } +} \ No newline at end of file diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java similarity index 63% rename from library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java rename to library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java index 3deb801f..af459889 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Shape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java @@ -1,8 +1,10 @@ -package uk.co.deanwild.materialshowcaseview; +package uk.co.deanwild.materialshowcaseview.shape; import android.graphics.Canvas; import android.graphics.Paint; +import uk.co.deanwild.materialshowcaseview.Target; + /** * Specifies a shape of the target (e.g circle, rectangle) */ @@ -12,4 +14,8 @@ public interface Shape { void draw(Canvas canvas, Paint paint, int x, int y); + int getWidth(); + + int getHeight(); + } diff --git a/library/src/main/res/layout/showcase_content.xml b/library/src/main/res/layout/showcase_content.xml index ef7c9c74..01b096b6 100644 --- a/library/src/main/res/layout/showcase_content.xml +++ b/library/src/main/res/layout/showcase_content.xml @@ -30,5 +30,7 @@ android:paddingLeft="5dp" android:paddingRight="5dp" android:textColor="@android:color/white" - android:textSize="22dp" /> + android:textSize="22dp" + android:visibility="gone" /> + \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 1cb825e0..7d2a6aa4 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.application' +// This does not break the build when Android Studio is missing the JRebel for Android plugin. +apply plugin: 'com.zeroturnaround.jrebel.android' android { compileSdkVersion 23 diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java index d368c021..047549bf 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/CustomExample.java @@ -73,8 +73,8 @@ public void onClick(View v) { private void presentShowcaseView(int withDelay) { new MaterialShowcaseView.Builder(this) .setTarget(mButtonShow) - .setDismissText("GOT IT") .setContentText("This is some amazing feature you should know about") + .setDismissOnTouch(true) .setContentTextColor(getResources().getColor(R.color.green)) .setMaskColour(getResources().getColor(R.color.purple)) .setDelay(withDelay) // optional but starting animations immediately in onCreate can make them choppy diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java index cecc9bca..9cef9568 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java @@ -76,7 +76,14 @@ private void presentShowcaseSequence() { .build() ); - sequence.addSequenceItem(mButtonThree, "This is button three", "GOT IT"); + sequence.addSequenceItem( + new MaterialShowcaseView.Builder(this) + .setTarget(mButtonThree) + .setDismissText("GOT IT") + .setContentText("This is button three") + .withRectangleShape() + .build() + ); sequence.start(); From cac34836524fa1b47298fc13bf5176241b8183b8 Mon Sep 17 00:00:00 2001 From: Anton Danshin Date: Mon, 31 Aug 2015 16:16:04 +0300 Subject: [PATCH 3/7] Refactoring: - MaterialShowcaseSequence: added show/dismiss listeners - added shape to ShowcaseConfig - support for fixed shape constraints: shape.setAdjustToTarget(false) --- .../AnimationFactory.java | 4 +-- .../IAnimationFactory.java | 4 +-- .../IDetachedListener.java | 7 ++-- .../IShowcaseListener.java | 5 +-- .../MaterialShowcaseSequence.java | 30 ++++++++++++++--- .../MaterialShowcaseView.java | 6 ++-- .../materialshowcaseview/PrefsManager.java | 4 +-- .../materialshowcaseview/ShowcaseConfig.java | 17 ++++++++-- .../shape/CircleShape.java | 25 ++++++++++++-- .../shape/RectangleShape.java | 33 ++++++++++++++----- .../materialshowcaseview/shape/Shape.java | 15 +++++++-- .../{ => target}/Target.java | 6 ++-- .../{ => target}/ViewTarget.java | 6 ++-- .../SequenceExample.java | 7 ++++ 14 files changed, 120 insertions(+), 49 deletions(-) rename library/src/main/java/uk/co/deanwild/materialshowcaseview/{ => target}/Target.java (83%) rename library/src/main/java/uk/co/deanwild/materialshowcaseview/{ => target}/ViewTarget.java (91%) diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/AnimationFactory.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/AnimationFactory.java index 411cd0a3..7aefb353 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/AnimationFactory.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/AnimationFactory.java @@ -7,9 +7,7 @@ import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; -/** - * Created by deanwild on 05/08/15. - */ + public class AnimationFactory implements IAnimationFactory{ private static final String ALPHA = "alpha"; diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/IAnimationFactory.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/IAnimationFactory.java index 1d01d4ec..f63afd15 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/IAnimationFactory.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/IAnimationFactory.java @@ -3,9 +3,7 @@ import android.graphics.Point; import android.view.View; -/** - * Created by deanwild on 05/08/15. - */ + public interface IAnimationFactory { void fadeInView(View target, long duration, AnimationStartListener listener); diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/IDetachedListener.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/IDetachedListener.java index e6e6849c..e964d568 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/IDetachedListener.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/IDetachedListener.java @@ -1,9 +1,6 @@ package uk.co.deanwild.materialshowcaseview; -/** - * Created by deanwild on 24/08/15. - */ -interface IDetachedListener { - void onShowcaseDetached(MaterialShowcaseView showcaseView, boolean wasDismissed); +public interface IDetachedListener { + void onShowcaseDetached(MaterialShowcaseView showcaseView, boolean wasDismissed); } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/IShowcaseListener.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/IShowcaseListener.java index 1edfbeeb..c527b092 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/IShowcaseListener.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/IShowcaseListener.java @@ -1,10 +1,7 @@ package uk.co.deanwild.materialshowcaseview; -/** - * Created by deanwild on 11/08/15. - */ + public interface IShowcaseListener { void onShowcaseDisplayed(MaterialShowcaseView showcaseView); void onShowcaseDismissed(MaterialShowcaseView showcaseView); - } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseSequence.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseSequence.java index 2bdef5b2..2b4e9dd6 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseSequence.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseSequence.java @@ -6,9 +6,7 @@ import java.util.LinkedList; import java.util.Queue; -/** - * Created by deanwild on 11/08/15. - */ + public class MaterialShowcaseSequence implements IDetachedListener { PrefsManager mPrefsManager; @@ -18,6 +16,9 @@ public class MaterialShowcaseSequence implements IDetachedListener { private ShowcaseConfig mConfig; private int mSequencePosition = 0; + private OnSequenceItemShownListener mOnItemShownListener = null; + private OnSequenceItemDismissedListener mOnItemDismissedListener = null; + public MaterialShowcaseSequence(Activity activity) { mActivity = activity; mShowcaseQueue = new LinkedList<>(); @@ -55,6 +56,14 @@ public MaterialShowcaseSequence singleUse(String sequenceID) { return this; } + public void setOnItemShownListener(OnSequenceItemShownListener listener) { + this.mOnItemShownListener = listener; + } + + public void setOnItemDismissedListener(OnSequenceItemDismissedListener listener) { + this.mOnItemDismissedListener = listener; + } + public boolean hasFired() { if (mPrefsManager.getSequenceStatus() == PrefsManager.SEQUENCE_FINISHED) { @@ -99,6 +108,9 @@ private void showNextItem() { MaterialShowcaseView sequenceItem = mShowcaseQueue.remove(); sequenceItem.setDetachedListener(this); sequenceItem.show(mActivity); + if (mOnItemShownListener != null) { + mOnItemShownListener.onShow(sequenceItem, mSequencePosition); + } } else { /** * We've reached the end of the sequence, save the fired state @@ -120,6 +132,10 @@ public void onShowcaseDetached(MaterialShowcaseView showcaseView, boolean wasDis */ if (wasDismissed) { + if (mOnItemDismissedListener != null) { + mOnItemDismissedListener.onDismiss(showcaseView, mSequencePosition); + } + /** * If so, update the prefsManager so we can potentially resume this sequence in the future */ @@ -136,8 +152,12 @@ public void setConfig(ShowcaseConfig config) { this.mConfig = config; } + public interface OnSequenceItemShownListener { + void onShow(MaterialShowcaseView itemView, int position); + } - - + public interface OnSequenceItemDismissedListener { + void onDismiss(MaterialShowcaseView itemView, int position); + } } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java index 9129ac60..63da034c 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java @@ -31,11 +31,10 @@ import uk.co.deanwild.materialshowcaseview.shape.CircleShape; import uk.co.deanwild.materialshowcaseview.shape.RectangleShape; import uk.co.deanwild.materialshowcaseview.shape.Shape; +import uk.co.deanwild.materialshowcaseview.target.Target; +import uk.co.deanwild.materialshowcaseview.target.ViewTarget; -/** - * Created by deanwild on 04/08/15. - */ public class MaterialShowcaseView extends FrameLayout implements View.OnTouchListener, View.OnClickListener { private int mOldHeight; @@ -411,6 +410,7 @@ public void setConfig(ShowcaseConfig config) { setContentTextColor(config.getContentTextColor()); setDismissTextColor(config.getDismissTextColor()); setMaskColour(config.getMaskColor()); + setShape(config.getShape()); } private void updateDismissButton() { diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/PrefsManager.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/PrefsManager.java index 095b2763..8bad7031 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/PrefsManager.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/PrefsManager.java @@ -3,9 +3,7 @@ import android.content.Context; import android.content.SharedPreferences; -/** - * Created by deanwild on 11/08/15. - */ + public class PrefsManager { public static int SEQUENCE_NEVER_STARTED = 0; diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java index 580f8eb0..33722d7c 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java @@ -2,20 +2,23 @@ import android.graphics.Color; -/** - * Created by deanwild on 11/08/15. - */ +import uk.co.deanwild.materialshowcaseview.shape.CircleShape; +import uk.co.deanwild.materialshowcaseview.shape.Shape; + + public class ShowcaseConfig { public static final String DEFAULT_MASK_COLOUR = "#dd335075"; public static final long DEFAULT_FADE_TIME = 300; public static final long DEFAULT_DELAY = 0; + public static final Shape DEFAULT_SHAPE = new CircleShape(); private long mDelay = DEFAULT_DELAY; private int mMaskColour; private int mContentTextColor; private int mDismissTextColor; private long mFadeDuration = DEFAULT_FADE_TIME; + private Shape mShape = DEFAULT_SHAPE; public ShowcaseConfig() { mMaskColour = Color.parseColor(ShowcaseConfig.DEFAULT_MASK_COLOUR); @@ -62,4 +65,12 @@ public long getFadeDuration() { public void setFadeDuration(long fadeDuration) { this.mFadeDuration = fadeDuration; } + + public Shape getShape() { + return mShape; + } + + public void setShape(Shape shape) { + this.mShape = shape; + } } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java index 38c9f7aa..d4788d4a 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java @@ -4,7 +4,7 @@ import android.graphics.Paint; import android.graphics.Rect; -import uk.co.deanwild.materialshowcaseview.Target; +import uk.co.deanwild.materialshowcaseview.target.Target; /** * Circular shape for target. @@ -12,6 +12,10 @@ public class CircleShape implements Shape { private int radius = 200; + private boolean adjustToTarget = true; + + public CircleShape() { + } public CircleShape(int radius) { this.radius = radius; @@ -25,6 +29,22 @@ public CircleShape(Target target) { this(target.getBounds()); } + public void setAdjustToTarget(boolean adjustToTarget) { + this.adjustToTarget = adjustToTarget; + } + + public boolean isAdjustToTarget() { + return adjustToTarget; + } + + public int getRadius() { + return radius; + } + + public void setRadius(int radius) { + this.radius = radius; + } + @Override public void draw(Canvas canvas, Paint paint, int x, int y) { canvas.drawCircle(x, y, radius, paint); @@ -32,7 +52,8 @@ public void draw(Canvas canvas, Paint paint, int x, int y) { @Override public void updateTarget(Target target) { - radius = getPreferredRadius(target.getBounds()); + if (adjustToTarget) + radius = getPreferredRadius(target.getBounds()); } @Override diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java index e37a178d..9eba1cc8 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java @@ -5,17 +5,24 @@ import android.graphics.Paint; import android.graphics.Rect; -import uk.co.deanwild.materialshowcaseview.Target; +import uk.co.deanwild.materialshowcaseview.target.Target; public class RectangleShape implements Shape { - private boolean fullWidth; + private boolean fullWidth = false; private int width = 0; private int height = 0; + private boolean adjustToTarget = true; private Rect rect; + public RectangleShape(int width, int height) { + this.width = width; + this.height = height; + init(); + } + public RectangleShape(Rect bounds) { this(bounds, false); } @@ -29,6 +36,14 @@ public RectangleShape(Rect bounds, boolean fullWidth) { init(); } + public boolean isAdjustToTarget() { + return adjustToTarget; + } + + public void setAdjustToTarget(boolean adjustToTarget) { + this.adjustToTarget = adjustToTarget; + } + private void init() { rect = new Rect(- width / 2, - height / 2, width / 2, height / 2); } @@ -42,12 +57,14 @@ public void draw(Canvas canvas, Paint paint, int x, int y) { @Override public void updateTarget(Target target) { - Rect bounds = target.getBounds(); - height = bounds.height(); - if (fullWidth) - width = Integer.MAX_VALUE; - else width = bounds.width(); - init(); + if (adjustToTarget) { + Rect bounds = target.getBounds(); + height = bounds.height(); + if (fullWidth) + width = Integer.MAX_VALUE; + else width = bounds.width(); + init(); + } } @Override diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java index af459889..8c798f06 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java @@ -3,19 +3,30 @@ import android.graphics.Canvas; import android.graphics.Paint; -import uk.co.deanwild.materialshowcaseview.Target; +import uk.co.deanwild.materialshowcaseview.target.Target; /** - * Specifies a shape of the target (e.g circle, rectangle) + * Specifies a shape of the target (e.g circle, rectangle). + * Implementations of this interface will be responsible to draw the shape + * at specified center point (x, y). */ public interface Shape { void updateTarget(Target target); + /** + * Draw shape on the canvas with the center at (x, y) using Paint object provided. + */ void draw(Canvas canvas, Paint paint, int x, int y); + /** + * Get width of the shape. + */ int getWidth(); + /** + * Get height of the shape. + */ int getHeight(); } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/target/Target.java similarity index 83% rename from library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java rename to library/src/main/java/uk/co/deanwild/materialshowcaseview/target/Target.java index 73306c57..3d854819 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/Target.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/target/Target.java @@ -1,11 +1,9 @@ -package uk.co.deanwild.materialshowcaseview; +package uk.co.deanwild.materialshowcaseview.target; import android.graphics.Point; import android.graphics.Rect; -/** - * Created by deanwild on 04/08/15. - */ + public interface Target { Target NONE = new Target() { @Override diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/target/ViewTarget.java similarity index 91% rename from library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java rename to library/src/main/java/uk/co/deanwild/materialshowcaseview/target/ViewTarget.java index 1d17f6a8..8595a2ae 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ViewTarget.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/target/ViewTarget.java @@ -1,13 +1,11 @@ -package uk.co.deanwild.materialshowcaseview; +package uk.co.deanwild.materialshowcaseview.target; import android.app.Activity; import android.graphics.Point; import android.graphics.Rect; import android.view.View; -/** - * Created by deanwild on 04/08/15. - */ + public class ViewTarget implements Target { private final View mView; diff --git a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java index 9cef9568..0e75436b 100644 --- a/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java +++ b/sample/src/main/java/uk/co/deanwild/materialshowcaseviewsample/SequenceExample.java @@ -63,6 +63,13 @@ private void presentShowcaseSequence() { MaterialShowcaseSequence sequence = new MaterialShowcaseSequence(this, SHOWCASE_ID); + sequence.setOnItemShownListener(new MaterialShowcaseSequence.OnSequenceItemShownListener() { + @Override + public void onShow(MaterialShowcaseView itemView, int position) { + Toast.makeText(itemView.getContext(), "Item #" + position, Toast.LENGTH_SHORT).show(); + } + }); + sequence.setConfig(config); sequence.addSequenceItem(mButtonOne, "This is button one", "GOT IT"); From 279148405fea97a003a3cbe2538c94d3f6214da6 Mon Sep 17 00:00:00 2001 From: Anton Danshin Date: Mon, 31 Aug 2015 18:23:37 +0300 Subject: [PATCH 4/7] Refactoring: - added padding to shape + some javadocs to classes --- .../MaterialShowcaseView.java | 37 +++++++++++++++---- .../materialshowcaseview/ShowcaseConfig.java | 10 +++++ .../shape/CircleShape.java | 4 +- .../materialshowcaseview/shape/NoShape.java | 32 ++++++++++++++++ .../shape/RectangleShape.java | 11 ++++-- .../materialshowcaseview/shape/Shape.java | 9 +++-- .../src/main/res/layout/showcase_content.xml | 5 +-- .../CustomExample.java | 3 +- 8 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/NoShape.java diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java index 63da034c..b089c68a 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java @@ -29,12 +29,16 @@ import java.util.List; import uk.co.deanwild.materialshowcaseview.shape.CircleShape; +import uk.co.deanwild.materialshowcaseview.shape.NoShape; import uk.co.deanwild.materialshowcaseview.shape.RectangleShape; import uk.co.deanwild.materialshowcaseview.shape.Shape; import uk.co.deanwild.materialshowcaseview.target.Target; import uk.co.deanwild.materialshowcaseview.target.ViewTarget; +/** + * Helper class to show a sequence of showcase views. + */ public class MaterialShowcaseView extends FrameLayout implements View.OnTouchListener, View.OnClickListener { private int mOldHeight; @@ -47,6 +51,7 @@ public class MaterialShowcaseView extends FrameLayout implements View.OnTouchLis private int mXPosition; private int mYPosition; private boolean mWasDismissed = false; + private int mShapePadding = ShowcaseConfig.DEFAULT_SHAPE_PADDING; private View mContentBox; private TextView mContentTextView; @@ -166,7 +171,7 @@ protected void onDraw(Canvas canvas) { } // draw (erase) shape - mShape.draw(mCanvas, mEraser, mXPosition, mYPosition); + mShape.draw(mCanvas, mEraser, mXPosition, mYPosition, mShapePadding); // Draw the bitmap on our views canvas. canvas.drawBitmap(mBitmap, 0, 0, null); @@ -269,20 +274,20 @@ public void setTarget(Target target) { int midPoint = height / 2; int yPos = targetPoint.y; - int radius = Math.max(targetBounds.height(), targetBounds.width()) / 2 + 10; + int radius = Math.max(targetBounds.height(), targetBounds.width()) / 2; if (mShape != null) { mShape.updateTarget(mTarget); - radius = mShape.getHeight() / 2 + 10; + radius = mShape.getHeight() / 2; } if (yPos > midPoint) { // target is in lower half of screen, we'll sit above it mContentTopMargin = 0; - mContentBottomMargin = (height - yPos) + radius; + mContentBottomMargin = (height - yPos) + radius + mShapePadding; mGravity = Gravity.BOTTOM; } else { // target is in upper half of screen, we'll sit below it - mContentTopMargin = yPos + radius; + mContentTopMargin = yPos + radius + mShapePadding; mContentBottomMargin = 0; mGravity = Gravity.TOP; } @@ -360,11 +365,14 @@ private void setDismissTextColor(int textColour) { } } + private void setShapePadding(int padding) { + mShapePadding = padding; + } + private void setDismissOnTouch(boolean dismissOnTouch) { mDismissOnTouch = dismissOnTouch; } - private void setShouldRender(boolean shouldRender) { mShouldRender = shouldRender; } @@ -411,6 +419,7 @@ public void setConfig(ShowcaseConfig config) { setDismissTextColor(config.getDismissTextColor()); setMaskColour(config.getMaskColor()); setShape(config.getShape()); + setShapePadding(config.getShapePadding()); } private void updateDismissButton() { @@ -443,6 +452,7 @@ public void onGlobalLayout() { public static class Builder { private static final int CIRCLE_SHAPE = 0; private static final int RECTANGLE_SHAPE = 1; + private static final int NO_SHAPE = 2; private boolean fullWidth = false; private int shapeType = CIRCLE_SHAPE; @@ -543,6 +553,16 @@ public Builder withCircleShape() { return this; } + public Builder withoutShape() { + shapeType = NO_SHAPE; + return this; + } + + public Builder setShapePadding(int padding) { + showcaseView.setShapePadding(padding); + return this; + } + public Builder withRectangleShape() { return withRectangleShape(false); } @@ -561,9 +581,12 @@ public MaterialShowcaseView build() { break; } case CIRCLE_SHAPE: { - showcaseView.mShape = new CircleShape(showcaseView.mTarget); + showcaseView.setShape(new CircleShape(showcaseView.mTarget)); break; } + case NO_SHAPE: { + showcaseView.setShape(new NoShape()); + } default: throw new IllegalArgumentException("Unsupported shape type: " + shapeType); } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java index 33722d7c..32d6ecbf 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/ShowcaseConfig.java @@ -12,6 +12,7 @@ public class ShowcaseConfig { public static final long DEFAULT_FADE_TIME = 300; public static final long DEFAULT_DELAY = 0; public static final Shape DEFAULT_SHAPE = new CircleShape(); + public static final int DEFAULT_SHAPE_PADDING = 10; private long mDelay = DEFAULT_DELAY; private int mMaskColour; @@ -19,6 +20,7 @@ public class ShowcaseConfig { private int mDismissTextColor; private long mFadeDuration = DEFAULT_FADE_TIME; private Shape mShape = DEFAULT_SHAPE; + private int mShapePadding = DEFAULT_SHAPE_PADDING; public ShowcaseConfig() { mMaskColour = Color.parseColor(ShowcaseConfig.DEFAULT_MASK_COLOUR); @@ -73,4 +75,12 @@ public Shape getShape() { public void setShape(Shape shape) { this.mShape = shape; } + + public void setShapePadding(int padding) { + this.mShapePadding = padding; + } + + public int getShapePadding() { + return mShapePadding; + } } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java index d4788d4a..4d7f3cc3 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java @@ -46,8 +46,8 @@ public void setRadius(int radius) { } @Override - public void draw(Canvas canvas, Paint paint, int x, int y) { - canvas.drawCircle(x, y, radius, paint); + public void draw(Canvas canvas, Paint paint, int x, int y, int padding) { + canvas.drawCircle(x, y, radius + padding, paint); } @Override diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/NoShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/NoShape.java new file mode 100644 index 00000000..1866d4bf --- /dev/null +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/NoShape.java @@ -0,0 +1,32 @@ +package uk.co.deanwild.materialshowcaseview.shape; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import uk.co.deanwild.materialshowcaseview.target.Target; + +/** + * A Shape implementation that draws nothing. + */ +public class NoShape implements Shape { + + @Override + public void updateTarget(Target target) { + // do nothing + } + + @Override + public void draw(Canvas canvas, Paint paint, int x, int y, int padding) { + // do nothing + } + + @Override + public int getWidth() { + return 0; + } + + @Override + public int getHeight() { + return 0; + } +} diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java index 9eba1cc8..2c37be87 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java @@ -49,9 +49,14 @@ private void init() { } @Override - public void draw(Canvas canvas, Paint paint, int x, int y) { - rect.offset(x, y); - canvas.drawRect(rect, paint); + public void draw(Canvas canvas, Paint paint, int x, int y, int padding) { + canvas.drawRect( + rect.left + x - padding, + rect.top + y - padding, + rect.right + x - padding, + rect.bottom + y - padding, + paint + ); rect.offset(-x, -y); } diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java index 8c798f06..53bdfa90 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/Shape.java @@ -12,12 +12,10 @@ */ public interface Shape { - void updateTarget(Target target); - /** * Draw shape on the canvas with the center at (x, y) using Paint object provided. */ - void draw(Canvas canvas, Paint paint, int x, int y); + void draw(Canvas canvas, Paint paint, int x, int y, int padding); /** * Get width of the shape. @@ -29,4 +27,9 @@ public interface Shape { */ int getHeight(); + /** + * Update shape bounds if necessary + */ + void updateTarget(Target target); + } diff --git a/library/src/main/res/layout/showcase_content.xml b/library/src/main/res/layout/showcase_content.xml index 01b096b6..efd81d63 100644 --- a/library/src/main/res/layout/showcase_content.xml +++ b/library/src/main/res/layout/showcase_content.xml @@ -5,10 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="30dp" - android:paddingLeft="15dp" - android:paddingRight="20dp" - android:paddingTop="30dp"> + android:padding="16dp"> Date: Mon, 31 Aug 2015 18:26:59 +0300 Subject: [PATCH 5/7] missing break :) --- .../co/deanwild/materialshowcaseview/MaterialShowcaseView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java index b089c68a..a9805edd 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java @@ -586,6 +586,7 @@ public MaterialShowcaseView build() { } case NO_SHAPE: { showcaseView.setShape(new NoShape()); + break; } default: throw new IllegalArgumentException("Unsupported shape type: " + shapeType); From 8c802574a7cbdad8ce492c79d8efda9d6cb29864 Mon Sep 17 00:00:00 2001 From: Anton Danshin Date: Tue, 1 Sep 2015 16:05:25 +0300 Subject: [PATCH 6/7] fix: padding on RectangleShape --- .../deanwild/materialshowcaseview/shape/RectangleShape.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java index 2c37be87..ee671b6c 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java @@ -53,8 +53,8 @@ public void draw(Canvas canvas, Paint paint, int x, int y, int padding) { canvas.drawRect( rect.left + x - padding, rect.top + y - padding, - rect.right + x - padding, - rect.bottom + y - padding, + rect.right + x + padding, + rect.bottom + y + padding, paint ); rect.offset(-x, -y); From 62c7e8aeeb902b0cbfaa273d53ead44c688212f7 Mon Sep 17 00:00:00 2001 From: Anton Danshin Date: Thu, 3 Sep 2015 15:19:59 +0300 Subject: [PATCH 7/7] A couple of minor improvements + added hasFired() method to the view + do not draw shape if it is empty, i.e. radius == 0 or rect.isEmpty() --- .../MaterialShowcaseView.java | 4 ++++ .../materialshowcaseview/shape/CircleShape.java | 4 +++- .../shape/RectangleShape.java | 17 +++++++++-------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java index a9805edd..b2d22bd3 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/MaterialShowcaseView.java @@ -433,6 +433,10 @@ private void updateDismissButton() { } } + public boolean hasFired() { + return mPrefsManager.hasFired(); + } + /** * REDRAW LISTENER - this ensures we redraw after activity finishes laying out */ diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java index 4d7f3cc3..7a51f015 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/CircleShape.java @@ -47,7 +47,9 @@ public void setRadius(int radius) { @Override public void draw(Canvas canvas, Paint paint, int x, int y, int padding) { - canvas.drawCircle(x, y, radius + padding, paint); + if (radius > 0) { + canvas.drawCircle(x, y, radius + padding, paint); + } } @Override diff --git a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java index ee671b6c..f06d9901 100644 --- a/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java +++ b/library/src/main/java/uk/co/deanwild/materialshowcaseview/shape/RectangleShape.java @@ -50,14 +50,15 @@ private void init() { @Override public void draw(Canvas canvas, Paint paint, int x, int y, int padding) { - canvas.drawRect( - rect.left + x - padding, - rect.top + y - padding, - rect.right + x + padding, - rect.bottom + y + padding, - paint - ); - rect.offset(-x, -y); + if (!rect.isEmpty()) { + canvas.drawRect( + rect.left + x - padding, + rect.top + y - padding, + rect.right + x + padding, + rect.bottom + y + padding, + paint + ); + } } @Override