Search code examples
javaandroidxmlfragmentfragmenttransaction

Trying to replace main_activity fragment with another fragment onbuttonclick


I'm trying to make a button on the welcome screen essentially, that when clicked, starts a fragment.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/touch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="me.alexoladele.quotebook.MainActivity">

    <TextView
        android:id="@+id/welcome_screen_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_gravity="center_horizontal|top"
        android:layout_marginTop="144dp"
        android:gravity="center"
        android:text="The QuoteBook hosts Kanye West quotes. Enjoy"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="36dp" />

    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal|bottom"
        android:layout_marginBottom="47dp"
        android:clickable="true"
        android:elegantTextHeight="false"
        android:enabled="true"
        android:text="Start!" />

</FrameLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity {
int count = 0;
final QuoteFragment quoteFragment = new QuoteFragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    Button startButton = (Button) findViewById(R.id.start_button);
    if (startButton != null)
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              quoteFragment.startSecondFragment();

            }
        });



    }



}

quote_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:id="@+id/real_quote_frag">
<fragment
    android:id="@+id/quote_frag"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="me.alexoladele.quotebook.QuoteFragment"
    tools:layout="@layout/activity_main">
<TextView
    android:text="@string/tap_screen"
    android:id="@+id/quote"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fontFamily="sans-serif-medium"
    android:textSize="26sp"
    android:gravity="center"
    android:layout_gravity="center"
    android:textColor="#a6a6a6" />

<TextView
    android:text="@string/quotist_name"
    android:visibility="visible"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fontFamily="sans-serif-medium"
    android:textSize="26sp"
    android:gravity="center"
    android:textColor="#585858"
    android:layout_gravity="center_horizontal|bottom"
    android:id="@+id/person" />
</fragment>

QuoteFragment.java:

public class QuoteFragment extends Fragment {
int count = 0;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.quote_fragment, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    FrameLayout touch = (FrameLayout) getView().findViewById(R.id.touch);
    final TextView quoteText = (TextView)  getView().findViewById(R.id.quote);

    // Makes quotes array
    String[] quotes = {
            "Quotes Placeholder"
    };

    // Put quotes array into arraylist
    final List<String> quoteList = new ArrayList<>(Arrays.asList(quotes));

    //
    if (touch != null)
        touch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (count >= quoteList.size()) {
                    count = 0;
                }
                Quote q = new Quote("");
                q.setQuote(quoteList.get(count));

                quoteText.setText(q.getQuote());
                count++;
            }
        });

}

public void startSecondFragment() {
    QuoteFragment myFragment = new QuoteFragment();
    FragmentManager manager = getFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.replace(R.id.real_quote_frag, myFragment, "Quote");
    transaction.addToBackStack(null);
    transaction.commit();

    }
}

I get this error :

 FATAL EXCEPTION: main
                                                                             java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.FragmentTransaction android.app.FragmentManager.beginTransaction()' on a null object reference
                                                                              at me.alexoladele.quotebook.QuoteFragment.startSecondFragment(QuoteFragment.java:91)
                                                                              at me.alexoladele.quotebook.MainActivity$1.onClick(MainActivity.java:23)
                                                                              at android.view.View.performClick(View.java:4756)
                                                                              at android.view.View$PerformClick.run(View.java:19761)
                                                                              at android.os.Handler.handleCallback(Handler.java:739)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                              at android.os.Looper.loop(Looper.java:135)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5253)
                                                                              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:900)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:695)
                                                                              at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:114)Bridge.java:114)

Can anybody help me figure out what I'm doing wrong, as well as point me to the more efficient way of handling this?


Solution

  • The problem

    You have not attached your QuoteFragment to your MainActivity, this would mean your getFragmentManager() call in QuoteFragment will return null, because it would essentially make a call to the parent activity that it is attached to, which in your case would be null and hence the NullPointerException. (Simply holding a reference to the fragment as your field is not enough)

    The better approach

    Here's the thing, it is highly recommended (and you really should to make your life easier) that you manage your Fragment from your Activity.

    Right now your are doing Fragment transactions in your QuoteFragment, instead you should be managing it in your MainActivity.

    So here's how it would look like, in your MainActivity:

    Button startButton = (Button) findViewById(R.id.start_button);
        if (startButton != null)
            startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startSecondFragment(); // start your fragment from MainActivity
            }
        });
    
    // This method will be in charge of handling your second fragment
    private void startSecondFragment() {
        FragmentManager manager = getFragmentManager();
        FragmentTransaction transaction = manager.beginTransaction();
        transaction.replace(R.id.real_quote_frag, quoteFragment, "Quote");
        transaction.addToBackStack(null);
        transaction.commit(); 
    }
    

    UPDATE:

    To help you get a better understanding, here's how my activity and fragment structure looks like:

    activity_main.xml

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <!-- The view where I place my fragment -->
        <FrameLayout
            android:id="@+id/fragmentContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    

    MainActivity.java where I replace my fragments:

    FragmentManager fragmentManager = getSupportFragmentManager();
    
    // Note how I place my fragment into fragmentContent layout
    fragmentManager.beginTransaction().replace(R.id.fragmentContent, fragment).commit();
    

    fragment_layout.xml

    Note how I have a separate fragment layout xml file to place INTO fragmentContent in `activity_main.xml1

    <FrameLayout 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"
        tools:context="com.forcasie.joelmin.Fragment.HomeFragment">
    
        <!-- TODO: Update blank fragment layout -->
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">
            <TextView
                android:textStyle="bold"
                android:textSize="25sp"
                android:text="@string/homeMessage"
                android:gravity="center_horizontal"
                android:layout_marginTop="40dp"
                android:layout_gravity="center_horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            </LinearLayout>
    
    </FrameLayout>
    

    Fragment.java where I SET the actual layout for the fragment with the above layout file:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
    
        return inflater.inflate(R.layout.fragment_layout, container, false);
    }