Null pointer exception when calling fragment method - android

I have been stuck on this for awhile, I am loading a fragment from within an activity (replacing another). It loads fine and I can see it and return to the previous fragment, but when I call a method to load data into it I get a null pointer exception. My first code was this...
public class MainActivity extends Activity implements MainListFragment.MainListListener, StoryFragment.StoryListener {
MainListFragment mainListFragment;
StoryFragment storyFragment;
.
.
.
public void onMainListClick(DBRecordType recordType, int recordID){
switch(recordType){
case story:{
DBHandler dbHandler = new DBHandler(this, null, null, 1);
Story story = dbHandler.getStory(recordID);
Toast.makeText(getApplicationContext(), story.toString(), Toast.LENGTH_SHORT ).show();
storyFragment = new StoryFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container, storyFragment);
transaction.addToBackStack(null);
transaction.commit();
storyFragment.loadStory(story);
break;
}
I am getting this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.EditText.setText(java.lang.CharSequence)' on a null object reference
at com.maniblett.listtest.StoryFragment.loadStory(StoryFragment.java:60)
at com.maniblett.listtest.MainActivity.onMainListClick(MainActivity.java:104)
The method in the fragment is setting text in an edittext, here's the fragment method I am calling mName and mSummary are references to edit text widgets :
public void loadStory(Story story){
mName.setText(story.get_name());
mSummary.setText(story.get_summary());
}
Is it legal to refer to a fragment like this (calling a method on the same reference I used to add the fragment via the fragment manager)? It seems like I already have a reference to the fragment, so using find fragment by ID would be redundant but when I double checked everything I read seemed to indicate I needed to find the fragment using findFragmentByID or Tag first so changed my code to this...
storyFragment = new StoryFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container, storyFragment, "loadedFragment");
transaction.addToBackStack(null);
transaction.commit();
StoryFragment loadedFragment = (StoryFragment)getFragmentManager().findFragmentByTag("loadedFragment");
loadedFragment.loadStory(story);
break;
But I get a similar error, which is:
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.maniblett.listtest.StoryFragment.loadStory(com.maniblett.listtest.datamodel.Story)' on a null object reference
at com.maniblett.listtest.MainActivity.onMainListClick(MainActivity.java:107)
at com.maniblett.listtest.MainListFragment.onListItemClick(MainListFragment.java:156)
at android.app.ListFragment$2.onItemClick(ListFragment.java:160)
at android.widget.AdapterView.performItemClick(AdapterView.java:300)
I have verified the object I am sending is not null after creating it (the 'story' in the case statement is an enum). Again, the frgament loads and runs fine if I comment out the method call, it is just when I call the emthod on it it fails. so, I guess I don't have the actual fragment that is loaded? Can someone tell me what I'm doing wrong? (I did search many other similar topics but couldn't find anything which helped, I'm pretty new to android). Thanks to any who take the time to help!
StoryFragment class:
public class StoryFragment extends Fragment
{
StoryListener activityCallback;
private EditText mName;
private EditText mSummary;
public interface StoryListener {
public void onAddButtonClick(String text);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_story, container, false);
Button button = (Button)rootView.findViewById(R.id.button);
mName = (EditText)rootView.findViewById(R.id.name);
mSummary = (EditText)rootView.findViewById(R.id.summary);
button.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
DBHandler dbHandler = new DBHandler(getActivity().getBaseContext(), null, null, 1);
Story story = new Story(mName.getText().toString(),mSummary.getText().toString());
dbHandler.addStory(story);
activityCallback.onAddButtonClick("Story"); //use same callback for all record types passing back record type created?
}
} );
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try { activityCallback = (StoryListener) activity;
}
catch (ClassCastException e) {
throw new ClassCastException(activity.toString()+ " must implement StoryListener");
}
}
public void loadStory(Story story){
mName.setText(story.get_name());
mSummary.setText(story.get_summary());
}
}

Well, you should understand how android works in the first place. The onCreateView method gets called only when the fragment is going to come visible. So when you call loadstory immediately after the FragmentTransaction method its obvious you'll get an NullPointer Exception
My solution:
Declare two variables in the StoryFragment as name and summary
Change the loadStory method is like this
public void loadStory(Story story){
this.name = story.get_name();
this.summary = story.get_summary();
}
Finally in the OnCreateView method of StoryFragment after change here appropriately
mName = (EditText)rootView.findViewById(R.id.name);
mSummary = (EditText)rootView.findViewById(R.id.summary);
//if your fragment is also gonna be called by some other manner you should check for null in this.name and this.summary before setting it to the `TextView`
mName.setText(this.name);
mSummary.setText(this.summary);

Thank you. That worked perfectly. Actually, what I did was create a private Story type and then in StoryFragment.loadStory I assigned it.
public void loadStory(Story story){
mStory = story;
}
I also was able to then see that it works fine to not have to search for the fragment using findFragmentByID, this part worked:
storyFragment = new StoryFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container, storyFragment);
transaction.addToBackStack(null);
transaction.commit();
storyFragment.loadStory(story);
Thanks very much for pointing me in the right direction here

Related

Unable to get Fragment Manager from RecyclerView.Adapter

I'm attempting to transit out of a recycler view into a target fragment, but it seems like I can't get the fragment manager to do so. When I try to build, I get the following error:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.FragmentManager android.support.v7.app.AppCompatActivity.getSupportFragmentManager()' on a null object reference
The code below is what's causing the error, specifically the line with getSupportManager().
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
final Course course = courses.get(i);
if (course != null)
{
viewHolder.course = course;
viewHolder.tvLine1.setText(course.getName());
viewHolder.tvLine2.setText(course.getCourse_code());
viewHolder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = ((AppCompatActivity) context).getSupportFragmentManager();
CourseViewFragment viewFragment = new CourseViewFragment();
Bundle args = new Bundle();
args.putString("CourseID", course.getId());
args.putString("CourseName", course.getName());
args.putString("CourseCode", course.getCourse_code());
args.putString("StartAt", course.getStart_at());
args.putString("EndAt", course.getEnd_at());
viewFragment.setArguments(args);
fm.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(android.R.id.content, viewFragment)
.addToBackStack(null)
.commit();
}
});
}
}
The problem is not in your adapter. It seems that the context is null when you click the item.
Normally it is caused by destroyed Activities. Here's one scenario:
The adapter receive the context via constructor or setter, and keep the reference to the activity.
The Activity is destroyed for some reason (user action, low memory, etc.)
The context that the adapter has becomes null
You get NPE.
So please check where you call adapter. Maybe the answer is in there.

Transferring data from 1 fragment to another fragment

Currently trying to use a bundle to transfer information from both my IncomeFragment and ExpenseFragment to HomeFragment but I'm unsure as to how to do it. I've tried implementing doubleA's code which he provided.
This is my onAcceptClicked method from my MainActivity which takes the value of the total income/expense from the relevant fragment and transfers it to the HomeFragment:
public void onAcceptClicked(String fragment, String total) {
final FragmentManager fm = getFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
if (fragment == "income") {
HomeFragment homeFrag = new HomeFragment();
Bundle incomeBundle = new Bundle();
incomeBundle.putString(IncomeFragment.TAG, total);
//homeFrag.newInstance(total);
ft.replace(R.id.content_layout, homeFrag, HomeFragment.TAG);
ft.commit();
}
else if (fragment == "expense"){
HomeFragment homeFragment = new HomeFragment();
Bundle expenseBundle = new Bundle();
expenseBundle.putString("bundleIncome", total);
homeFragment.setArguments(expenseBundle);
ft.replace(R.id.content_layout, homeFragment, HomeFragment.TAG);
ft.commit();
}
}
I have an interface in my IncomeFragment which I use to communicate with my MainActivity so I can use the onAcceptClicked method to transfer my totals over. I plan on basically doing the same thing with my ExpenseFragment. The code below is a snippet from my IncomeFragment:
public interface SendIncomeData {
public void onAcceptClicked(String fragment, String total);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_accept:
//Toast.makeText(getActivity(), stringIncomeTotal, Toast.LENGTH_LONG).show();
sendIncomeData.onAcceptClicked("income", stringIncomeTotal);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Unfortunately I'm getting an error with this line of code
sendIncomeData.onAcceptClicked("income", stringIncomeTotal);
This is the error
java.lang.NullPointerException: Attempt to invoke interface method 'void mos.myapplication.IncomeFragment$SendIncomeData.onAcceptClicked(java.lang.String, java.lang.String)' on a null object reference
I don't know why it's saying there's a null object reference and/or how I could fix this error.
I'm guessing there's probably going to be an error displaying my totals in my HomeFragment because I haven't called the method below anywhere within my code in my MainActivity or my IncomeFragment / ExpenseFragment. The reason I haven't used it is because I wasn't sure how to get it so that the HomeFragment opens first when the application is launched.
static HomeFragment newInstance(String total)
{
HomeFragment frag = new HomeFragment();
Bundle args = new Bundle();
args.putString(TAG, total);
frag.setArguments(args);
return frag;
}
I don't even mind starting from scratch, as long as I can transfer totals and display them from IncomeFragment > HomeFragment and also ExpenseFragment > HomeFragment
You need to check if bundle is null in the first place:
bundle = this.getArguments();
if (bundle != null)
{
// continue with your logic
}
Also, all Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly. See below links for more info:
http://developer.android.com/training/basics/fragments/communicating.html
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
A lot of people will argue that using interfaces is the best way to do that, which isn't really wrong but there are much simpler ways. Edit: not necessarily simpler
-One way is to get a reference to the activity within the fragment by calling
this.getActivity();
then in your activity class you can have a method that passes the data to the other fragment since it has a reference to both fragments.
-Then there's an even better way to do it actually (though that first part is probably helpful too):
In the fragment that has the data that needs to be moved, you can get a reference to the other fragment from the FragmentManager. This assumes that you created that fragment with a string ID like so:
Fragment otherFragment;
getSupportFragmentManager().beginTransaction().add(otherFragment, "otherFrag").commit();
Then in the fragment with the data, you'll do:
FragmentActivity fragmentActivity = (FragmentActivity)getActivity();
OtherFragment otherFragment = (OtherFragment)fragmentActivity.getSupportFragmentManager().findFragmentByTag("otherFrag");
Then with a reference to OtherFragment (in this case), you can do something like:
otherFragment.dataString = "myData";
But you'll probably want to have a test to make sure the other fragment doesn't come back null, and if it is null you might just want to create it then and there since you already have a reference to the FragmentManager.
Edit: I'm just going to say that I like this method more, subjectively.

EditText.setText() Null Pointer Exception

I have this NPE that is driving me crazy, maybe it's just "tunnel vision" but I can't solve it. I have a fragment inside an activity, and in the activity's onCreate() I instantiate fragment. Then later when the fragment is added to activity I call fragments method from the activity. The code is below
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.ActionInputFragment, settingsFragment).addToBackStack(null);
transaction.commit();
if(jsonUserData != null)
settingsFragment.loadUserData(jsonUserData);
In the fragment I have:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.ctx = getActivity();
edtFullName = (EditText) getActivity().findViewById(R.id.edtFullNameSettings);
edtFullName.addTextChangedListener(new TextWatcher .....);
}
Adding the textwatcher works. Below (in the same fragment) is the method whose call generates the error:
public void loadUserData(JSONObject jsonUserData) {
String fullName;
try {
fullName = String.format("%s %s", jsonUserData.getString("first_name"), jsonUserData.getString("last_name"));
} catch (JSONException e) {
fullName = "";
}
//if(edtFullName != null)
this.edtFullName.setText(fullName);
}
I have checked the layout and the editText id is there.
the problem is that this.edtFullName is not yet initialized because onActivityCreated is not called yet thus edtFullName is null upon calling setText() method
You can pass the reference of the EditText in the loadUserData instead in the onActivityCreated

Pattern for Activity / Fragment in android

I've a activity which basically is :
public class FragmentContainer extends FragmentActivityBase implements IRefreshListener {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().getExtras() == null
|| getIntent().getExtras().get("type") == null) {
showProductList();
}
else
{
if (getIntent().getExtras().get("type").equals("customer"))
showCustomerList();
}
#Override
public void showProductList() {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
// load the product list
ProductList fragment = new ProductList();
fragmentTransaction.replace(R.id.fragment_container, fragment)
.addToBackStack(null);
fragmentTransaction.commit();
}
.....
}
in the fragment, I use onCreateView to get intent and then I create my view.
If I need to change the fragment, I get the reference to the parent Activity (taken from onAttach) and I call method referenced by the IRefreshListener.
like :
IRefreshListener mCallback;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception.
try {
mCallback = (IRefreshListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement IRefreshListener");
}
}
public void callCustomer() {
mCallback.showCustomerList();
}
It works but whne I change the orientation, even I use setRetainInstance(true) it will be reseted.
I have 2 questions :
Do I use the good pattern to manage my application. The big activity which contains one fragment become bigger with the time
How should I handle orientation change ?
Regards
I do not find this pattern is more perfect or best one, although it is or was a suggestion from Google. Because it could be a worse coding style if fragment knows particular activity or listeners, you might write more and more code, when you wanna to let your fragment know more its "container" or "parents". Will the fragment later be used for other activity which has not been implemented with IRefreshListener etc, you will code much more.
My introduce is using Otto-Bus or Event-Bus. You can just send message from one to one. Every one doesn't have to know each other.

Android fragments - findFragmentByTag always returns null

I've had a look around and found a couple of questions with a similar topic, but nothing that helped in my case.
I'm trying to access an existing active fragment using getSupportFragmentManager().findFragmentByTag(TAG), but it always returns null. Replies on similar questions suggested that it takes a while for the commit to be executed, so calling findFragmentByTag would return null if called too early. I've tried two things:
add getSupportFragmentManager().executePendingTransactions()
immediately after the commit, but still get null.
added a button... pressing this after the activity has been created,
the fragment registered and the view displayed should leave the
system enough time to commit. But i still get null.
Here's my activity:
public class MainActivity extends ActionBarActivity {
private static final String F_SETTINGS = "f_settings";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
debug();
}
});
if (savedInstanceState == null) {
FSettings newFragment = new FSettings();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, newFragment);
ft.addToBackStack(F_SETTINGS);
ft.commit();
// getSupportFragmentManager().executePendingTransactions();
//// Activating this did not make any difference...
}
debug();
}
private void debug() {
String txt = "null";
Fragment frag = getSupportFragmentManager().findFragmentByTag(F_SETTINGS);
if (frag != null) {
txt = frag.toString();
}
Log.i("Testing", txt);
}
}
What am I doing wrong here?
Cheers,
Max
In your code you haven't mentioned tag in replace method So,
Use this structure of replace method of fragment
ft.replace(R.id.container, newFragment,"fragment_tag_String");
Refer this link for more information. fragment replace with tag name

Categories

Resources