Unable to get Fragment Manager from RecyclerView.Adapter - android

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.

Related

FragmentManager.findFragmentByTag() returns null

I have found a specific case when FragmentManager.findFragmentByTag("tag") returns null.
I have a gut feeling it has to do with timing?
I have a networking library with the following callbacks:
onStart()
{
Utils.ShowLoadingDialog("loading");
}
onFinnish()
{
Utils.DismissLoadingDialog("loading");
}
Then in my Utils class I have the following code:
public void showLoadingDialog(String title, String message, String tag) {
DialogFragment loadingDialogFragment = new LoadingDialogFragment();
Bundle args = new Bundle();
args.putString(CommonBundleAttributes.CONNECTING_ACTIVITY_DIALOG_TITLE, title);
args.putString(CommonBundleAttributes.CONNECTING_ACTIVITY_DIALOG_MESSAGE, message);
loadingDialogFragment.setArguments(args);
FragmentTransaction transaction = fragManager.beginTransaction();
loadingDialogFragment.show(transaction, tag);
}
public void dismissLoadingDialog(String tag) {
DialogFragment dg = (DialogFragment) fragManager.findFragmentByTag(tag);
if (dg != null) {
// this reference isn't null so the dialog is available
dg.dismissAllowingStateLoss();
}
}
Now this generally works fine. However in cases when the network layer detects there is no internet. It will throw an error and then immediately call onFinnish(). In this case the Utils.DismissDialog(tag) does nto find the fragment and therefore does not dismiss it?
You can use executePendingTransactions() to wait for the fragment transaction to come through.
public void dismissLoadingDialog(String tag) {
fragManager.executePendingTransactions();
DialogFragment dg = (DialogFragment) fragManager.findFragmentByTag(tag);
if (dg != null) {
// this reference isn't null so the dialog is available
dg.dismissAllowingStateLoss();
}
}
Use TRY-CATCH or even IF statement to check current internet-connection.
This can be case with the committing the transaction.
Just check if in your show method if you have commit the transaction for Dialog fragment or not.
transaction.commit();
Until that your fragment manager does not have fragment to be added in that.
Also you need to make sure you are finding fragment from same Activity's fragment manager to which you committed fragment.

Application crashes when there is no fragment in container.How to avoid this?

My below code is working when there is a fragment but when there is no fragment application crashes....how to avoid this...i can't understand answers given in previous asked queston
FloatingActionButton bt_home = (FloatingActionButton) findViewById(R.id.home);
bt_home.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getFragmentManager().beginTransaction().remove(getFragmentManager().findFragmentById(R.id.myFrame)).commit();
}
});
Basically, NullPointerException is thrown when an application attempts to use an object reference that has the null value.
So add a null check on the fragment before using it.
Try this:
FragmentManager fm = getFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.myFrame);
if (fragment != null) {
fm.beginTransaction().remove(fragment).commit();
}

Null pointer exception when calling fragment method

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

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.

getActivity returns null in Fragment

I have a fragment which is basically a list view. The parent activity calls a method to retrieve a list of roster items from a service. When the data returns from the service I call updateRosterItems on the fragment passing through and ArrayList of Roster items. The problem is that it works the first time through, but then when I select a different tab, and then come back to the tab with the fragment, the getActivity() returns null and I can't hook up the data to the ArrayAdapter.
This is the code for the updateRosterItems function:
public void updateRosterList(ArrayList<RosterInfo> rosterItems)
{
if(_adapter == null)
{
_adapter = new RosterItemAdapter(getActivity(), R.layout.roster_listview_item, rosterItems);
}
Activity activity = getActivity();
if(activity != null)
{
ListView list = (ListView)activity.findViewById(R.id.lstRosterItems);
list.setAdapter(_adapter);
_adapter.notifyDataSetChanged();
}
}
I've read about similar issues caused by code being called before the fragment is attached. I guess my question is, is there a way to delay the call to the updateRosterList until after the onAttach is called? The solution I'm toying with is that if getActivity() returns null then store the data in private variable in the fragment, and in the onAttach method check if there is data in the varialbe and then call the update on the adapter. This seems a bit hacky though. Any ideas?
UPDATE: I've managed to get it working by doing this. I'm quite new to Android development and it seems a bit hacky to me as a solution. Is there a better way? Basically the updateRosterList function is the one that is called from outside of the fragment.
public class RosterListFragment extends Fragment {
RosterItemAdapter _adapter = null;
private ArrayList<RosterInfo> _items;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.roster_listview, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
if(_items != null)
{
performUpdateRosterList(_items);
}
}
public void updateRosterList(ArrayList<RosterInfo> rosterItems)
{
Activity activity = getActivity();
if(activity != null)
{
performUpdateRosterList(rosterItems);
}
else
{
_items = rosterItems;
}
}
private void performUpdateRosterList(ArrayList<RosterInfo> rosterItems)
{
Activity activity = getActivity();
if(_adapter == null)
{
_adapter = new RosterItemAdapter(activity, R.layout.roster_listview_item, rosterItems);
}
ListView list = (ListView)activity.findViewById(R.id.lstRosterItems);
list.setAdapter(_adapter);
_adapter.notifyDataSetChanged();
}
}
You are correct, the activity isn't yet attached. There's two ways to handle this.
Don't make the changes until after the activity has been attached. Perhaps just save off rosterItems, and have it updated later.
Pass in the context into your updater function.
Personally, I would say the first is probably be better path, but either one could work fine.

Categories

Resources