4

Research has been fruitless because all other references to this error seem to be reliably repeatable; I'm unable to consistently repeat this error:

08-22 17:32:25.216  22699-22699/com.smbds.punisher11 E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.IllegalArgumentException: width and height must be > 0
            at android.graphics.Bitmap.createBitmap(Bitmap.java:1023)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:1002)
            at android.graphics.Bitmap.createBitmap(Bitmap.java:985)
            at com.github.amlcurran.showcaseview.ShowcaseView.updateBitmap(ShowcaseView.java:171)
            at com.github.amlcurran.showcaseview.ShowcaseView.onGlobalLayout(ShowcaseView.java:354)
            at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:655)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2054)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1190)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4860)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:766)
            at android.view.Choreographer.doCallbacks(Choreographer.java:575)
            at android.view.Choreographer.doFrame(Choreographer.java:542)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:751)
            at android.os.Handler.handleCallback(Handler.java:725)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:158)
            at android.app.ActivityThread.main(ActivityThread.java:5751)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1083)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:850)
            at dalvik.system.NativeStart.main(Native Method)

As I stated above, this error does not occur all the time. It seems to happen about (but not exactly) 2-3 times, then 2-3 runs go through without a problem.

Here is my resources file:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container_fragment_person_list"
    android:tag="PersonListFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragments.PersonListFragment">

    <TextView
        android:id="@+id/textView_add_guardian_placeholder"
        android:layout_centerInParent="true"
        android:layout_width="1dp"
        android:layout_height="1dp" />

    <TextView
        android:id="@+id/textView_intro_placeholder"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:minWidth="1dp"
        android:minHeight="1dp"
        android:layout_width="1dp"
        android:layout_height="1dp" />

    <ListView
        android:visibility="gone"
        android:id="@id/android:list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:orientation="vertical"
        android:id="@id/android:empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center" >

        <TextView
            android:text="@string/no_people"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageButton
            android:id="@+id/imageButton_empty_text"
            android:src="@drawable/ic_man_silhouette_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@string/generic_content_description"/>

        <TextView
            android:id="@+id/test"
            android:text="@string/tap_icon_to_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

</RelativeLayout>

And here is my java. Please notice that I even tried hard-coding a width and height for my ViewTarget, but still get inconsistent errors. I also tried creating placeholder TextViews in my resources file, but still no go.

public class PersonListFragment extends BaseListFragment {

    private PersonAdapter personAdapter;
    private PersonModel personModel;
    private int personCount;
    private TextView tvTargetIntro;

    public static PersonListFragment newInstance(String tag, Long index) {
        PersonListFragment fragment = new PersonListFragment();
        Bundle args = new Bundle();
        args.putString(TAG, tag);
        args.putLong(INDEX, index);
        fragment.setArguments(args);
        return fragment;
    }

    public PersonListFragment() {
    }

    @SuppressWarnings("UnusedDeclaration")
    public void onEventMainThread(People_FetchedEvent ignored) {
        refreshList();
    }

    @SuppressWarnings("UnusedDeclaration")
    public void onEventMainThread(Person_CreatedEvent ignored) {
        String createType;
        if(ignored.getId() == 0 | ignored.getPerson().getId() == null) {
            createType = resources.getString(R.string.added);
        } else {
            createType = resources.getString(R.string.updated);
        }
        MessageManager.getInstance().makeToast(getActivity(), ignored.getPerson().getName(), " ", createType);
        fm.popBackStack();
        refreshList();
    }

    @SuppressWarnings("UnusedDeclaration")
    public void onEventMainThread(Person_BuiltEvent ignored) {
        makeCreatePersonBackgroundTask(ignored.getPerson());
    }

    @SuppressWarnings("UnusedDeclaration")
    public void onEventMainThread(Person_DeletingEvent ignored) {
        personModel.deletePersonByLocalId(ignored.getId());
        MessageManager.getInstance().makeToast(getActivity(), PersonController.getInstance().getCurrentPerson().getName() + " " + resources.getString(R.string.removed));
        refreshList();
        FragmentManager fm = getChildFragmentManager();
        fm.popBackStack();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        personModel = PersonModel.getInstance();
        personAdapter = new PersonAdapter(getActivity().getLayoutInflater(), getActivity(), ListType.PROFILE_INFO);
        setListAdapter(personAdapter);
        personCount = PersonModel.getInstance().getPersonCount();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_person_list, container, false);

        LinearLayout layoutEmptyList = (LinearLayout) view.findViewById(android.R.id.empty);
        tvTargetIntro = (TextView) view.findViewById(R.id.textView_intro_placeholder);
        ImageButton imageButtonEmptyText = (ImageButton) view.findViewById(R.id.imageButton_empty_text);
        imageButtonEmptyText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentTransaction ft;
                ft = fm.beginTransaction();
                ft.replace(R.id.container_fragment_person_list, PersonEditFragment.newInstance("PERSON_EDIT_FRAGMENT", (long) 0, new Person()), "PERSON_EDIT_FRAGMENT");
                ft.addToBackStack(null);
                ft.commit();
                fm.executePendingTransactions();
            }
        });


        switch(personCount) {
            case 0:
                break;
            case 1:
                break;
            default:
        }

        return view;
    }

    private void makeShowCaseIntro() {
        ViewTarget target1 = new ViewTarget(tvTargetIntro);
        tvTargetIntro.setWidth(1);
        tvTargetIntro.setHeight(1);
        ShowcaseView sv = new ShowcaseView.Builder(getActivity())
                .setTarget(target1)
                .setContentTitle(resources.getString(R.string.showcase_intro_title))
                .setContentText(resources.getString(R.string.showcase_intro_text))
                .setStyle(R.style.CustomShowcaseTheme2)
                .build();
        sv.setButtonText(resources.getString(R.string.next));
        sv.show();
    }

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

        makeShowCaseIntro();

    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        PersonController.getInstance().setCurrentPerson(personAdapter.getItem(position));

        FragmentTransaction ft = fm.beginTransaction();

        ft.replace(R.id.container_fragment_person_list, PersonDisplayFragment.newInstance("PERSON_DISPLAY_FRAGMENT", PersonController.getInstance().getLocalId(), PersonController.getInstance().getCurrentPerson()), "PERSON_DISPLAY_FRAGMENT");
        ft.addToBackStack(null);
        ft.commit();
        fm.executePendingTransactions();
    }

    private void refreshList() {
        new SimpleBackgroundTask<LazyList<Person>>(getActivity()) {

            @Override
            protected LazyList<Person> onRun() {
                return PersonModel.getInstance().lazyLoadPeople();
            }

            @Override
            protected void onSuccess(LazyList<Person> result) {
                personAdapter.replaceLazyList(result);
            }
        }.execute();
    }

    private void makeCreatePersonBackgroundTask(Person person) {
        jobManager.addJobInBackground(new CreatePersonJob(person));
        fm.popBackStack();
    }

    @Override
    public void onResume() {
        super.onResume();
        jobManager.addJobInBackground(new FetchPeopleJob());
        if(dataDirty) {
            refreshList();
            dataDirty = false;
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        FragmentTransaction ft;
        Person person = PersonController.getInstance().getCurrentPerson();
        ft = fm.beginTransaction();
        switch(item.getItemId()) {
            case R.id.action_new:
                ft.replace(R.id.container_fragment_person_list, PersonEditFragment.newInstance("PERSON_EDIT_FRAGMENT", (long) 0, new Person()), "PERSON_EDIT_FRAGMENT");
                ft.addToBackStack(null);
                ft.commit();
                fm.executePendingTransactions();
                return true;
            case R.id.action_edit:
                ft.replace(R.id.container_fragment_person_list, PersonEditFragment.newInstance("PERSON_EDIT_FRAGMENT", person.getId(), person), "PERSON_EDIT_FRAGMENT");
                ft.addToBackStack(null);
                ft.commit();
                fm.executePendingTransactions();
                return true;
            case R.id.action_remove:
                String title = MessageManager.getInstance().makeString(resources.getString(R.string.remove), " ", person.getName());
                String message = MessageManager.getInstance().makeString(resources.getString(R.string.message_confirm_remove), " ", person.getName(), resources.getString(R.string.punctuation_question));
                final InfoDialogFragment dialog = InfoDialogFragment.newInstance(title, message, DialogActionType.Delete, DialogActionTarget.Person, person.getId());
                dialog.show(ft, DIALOG_TAG);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

}

I'm pretty sure this is some timing issue with the way android draws out objects, but I don't have the knowledge to know why they're drawn correctly on some runs but not on others.

Please don't zap this question unless you're sure that it has been asked elsewhere with respect to an intermittent problem :-)

Any help is appreciated.

zba
  • 103
  • 1
  • 2
  • 7
  • Move your `makeShowCaseIntro()` method to the `onResume` method – Simon Marquis Aug 22 '14 at 22:20
  • Sorry, forgot to mention I've moved it into onCreateView, onResume, onActivityCreated. – zba Aug 22 '14 at 22:24
  • On your code, `tvTargetIntro` is null when executing makeShowCaseIntro(). – Simon Marquis Aug 22 '14 at 22:29
  • Really? I thought it is assigned a value in onCreateView and onCreateView is called after onActivityCreated/onResume? – zba Aug 22 '14 at 22:32
  • I also think you don't have the latest version of this library. And take a look at the issues section on the library, a lot of people have the same issue: https://github.com/amlcurran/ShowcaseView/issues/179 – Simon Marquis Aug 22 '14 at 22:36
  • I think you're right! Thank you for the help but I can't seem to find a Maven or Gradle reference for the newest version. Can you tell me how to get the latest version? – zba Aug 22 '14 at 22:44
  • If you're using grade, add this dependency: `'com.github.amlcurran.showcaseview:library:5.0.0'` or directly download the source from their github – Simon Marquis Aug 22 '14 at 22:45
  • Actually, this is the depenency I'm using: 'com.github.amlcurran.showcaseview:library:5.1.0-SNAPSHOT@aar' Is this the wrong version? – zba Aug 22 '14 at 22:46
  • Try to use the official stable version (5.0.0) https://github.com/amlcurran/ShowcaseView – Simon Marquis Aug 22 '14 at 22:49
  • Made changes but still crashing every 2-3 times... But i'll keep an eye on that github issues log. Thanks for the help. – zba Aug 22 '14 at 22:59
  • Here is a quick workaround. Try to display the ShowcaseView with a `postDelayed(new Runnabe() {... /* display here */ ...}, 300 /* 3000ms */);` – Simon Marquis Aug 22 '14 at 23:01

4 Answers4

8

This is a known issue : Github issues when updateBitmap() is called too early.
This issue comes from Bitmap.createBitmap() method either width or height equals to 0.
A workaround is to delay the ShowcaseView creation more:

someView.post(new Runnable() {
    @Override
    public void run() {
        // display ShowcaseView here
    }
});

You could also change the updateBitmap() method from the library to not create the bitmap if the view width or height is wrong.

Simon Marquis
  • 7,248
  • 1
  • 28
  • 43
0

Had the same problem, and posted my solution in another thread: https://stackoverflow.com/a/25521608/1752670

I changed the ShowcaseView class slightly

Community
  • 1
  • 1
Wessel du Plooy
  • 505
  • 5
  • 18
0

go to com.github.amlcurran.showcaseview.ShowcaseView and change this method

private void updateBitmap() {
        if ((bitmapBuffer == null || haveBoundsChanged()) && getMeasuredHeight() > 0 && getMeasuredWidth() > 0) {
            if(bitmapBuffer != null)
                bitmapBuffer.recycle();
            int width = getMeasuredWidth();
            int height = getMeasuredHeight();
            if(width > 0 && height > 0)
                bitmapBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        }
    }
AZ_
  • 21,688
  • 25
  • 143
  • 191
0

This was a bug in ShowcaseView, and was fixed starting in v5.4.2: https://github.com/amlcurran/ShowcaseView/commit/c79c60dc798f081fb62e48c549c7bdbff8da51b9

So, you can just update your build.gradle to:

compile 'com.github.amlcurran.showcaseview:library:5.4.2'

Sean Barbeau
  • 11,496
  • 8
  • 58
  • 111