Unable to send Strings from one Fragment to another via Bundles - java

I am trying to send two strings via a bundle to the next fragment but the app crashes even before I get to the first fragment.
I get this error in the second fragment:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.guessit, PID: 22360
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference
at com.example.android.guessit.GameFlow.FragmentAnswer1.onCreateView(FragmentAnswer1.java:125)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execSingleAction(FragmentManagerImpl.java:1696)
at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:299)
at androidx.fragment.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:259)
at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1244)
at androidx.viewpager.widget.ViewPager.setCurrentItemInternal(ViewPager.java:669)
at androidx.viewpager.widget.ViewPager.setCurrentItemInternal(ViewPager.java:631)
at androidx.viewpager.widget.ViewPager.setCurrentItem(ViewPager.java:612)
at com.example.android.guessit.GameFlow.GameActivity.setViewPager(GameActivity.java:152)
at com.example.android.guessit.GameFlow.FragmentOneCategory$6.onClick(FragmentOneCategory.java:190)
at android.view.View.performClick(View.java:7352)
at android.view.View.performClickInternal(View.java:7318)
at android.view.View.access$3200(View.java:846)
at android.view.View$PerformClick.run(View.java:27800)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7050)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
I know that this means that the string value that I am trying to receive in the second fragment means that there is no value for that string but I don't know where the problem here is because the strings I am trying to send are not null.
This is the code I am using for the bundle in the first fragment (using this in onCreatView):
FragmentAnswer1 frag1 = new FragmentAnswer1();
Bundle args = new Bundle();
args.putString("question_1", question);
args.putString("answer_1", answer);
frag1.setArguments(args);
getFragmentManager().beginTransaction().add(R.id.container, frag1).commit(); // there is a message that says that "beginTransaction" may produce a nullPointer
Here is the code to receive the strings in the second fragment (currently also in onCreateView)
question = getArguments().getString("question_1"); // error is in this line
answer = getArguments().getString("answer_1");
question1.setText(question);
rightAnswer.setText(answer);
I already tried to move the above code into the onStartmethod but I still get the same error. I also tried using this solution but its not working for me. I did not find any other solution or method on how to resolve the problem.
Please let me know if you need more informations. Any help is much appreciated!

I have now found the correct way to send data from one Fragment to another while having a ViewPager. We have to use a ViewModel.
First we have to create a ViewModel, in this case our goal is to send a String from Fragment 1 to Fragment 2. Here is the code for the ViewModel with Getter and Setter method for the String:
public class ViewModelString extends ViewModel {
private MutableLiveData<String> mString = new MutableLiveData<>();
public MutableLiveData<String> getmString() {
return mString;
}
public void setmString(MutableLiveData<String> mString) {
this.mString = mString;
}
}
Then in Fragment 1 from where we want to sent the string we need to first initialize the ViewModel in onCreate.
public class Fragment1 extends Fragment {
private ViewModelString viewModel;
public Fragment1() {
// Required empty public constructor
}
public static Fragment1 newInstance() {
return new Fragment1();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize ViewModel here
viewModel = ViewModelProviders.of(requireActivity()).get(ViewModelString.class);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
String hello = "hello";
viewModel.setmString(hello);
}
}
Then in the second Fragment we also have to first initialize the viewModel and then create an observer. Here we then take the String that is stored in the ViewModel and set a TextView to its value:
public class Fragment2 extends Fragment {
private ViewModelString viewModel;
public Fragment2() {
// Required empty public constructor
}
public static Fragment2 newInstance() {
return new Fragment2();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize ViewModel here
viewModel = ViewModelProviders.of(requireActivity()).get(ViewModelString.class);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
TextView textView = findViewById(R.id.yourTextViewId);
// Gets the string
viewModel.getmString().observe(requireActivity(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
textView.setText(s);
}
});
}
...
}
I hope this helps anyone! Please feel free to edit my post.

Use Bundle to send String:
//Put the value
Bundle args = new Bundle();
args.putString("YourKey", "YourValue");
YourFragmentName ldf = new YourFragmentName ();
ldf.setArguments(args);
//Inflate the fragment
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.container, ldf).commit();
In onCreateView of the new Fragment:
//Retrieve the value
Bundle bundle = this.getArguments();
if (bundle != null) {
String value = bundle.getString("YourKey");
}

Use a Bundle. Here's an example:
Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt("Key", "value");
fragment.setArguments(bundle);
fragmentManager.beginTransaction().replace(R.id.container, frag1).commit()
Bundle has put methods for lots of data types. See this
Then in your Fragment, retrieve the data in onCreate() method like:
Bundle bundle = this.getArguments();
if (bundle != null) {
int myInt = bundle.getString(Key);
}

try this.
var fragment: MainFragment =MainFragment()
val args = Bundle()
args.putString("question_1", "My question")
args.putString("answer_1", "My answer")
fragment.setArguments(args)
fragmentManager.beginTransaction().add(R.id.fragment_content, fragment).commit()

Related

How do I update my Fragment after data modification in my activity (SQLite)?

I have a ListView in a Fragment that is populated when the app starts.
I put a ParcelableArrayList in a Bundle in my newInstance method, and I get it back in my OnCreateView after passing the ArrayList in the newInstance method in my Activity (which is the data read from the SQLite database).
This part works, as I display my data in my Fragment correctly.
I implemented a button that removes all data from the table, and I would now like to update my view after I cleaned the table.
The remove all button is handled in my main activity where I call my database handler to empty the table.
What is the best way to do that ? Here are the parts of the code that seem relevant to me :
My Fragment class :
public class MainFragment extends Fragment {
public static final String RECETTES_KEY = "recettes_key";
private List<Recette> mRecettes;
private ListView mListView;
public MainFragment() {
// Required empty public constructor
}
public static MainFragment newInstance(List<Recette> r) {
MainFragment fragment = new MainFragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(RECETTES_KEY, (ArrayList<? extends Parcelable>) r);
fragment.setArguments(bundle);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRecettes = getArguments().getParcelableArrayList(RECETTES_KEY);
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
configureListView();
}
// Configure ListView
private void configureListView(){
this.mListView = getView().findViewById(R.id.activity_main_list_view);
RecetteAdapter adapter = new RecetteAdapter(getContext(), mRecettes);
mListView.setAdapter(adapter);
}
}
Relevant parts from my Main acivity :
This is in my OnCreate method :
mDatabaseHandler = new DatabaseHandler(this);
mRecettes = mDatabaseHandler.readRecettes();
mDatabaseHandler.close();
This is in the method I use to show a fragment :
if (this.mMainFragment == null) this.mMainFragment = MainFragment.newInstance(mRecettes);
this.startTransactionFragment(this.mMainFragment);
Let me know if I should add more of my code, this is my first time posting :)
Lucile
In your R.layout.fragment_main, you can add an id to the root view, say with android:id#+id/fragment_root
And whenever you want to change the fragment view:
In activity:
MainFragment fragment = (MainFragment) getSupportFragmentManager().getFragmentById(R.id.fragment_root);
fragment.updateList(mRecettes);
And then create the new method updateList() in your MainFragment
public void updateList(List<Recette> recettes) {
mRecettes.clear();
mRecettes.addAll(recettes);
configureListView();
}
Also you can tag your fragment when you add it to your transaction instead of using its id, and then use getSupportFragmentManager().getFragmentByTag()

Android update listview after changing data using sharedpreferences

I'm trying to implement PreferenceFragmentCompat and SharedPreferences.OnSharedPreferenceChangeListener.
My app consists of main activity and fragments. The home fragment has a list of URLs with a title, and I would like to add a setting to add a URL to this list. This is what I've tried so far:
Here's the SettingsFragment.java:
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preference preference = findPreference(key);
if (preference instanceof EditTextPreference) {
EditTextPreference editTextPreference = (EditTextPreference) preference;
String value = editTextPreference.getText();
new HomeFragment().addLink(value);
} else {
assert preference != null;
preference.setSummary(sharedPreferences.getString(key, ""));
}
}
And the HomeFragment.java:
private ArrayList<LinkItem> urls = new ArrayList<>(Arrays.asList(
new LinkItem("LifeHacker RSS Feed", "https://lifehacker.com/rss"),
new LinkItem("Google News Feed", "https://news.google.com/news/rss");
private LinkItemAdapter itemAdapter;
private ListView listView;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
listView = view.findViewById(R.id.postListView);
itemAdapter = new LinkItemAdapter(getActivity(), R.layout.link_item, urls);
listView.setAdapter(itemAdapter);
listView.setOnItemClickListener(onItemClickListener);
itemAdapter.notifyDataSetChanged();
return view;
}
void addLink(String title) {
urls.add(new LinkItem(title, "https://google.com"));
itemAdapter.notifyDataSetChanged();
}
private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HomeFragmentDirections.ActionHomeFragmentToRssFragment action =
HomeFragmentDirections.actionHomeFragmentToRssFragment(urls.get(position).Link, urls.get(position).Title);
NavHostFragment.findNavController(HomeFragment.this).navigate(action);
}
};
If I try doing it like this, the itemAdapter will be null, crashing the app, so I am unsure of how to implement this. If I try recreating it in addLink like in the onCreate method of HomeFragment, the activity ends up being null. If I try passing the activity or the context from settings fragment, the same result occurs.
LinkItemAdapter adapts the following object:
public class LinkItem {
public String Title;
public String Link;
}
My results so far have always been the same: crash as soon as I click "OK" on the edit text field after changing it, due to a null pointer. Could anyone help me out with this, please? I am new to android.
Stack trace:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myfragmentapp, PID: 5185
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.myfragmentapp.adapters.LinkItemAdapter.notifyDataSetChanged()' on a null object reference
at com.example.myfragmentapp.screens.HomeFragment.addLink(HomeFragment.java:86)
at com.example.myfragmentapp.screens.SettingsFragment.onSharedPreferenceChanged(SettingsFragment.java:42)
at android.app.SharedPreferencesImpl$EditorImpl.notifyListeners(SharedPreferencesImpl.java:560)
at android.app.SharedPreferencesImpl$EditorImpl.apply(SharedPreferencesImpl.java:443)
at androidx.preference.Preference.tryCommit(Preference.java:1632)
at androidx.preference.Preference.persistString(Preference.java:1663)
at androidx.preference.EditTextPreference.setText(EditTextPreference.java:80)
at androidx.preference.EditTextPreferenceDialogFragmentCompat.onDialogClosed(EditTextPreferenceDialogFragmentCompat.java:99)
at androidx.preference.PreferenceDialogFragmentCompat.onDismiss(PreferenceDialogFragmentCompat.java:267)
at android.app.Dialog$ListenersHandler.handleMessage(Dialog.java:1377)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6709)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)
You should call addLink() after you've created the adapter:
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
listView = view.findViewById(R.id.postListView);
itemAdapter = new LinkItemAdapter(getActivity(), R.layout.link_item, urls);
listView.setAdapter(itemAdapter);
listView.setOnItemClickListener(onItemClickListener);
addLnk();
return view;
}
If you're trying to set a value from one fragment to another you should either use callbacks or a ViewModel, the simpler of those being a callback:
Define a callback inteface:
interface OnSetPreferenceItem{
void setPrefItemInList(String item);
}
Inside SettingsFragment, define a variable:
private OnSetPreferenceItem callback;
In the same fragment, fill in the variable in onAttach:
public void onAttach(Context context) {
super.onAttach(context);
callback = (OnSetPreferenceItem )context;
}
Now instead of calling new HomeFragment().addLink(value);, call
callback.setPrefItemInList(value);
Let your parent activity implement that interface and implement the method suggested:
public void setPrefItemInList(String item){
homeFragment.addLink(item);
}
Modify your addLink method to protect it:
void addLink(String title) {
urls.add(new LinkItem(title, "https://google.com"));
if(itemAdapter!=null){
itemAdapter.notifyDataSetChanged();
}
}
I would suggest you using the lifecycle functions of the Fragment correctly. When you are modifying some data (i.e. adding a new URL in the list) from another fragment (i.e. SettingsFragment), you do not have to call the HomeFragment.addLink right away actually. Instead, you might consider having the onResume method implemented in your HomeFragment so that when you go back to your HomeFragment, the onResume function is called automatically and there you should update your list and consider calling notifyDataSetChanged on your adapter.
Hence I am trying to provide some pseudo code here. In your SettingsFragment do something like the following.
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preference preference = findPreference(key);
if (preference instanceof EditTextPreference) {
EditTextPreference editTextPreference = (EditTextPreference) preference;
String value = editTextPreference.getText();
// new HomeFragment().addLink(value); // You do not call this here
saveTheNewURLInPrefrence(); // Just save the new value in your preference
} else {
assert preference != null;
preference.setSummary(sharedPreferences.getString(key, ""));
}
}
Now in your HomeFragment, implement the onResume function like the following.
#Override
protected void onResume() {
super.onResume();
urls = getAllItemsFromPreference();
if(itemAdapter != null) itemAdapter.notifyDataSetChanged();
else {
itemAdapter = new LinkItemAdapter(getActivity(), R.layout.link_item, urls);
listView.setAdapter(itemAdapter);
listView.setOnItemClickListener(onItemClickListener);
}
}
To understand more about fragment lifecycle, please check the documentation here. I hope you get the idea.

Pass Data to Fragment from Activity, when intially loaded with Fragment

So my issue is basically my MainActivity is initially loaded with a Fragment, which we will call MyFragment.
I am loading JSON, from online and wanting to pass into my MyFragment.
The problem is arising when setContentView is called in the MainActivity, it is calling onCreateView in MyFragment, which contains getArguments.getSerializable("myTag"). The key isn't passed because I haven't loaded the JSON yet.
Can you help me resolve this issue?
Here is my code:
In my MyFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(getArguments() != null) {
coll = (HashSet<String>) getArguments().getSerializable("myTag");
}
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my, container, false);
}
MainActivity (assume I loaded my JSON already):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadJSON();
passTagsToFragment(); //passes to the fragment
}
public void passTagsToFragment(){
Bundle bundle = new Bundle();
bundle.putSerializable("myTags", tagsSet);
TagsFragment frag = new MyFragment();
frag.setArguments(bundle);
}
EDIT:
Basically, my issue is that I want to load the MainActivity fully, before even starting to load the Fragment. Not sure how to do that.
EDIT 2:
I fixed the problem here is my code: (Changed the variable names)
MainActivity.java
public TagsFragment passInfoToTagsFramgent(){
Bundle bundle = new Bundle();
bundle.putSerializable("tags", tagsList);
TagsFragment frag = new TagsFragment();
frag.setArguments(bundle);
return frag;
}
in OnPostExecute of MainActvity.java:
Fragment tagFragment = passInfoToTagsFramgent();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, tagFragment);
transaction.commit();
You should call your passTagsToFragment() method in post execute method of your asynctask when all json data gets loaded.
protected void onPostExecute(Long result) {
passTagsToFragment();
}
loadJSON is from online source, so I assume it is an AsyncTask.
I usually do this as a lamda expression (kotlin):
loadJSON({passTagsToFragment()})
loadJSON should take a lamda expression as parameter:
loadJSON(private val callback:()->Unit )
and in the AsyncTask::onPostExecute, you should call the callback:
callback.invoke()
or just
callback()
Then you made sure the fragment is opened when the JSON is loaded and passed to fragment.
OK let me try to make it in Java.
In your AsyncTask which loads JSON, you will need an interface e.g.,
public interface JSONLoadCallback {
void loaded();
}
And the its constructor takes the interface as parameter:
class JSONLoader : AsyncTask<....> {
JSONLoader(JSONLoadCallback callback) {
_callback = callback;
}
#Override
public void onPostExecute() {
_callback.loaded();
}
}
And your Activity implements JSONLoadCallback:
#Override
public void loaded() {
passTagsToFragment();
}
And should pass itself to the AsyncTask:
JSONLoader(this).executeOnExecutor();
This way, the loaded() function is fired when JSON load is finished.
You see, Java codes are very verbal, Kotlin almost removed the necessity of Java interface.
As per my understanding, you can call Loadjson() method on fragment as well and use data accordingly but if you have some specific logic you can use asynctask and on json retrieval with progress bar you can set any MyFragment callback and update your fragment accordingly.

SavedInstanceStateNull between Fragments is null

I have a fragment which shows a list of buttons, each have their own id. When the user clicks the button, I am trying to replace the current fragment with another. Right after I replace the fragment, I a am adding arguments to the bundle. From what I can tell from debug mode, that part is working 100%. However, when we get to the new fragment, the "savedInstanceState" state will always be null.
I have read several posts on this however, I am confused because I am attempting to set the bundle and replace the fragment the same way I did when going from the activity to the first fragment and it is still not working.
MyActivity.java (this is working and all values are being set):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyFirstFragment fragment = new MyFirstFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame,fragment);
fragmentTransaction.commit();
Intent i = getIntent();
value = i.getExtras().getString("key");
Bundle bundle = new Bundle();
bundle.putString("key",value);
fragment.setArguments(bundle);
}
MyFirstFragment.java (also working):
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first, container, false);
final String value = getArguments().getString("key");
myButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
String gid = Integer.toString(v.getId());
MySecondFragment secondfragment = new MySecondFragment();
Bundle secondFragmentBundle = new Bundle();
secondFragmentBundle.putString("key",value);
secondFragmentBundle.putString("id",id);
secondfragment.setArguments(secondFragmentBundle);
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame,secondfragment);
fragmentTransaction.commit();
}
});
MySecondFragment (getting null for savedInstanceState and all bundle agruments!):
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_second, container, false);
value = getArguments().getString("key");
id = getArguments().getString("id");
}
You cannot set arguments on a Fragment after it is attached to an Activity.
Your issue is caused by confusion about Fragment creation. What you provided in your question is not the correct way to send arguments to another Fragment type.
The de-facto standard way of passing arguments to a Fragment when it is created it to use a static factory method, generally called newInstance(). You should set the arguments this way, and should do so before committing the FragmentTransaction - however, you technically could do it right after committing the transaction with no ill effects. This is because when a Fragment is committed, it isn't immediately attached/created, rather, it is queued on the main thread.
Here is how you would do it:
Write a method in your Fragment class called new instance. Be sure to leave a public default (no arguments) constructor for the system to handle things in the background.
public MyFirstFragment extends Fragment {
public MyFirstFragment(){
//constructor - don't use this to create a new fragment directly
}
public static MyFirstFragment newInstance(String argument1, String argument2) {
// This is where you set the Fragment arguments:
MyFirstFragment instance = new MyFirstFragment();
Bundle arguments = new Bundle();
// add whatever arguments you need to the bundle
arguments.putString("ARGUMENT_1_KEY", argument1);
arguments.putString("ARGUMENT_2_KEY", argument2);
//Set the arguments on the new fragment instance
instance.setArguments(arguments);
return instance;
}
// recover the values in onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
// an easy way to check if this is a new instance is to null-check the saved state Bundle
if (savedInstanceState == null) {
String argument1 = getArguments().getString("ARGUMENT_1_KEY");
String argument2 = getArguments().getString("ARGUMENT_2_KEY");
} else {
// restore whatever you need from savedInstanceState bundle
}
}
Key point - the arguments are passed into the newInstance() method (these can be whatever you want, as long as the type is allowed). The newInstance() method enforces argument types and also prevents setting them after the fragment is already attached to an activity.
After this, in your Activity.onCreate() method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String argument1 = "some information";
String argument2 = "other information";
MyFirstFragment fragment = MyFirstFragment.newInstance(argument1, argument2);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame,fragment);
fragmentTransaction.commit();
}

Transferring multiple strings between Fragments of the same activity with tags

I am having trouble figuring out how to share data between my two fragments which are hosted on the same activity.
The objective:
I want to transfer string from the the selected position of a spinner and an image url string from a selected list view position from fragment A to fragment B.
The Attempt:
I read the fragments doc on this problem here http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
And went ahead an created the following Interface to use betweeen the Fragments and the Host Activity.
public interface OnSelectionListener {
public void OnSelectionListener(String img, String comments );
}
Then I proceeded to implement it in my fragment A's onCreateView method like so:
postList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
ListData link = data.get(position);
String permalink = link.getComments();
String largeImg = link.getImageUrl();
Fragment newFragment = new DetailsView();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
//pass data to host activity
selectionListener.OnSelectionListener(permalink,largeImg);
}
});
And also in the onAttach method
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
selectionListener = (OnSelectionListener)getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement onSelectionListener");
}
}
In the Host activity I implemented the interface I wrote and overrided the method like so:
#Override
public void OnSelectionListener(String img, String comments) {
DetailsView detailsView = new DetailsView();
DetailsView dView = (DetailsView)getSupportFragmentManager().findFragmentByTag(detailsView.getCustomTag());
dView.setInformation(img, comments);
}
In Fragment B I set a "tag" the following way
private String tag;
public void setCustomTag(String tag)
{
this.tag = tag;
}
public String getCustomTag()
{
return tag;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setCustomTag("DETAILS_VIEW");
And my thinking is that that the information can be passed to Fragment B by calling this method from the host activity
void setInformation (String info, String img){
RedditDetailsTask detailsTask = new RedditDetailsTask(null,DetailsView.this);
detailsTask.execute(info);
setDrawable(img);
}
What I need:
I want to know how to properly use tags to get this to work, I dont have any fragment id's declared in my xml and rather opted to exchange fragments in a fragment_container.
I also am not sure if this is a good way to pass multiple strings between fragments. I am a newbie programmer so I know my logic probably looks pretty embarrassing but I am trying to do my best learn to do this right. I would appreciate it if you more senior developers can point me in the right direction for doing this.
You don't need to use tags. Take a look at this example. The Activity implements an interface that allows you to talk from Fragment1 back to the Activity, the Activity then relays the information into Fragment2.
I've left out all the android stuff about FragmentManager etc.
interface FragmentListener {
void onTalk(String s1);
}
public class MyActivity extends Activity implements FragmentListener {
Fragment2 fragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
// Find fragment2 and init
}
#Override
public void onTalk(String s1) {
fragment2.onListen(s1);
}
private static class Fragment1 extends Fragment {
private FragmentListener communication;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
communication = (FragmentListener) activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_one, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// or in an onClick listener
communication.onTalk("blah blah");
}
}
private static class Fragment2 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_two, container, false);
}
public void onListen(String s1) {
Log.d("TADA", s1);
}
}
}
My approach would be, when you get the callback in activity through the OnSelectionListener interface, I would create the Fragment B object and set arguments to it as follows:
#Override
public void OnSelectionListener(String img, String comments) {
DetailsView detailsView = new DetailsView();
Bundle args=new Bundle();
args.putString("img",img);
args.putString("comments",comments);
detailsView.setArguments(args);
//code here to replace the fragment A with fragment B
}
Then in Fragment B's onCreate method you can retrieve the values as follows:
#Override
public void onCreate(Bundle savedInstanceState) {
Bundle args=getArguments();
String img=args.getString("img");
String comments=args.getString("comments");
//do whatever you want to do with the varaibles
}
You could try to make two public static String's in your B fragment.
it Would look like something like this
public static String img;
public static String comment;
The you set the variables before making the transaction to fragment B
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
SecondFragment.img = new String("imgString"); //Making a new string so incase you change the string in bfragment, the values wont change in here
SecondFragment.comment = new String("comment");
// Commit the transaction
transaction.commit();
Then in the onStop(), or onDestroy() - depending on when you want the variables to be null, check this - you set the the static variables to null, so they dont take memory space
public void onDestroy(){
super.onDestroy();
img = null;
comment = null;
}

Categories