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?
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);
}