2

I use the last version of ShowcaseView from here

When I tried to use it with demo app and activity it works but when I tried to use it with fragments it crashed my app with no logcat errors

     @override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);

            final ImageButton saveButton = (ImageButton) view.findViewById(R.id.button_Save);
            ViewTarget viewTarget = new ViewTarget(saveButton);

            showcaseView = new ShowcaseView.Builder(getActivity())
                    .setTarget(viewTarget)
                    .setContentTitle("Reset Button")
                    .setContentText("This will erase all the data in the table except the line that in progress")
                    .build();
        }

what could be the problem here?

EDIT1:

when I tried to do the same thing in my fragmentactivity and didn't work, but when I did the same thing but took a view that declared in the fragmentactivity and not in the fragment it worked.

EDIT2: I managed to get the error from the logcat.

FATAL EXCEPTION: main
java.lang.IllegalArgumentException: width and height must be > 0
        at android.graphics.Bitmap.createBitmap(Bitmap.java:724)
        at android.graphics.Bitmap.createBitmap(Bitmap.java:703)
        at android.graphics.Bitmap.createBitmap(Bitmap.java:670)
        at com.github.amlcurran.showcaseview.ShowcaseView.updateBitmap(ShowcaseView.java:169)
        at com.github.amlcurran.showcaseview.ShowcaseView.onGlobalLayout(ShowcaseView.java:343)
        at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:839)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2050)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1249)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6364)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
        at android.view.Choreographer.doCallbacks(Choreographer.java:591)
        at android.view.Choreographer.doFrame(Choreographer.java:561)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
        at android.os.Handler.handleCallback(Handler.java:730)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:176)
        at android.app.ActivityThread.main(ActivityThread.java:5419)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
        at dalvik.system.NativeStart.main(Native Method)

I understand the error but what is the cause for it?

Alex K
  • 5,092
  • 15
  • 50
  • 77

5 Answers5

9

In my opinion the best solution is to place the code within the "post" callback of the view, this way it will be executed when everything is rendered.

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view.post(new Runnable() {
        @Override
        public void run() {
            // your showcase initialisation code here
            showcase();
        }
    });
}

I tested it and worked like a charm.

Giordano
  • 1,401
  • 15
  • 26
  • 1
    I'm still encountering the issue when using this. I am allowing the user to show the tutorial again so I am calling the method for the tutorials when the user clicks on a menu item. But it sometimes works, then sometimes it will throw the IllegalargumentException, I can't even catch the exception as a workaround – John Ernest Guadalupe Dec 22 '15 at 05:28
6

This appears to be a timing issue. If the call happens before the views are actually drawn on screen, it fails saying the width and height should be > 0. This is clear from the source code of ShowcaseView, in updateBitmap() function, where it fails:

bitmapBuffer = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);

where getMeasuredWidth() and getMeasuredHeight() will be zero before actually drawing.

The work around to this issue is to call this with a good delay. I currently use it with a 5s delay and call it from onResume(), with the help of a Handler

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        new ShowcaseView.Builder(mActivity)
                            .setTarget(new ViewTarget(saveMatchButton))
                            .setContentTitle("This is a demo")
                            .build();
    }
}, 5000);
Aswin Kumar
  • 5,158
  • 5
  • 34
  • 39
4

I think a better solution is to change the ShowcaseView class (Function updateBitmap()). Passing any non-zero dimension values to the createBitmap() function when getMeasure* returns 0 would ensure the bitmap is created with a valid size. Since updateBitmap() function will be called again as soon as the view gets is actual size, this poses no problem.

private void updateBitmap() 
{
    if (bitmapBuffer == null || haveBoundsChanged()) 
    {
        if(bitmapBuffer != null)
            bitmapBuffer.recycle();

        bitmapBuffer = Bitmap.createBitmap(getMeasuredWidth()  > 0 ? getMeasuredWidth()  : 1, 
                                           getMeasuredHeight() > 0 ? getMeasuredHeight() : 1, 
                                           Bitmap.Config.ARGB_8888);
    }
}
Wessel du Plooy
  • 505
  • 5
  • 18
  • I like this answer and I checked the code, updateBitmap is indeed called twice. I don't understand why it is called the first time since it is giving so much trouble. – Yoann Hercouet Jun 22 '15 at 11:09
  • called once onGlobalLayout and once when you setTarget, see the view tree observer – AZ_ Jul 06 '15 at 02:36
1

According to this answer, the ShowcaseView should be built in onCreateView().

Community
  • 1
  • 1
Michael Litvin
  • 3,976
  • 1
  • 34
  • 40
0

Even you can write it down in RESUME() and make sure if you are writing in resume then you must have to maintain a flag which will be used to avoid issue of building more then one showcase one above on whenever OnResume() will be called.

if (!mShowCaseVisible){
ActionViewTarget target = new ActionViewTarget(getActivity(), ActionViewTarget.Type.HOME);
showcaseView = new ShowcaseView.Builder(getActivity(), true)
.setTarget(target)
.setContentTitle("Home Screen")
.setOnClickListener(this)
.setContentText("Click on top left corner to seemenu.")
.doNotBlockTouches()`enter code here`
.hideOnTouchOutside()
.singleShot(42)
.build();
showcaseView.setButtonText("Next");
mShowCaseVisible = true;
}
Dhruvit Darji
  • 205
  • 2
  • 14