0

Based off the template IntelliJ Android application, I'm trying to make an activity that slides from one fragment to another when a TextView is pressed.

However, the onTouch in my activity can't find the original fragment. When I touch the relevant TextView, the application crashes, and log messages indicate a NullPointerException (see below).

What's the correct way to retrieve an existing fragment from within an onTouch event?

Here's the code:

public class MainActivity extends FragmentActivity implements View.OnTouchListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if (view.getId() == R.id.textView2) {
            FragmentManager fm = getSupportFragmentManager();
            Fragment originalFragment = fm.findFragmentById(R.layout.fragment_main);

            System.out.println(("===============\noriginal Fragment is ") +
                (originalFragment == null ? "" : "not ") +
                "null\n===============");

            fm.beginTransaction()  // <-- line 84
                    .setCustomAnimations(R.animator.slide_in_right,
                            R.animator.slide_out_right,
                            R.animator.slide_in_right,
                            R.animator.slide_out_right)
                    .replace(originalFragment.getId(), Fragment
                                    .instantiate(this, AddQuestionFragment.class.getName()),
                            TAG)
                    .addToBackStack(null).commit();
            }

        }
        return false;
    }

    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

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

            TextView v = (TextView) rootView.findViewById(R.id.textView2);
            TextView v1 = (TextView) rootView.findViewById(R.id.textView3);

            View.OnTouchListener activityAsListener = (View.OnTouchListener) getActivity();
            v.setOnTouchListener(activityAsListener);
            v1.setOnTouchListener(activityAsListener);

            return rootView;
        }
    }

    public static class AddQuestionFragment extends Fragment {
        public AddQuestionFragment() {}

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

            return rootView;
        }
    }
}

res/layout/main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    tools:ignore="MergeRootFrame" />

res/layout/fragment_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity$PlaceholderFragment">

    <TextView
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:id="@+id/textView"/>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true" android:layout_alignParentStart="true">
    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="@string/add_question_title"
            android:id="@+id/textView2"/>
    </LinearLayout>

</RelativeLayout>

and the log messages:

01-08 17:39:42.904    6555-6555/com.scubbo.lifetracker.app I/art﹕ Late-enabling -Xcheck:jni
01-08 17:40:08.125    6555-6555/com.scubbo.lifetracker.app I/System.out﹕ ===============
01-08 17:40:08.125    6555-6555/com.scubbo.lifetracker.app I/System.out﹕ original Fragment is null
01-08 17:40:08.125    6555-6555/com.scubbo.lifetracker.app I/System.out﹕ ===============
01-08 17:40:08.126    6555-6555/com.scubbo.lifetracker.app E/InputEventReceiver﹕ Exception dispatching input event.
01-08 17:40:08.126    6555-6555/com.scubbo.lifetracker.app E/MessageQueue-JNI﹕ Exception in MessageQueue callback: handleReceiveCallback
01-08 17:40:08.136    6555-6555/com.scubbo.lifetracker.app E/MessageQueue-JNI﹕ java.lang.NullPointerException: Attempt to invoke virtual method 'int android.support.v4.app.Fragment.getId()' on a null object reference
            at com.scubbo.lifetracker.app.MainActivity.onTouch(MainActivity.java:84)
            at android.view.View.dispatchTouchEvent(View.java:8382)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2314)
            at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1692)
            at android.app.Activity.dispatchTouchEvent(Activity.java:2739)
            at android.support.v7.app.ActionBarActivityDelegateICS$WindowCallbackWrapper.dispatchTouchEvent(ActionBarActivityDelegateICS.java:268)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2275)
            at android.view.View.dispatchPointerEvent(View.java:8578)
            at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4021)
            at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3887)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3502)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3468)
            at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3578)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3476)
            at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3635)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3502)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3468)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3476)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
            at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5701)
            at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5675)
            at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5646)
            at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5791)
            at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
            at android.os.MessageQueue.nativePollOnce(Native Method)
            at android.os.MessageQueue.next(MessageQueue.java:143)
            at android.os.Looper.loop(Looper.java:122)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
01-08 17:40:08.137    6555-6555/com.scubbo.lifetracker.app D/AndroidRuntime﹕ Shutting down VM
    --------- beginning of crash
01-08 17:40:08.138    6555-6555/com.scubbo.lifetracker.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.scubbo.lifetracker.app, PID: 6555
    java.lang.NullPointerException: Attempt to invoke virtual method 'int android.support.v4.app.Fragment.getId()' on a null object reference
            at com.scubbo.lifetracker.app.MainActivity.onTouch(MainActivity.java:84)
            at android.view.View.dispatchTouchEvent(View.java:8382)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2314)
            at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1692)
            at android.app.Activity.dispatchTouchEvent(Activity.java:2739)
            at android.support.v7.app.ActionBarActivityDelegateICS$WindowCallbackWrapper.dispatchTouchEvent(ActionBarActivityDelegateICS.java:268)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2275)
            at android.view.View.dispatchPointerEvent(View.java:8578)
            at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4021)
            at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3887)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3502)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3468)
            at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3578)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3476)
            at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3635)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3502)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3468)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3476)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
            at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5701)
            at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5675)
            at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5646)
            at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5791)
            at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
            at android.os.MessageQueue.nativePollOnce(Native Method)
            at android.os.MessageQueue.next(MessageQueue.java:143)
            at android.os.Looper.loop(Looper.java:122)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
01-08 17:40:10.095    6555-6555/com.scubbo.lifetracker.app I/Process﹕ Sending signal. PID: 6555 SIG: 9

EDIT: I think I've found part of the problem. By changing the code to read:

[...]
PlaceholderFragment fg = new PlaceholderFragment();
System.out.println("id of fg is " + ((Integer)fg.getId()).toString());
getSupportFragmentManager().beginTransaction()
    .add(R.id.container, fg)
    .commit();
[...]
FragmentManager fm = getSupportFragmentManager();
System.out.println("Searching for fragment with id " + ((Integer) R.layout.fragment_main).toString());
Fragment originalFragment = fm.findFragmentById(R.layout.fragment_main);
[...]

I got:

01-08 18:11:40.873  20001-20001/com.scubbo.lifetracker.app I/System.out﹕ id of fg is 0
01-08 18:12:04.395  20001-20001/com.scubbo.lifetracker.app I/System.out﹕ Searching for fragment with id 2130903066

I'm surprised to see that the original Fragment has id 0 (why is that?), but I guess that explains why it couldn't be found - 0 != 2130903066. I still don't know how best to fetch that original fragment, though.


This answer suggests that I should reference Fragments by R.id.<foo>, rather than R.layout.<bar> - but it also expects a fragment definition to begin with <fragment, whereas the template provided by IntelliJ has them starting in media res (e.g. <RelativeLayout). Time for some more tinkering...

Community
  • 1
  • 1
scubbo
  • 4,969
  • 7
  • 40
  • 71

3 Answers3

2

Attempt to invoke virtual method 'int android.support.v4.app.Fragment.getId()' on a null object reference

Because originalFragment is null.

Use add(int containerViewId, Fragment fragment, String tag) version of add method if you want to get id of Fragment by calling findFragmentById.

ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
  • Interesting - `add(int containerViewId, Fragment fragment, String tag)` was what I originally used (based on [this](http://trickyandroid.com/fragments-translate-animation/) ), but that meant that the second fragment appeared over the top of the first. I wanted the first fragment to slide out at the same time. Is that not possible? – scubbo Jan 08 '15 at 18:20
2

You could also use a normal onClickListener and start your new fragment replace in that (similar to how you already show PlaceholderFragment in your onCreate)

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                                       .add(R.id.container, new PlaceholderFragment())
                                       .commit();
        }
    }

    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

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

            TextView v = (TextView) rootView.findViewById(R.id.textView2);
            TextView v1 = (TextView) rootView.findViewById(R.id.textView3);

            View.OnClickListener onClickListener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getActivity().getSupportFragmentManager()
                        .beginTransaction()  // <-- line 84
                        .setCustomAnimations(R.animator.slide_in_right,
                                             R.animator.slide_out_right,
                                             R.animator.slide_in_right,
                                             R.animator.slide_out_right)
                        .replace(R.id.container, new AddQuestionFragment(), "TAG_AddQuestionFragment")
                        .addToBackStack(null).commit();
                }
            };

            v.setOnClickListener(onClickListener);
            v1.setOnClickListener(onClickListener);

            return rootView;
        }
    }

    public static class AddQuestionFragment extends Fragment {
        public AddQuestionFragment() {}

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

            return rootView;
        }
    }
}
petey
  • 16,914
  • 6
  • 65
  • 97
0

Cracked it! Because of the aforementioned unexpected behaviour with IDs, I had to search by Tag instead. The following works:

[...]
if (savedInstanceState == null) {
    PlaceholderFragment fg = new PlaceholderFragment();
    getSupportFragmentManager().beginTransaction()
            .add(R.id.container, fg, MAIN_FRAGMENT_TAG)
            .commit();
}
[...]
Fragment originalFragment = fm.findFragmentByTag(MAIN_FRAGMENT_TAG);
Fragment f = fm.findFragmentByTag(TAG);
fm.beginTransaction()
    .setCustomAnimations(R.animator.slide_in_right,
        R.animator.slide_out_left,
        R.animator.slide_in_left,
        R.animator.slide_out_right)
    .replace(originalFragment.getId(),
             Fragment.instantiate(this, AddQuestionFragment.class.getName()),
             TAG)
    .addToBackStack(null).commit();
scubbo
  • 4,969
  • 7
  • 40
  • 71