Hi in my Activity I have a fragmentA with a textview, when I click on the textview , this fragmentA is replaced with fragment which has a listview. Now when I click on the litsItem I have to goback to fragmentA and update the textview with the list item.
Implementation:
I created an interface in fragment,
public interface OnListItemSelectedListener {
public void onListItemSelected(String msg);
}
and in onAttach() I have the below code
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
mListener = (OnListItemSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnListItemSelectedListener");
}
}
In my listitem OnClickListener() I have this
mListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String message = parent.getItemAtPosition(position).toString();
mListener.onListItemSelected(message);
}
});
then in my activity i implemented the interface
#Override
public void onListItemSelected(String msg) {
// TODO Auto-generated method stub
ISOFragment myFrag = (ISOFragment)
getSupportFragmentManager().findFragmentById(R.id.isomain);
if (myFrag != null) {
myFrag.incrementdata(msg);
} else {
ISOFragment newFrag = new ISOFragment();
Bundle args = new Bundle();
args.putString("selecteitem", msg);
newFrag.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.content_frame, newFrag);
transaction.addToBackStack(null);
transaction.commit();
}
}
Now since the fragmentA is not available it goes to else part now how should i get the bundle arguments to fragmentA and update the textview.
Suppose I am going from A to B and coming back from B to A, if i get arguments and update the textview in Fragment A in onCreateView(), if when i run for the first time it is checking for the arguments which will be null.
I had the same problem and the best solution for me was to extend activity and store shared data there. My new activity had a get data function that returned an object of class MyData implements Parcelable (Serializeable would work for simple data). The data was stored in onSaveInstanceState in the activity and restored in the activivitys onCreate.
Related
I have 3 fragments. Fragment A, B and C. A have a "continue" button which will take it to B. B have a proceed button which will take it to C. C have a "add" button which will take it back to B. Now I want to send data from A to B when the continue button is pressed. and also from C to B when the add button is pressed. I tried using bundle. It is giving me null pointer exception as the first time when going from A to B , the bundle from C is null. How to solve this? Any help is highly appreciated. Please go through the code snippet below
Note: ItemDetails is obtained from fragment A and EmployeeDetails is obtained from fragment C. Fragment Flow => 1. fragment A 2. A to B(itemsList passed to B) 3. B to C (No communication) 4. Back to B from C(Employee List passed to B).
String TEMP_STRING_EMPLOYEES, TEMP_STRING_ITEMS;
EmployeeList employeeList;
ItemsList itemsList;
#Override
public void onStart() {
super.onStart();
Bundle args = getArguments();
if (args != null) {
TEMP_STRING_ITEMS = args.getString("ItemsDetails");
try {
// Set article based on argument passed in
TEMP_STRING_EMPLOYEES = args.getString("EmployeeDetails");
} catch (NullPointerException ex) {
}
} else {
}
}
//Next lines of code from MAinActivity.java
#Override
public void onFragmentInteractionForEmployeeDetails(ArrayList arrayList) {
EmployeeList employeeList = new EmployeeList(arrayList);
String correspondingJson = NavigationUtils.getStringForObject(employeeList);
B newFragment = new B();
Bundle args = new Bundle();
args.putString("EmployeeDetails", correspondingJson);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
You can use a callback in the Fragment and the Activity stores your object.
Activity
public class MyActivity extends Activity implements MyActivityCallback{
private Object myObject; // Replace with your object type
#Override
public Object getMyObject(){
return myObject;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myObject = new Object();
// ...
}
// ....
}
MyActivityCallback
public interface MyActivityCallback{
Object getMyObject();
}
Fragment
private MyActivityCallback mCallback;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (MyActivityCallback) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + MyActivityCallback.class.getSimpleName());
}
}
#Override
public void onDetach() {
mCallback = null;
super.onDetach();
}
Then in your Fragment, to access your object you do mCallback.getMyObject();
I am using the example give in the below link
http://android-er.blogspot.in/2013/04/handle-onlistitemclick-of-listfragment.html
Here i have two classes one extending List Fragment and other extending Fragment.
Now i am passing object to detailfragment in this way :
*from ListFragment *
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Detailfragment detailFragment = (Detailfragment)getFragmentManager().findFragmentById(detailFragmentID);
Bundle bundle = new Bundle();
bundle.putSerializable(BUNDLE_KEY, obj);// passing this object
detailFragment.setArguments(bundle);
detailFragment.setUpLayout();// update the UI
}
Now in the Fragment class i receive it,basic goal is to update the UI of the fragment based on the item selected in the list fragment, thats the reason i am sending the object
Bundle b = getArguments();
b.getSerializable(BUNDLE_KEY);
Now on item selected it says "Fragment already active".
What is the issue here? what am i doing wrong?
Another solution is to create an empty constructor for your fragment.
public Detailfragment() {
super();
// Just to be an empty Bundle. You can use this later with getArguments().set...
setArguments(new Bundle());
}
and in the onListItemClick method you use that bundle:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Detailfragment detailFragment = (Detailfragment)getFragmentManager().findFragmentById(detailFragmentID);
// Update the keys.
detailFragment.getArguments().putSerializable(BUNDLE_KEY, obj);// passing this object
detailFragment.setUpLayout();// update the UI
}
Now you can call the getArguments() methond in your setUpLayout() method.
From the Official Android development Reference:
public void setArguments (Bundle args) Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.
Your fragment is already attached to its activity
i suggest you to use your own method, you don't need setArguments!
create your own setUIArguments(Bundle args) inside the fragment class and update the fragment UI inside this method
You will call this method in this way:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Detailfragment detailFragment = (Detailfragment)getFragmentManager().findFragmentById(detailFragmentID);
Bundle bundle = new Bundle();
bundle.putSerializable(BUNDLE_KEY, obj);// passing this object
detailFragment.setUIArguments(bundle); /* your new method */
}
in your fragment class
public void setUIArguments(Bundle args) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
/* do your UI stuffs */
}
}
}
You can check if there are already arguments, and if so just add/update them.
private static void initFrag(Fragment frag, Bundle args) {
if (frag.getArguments() == null) {
frag.setArguments(args);
} else {
//Consider explicitly clearing arguments here
frag.getArguments().putAll(args);
}
}
Optionally, you might want to clear away existing arguments if you can't safely assume that pre-existing arguments are still valid.
This one global variable:
private FragmentManager fragmentmanager;
private FragmentTransaction fragmenttransaction;
These code put in your "List Fragment" onCreate() Activity :
fragmenttransaction = fragmentmanager.beginTransaction();
fragmenttransaction.replace(detailFragmentID, detailFragment, "test");
fragmenttransaction.addToBackStack(null);
fragmenttransaction.commit();
These is Drawerlistitem click event:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Bundle bundle = new Bundle();
fragmenttransaction = fragmentmanager.beginTransaction();
if(fragmentmanager.findFragmentById("test") != null) {
fragmenttransaction.remove(fragmentmanager.findFragmentByTag("test"));
}
Detailfragment detailFragment = (Detailfragment)getFragmentManager().findFragmentById(detailFragmentID);
bundle.putSerializable(BUNDLE_KEY, obj);// passing this object
detailFragment.setArguments(bundle);
fragmenttransaction.replace(detailFragmentID, detailFragment, "test");
fragmenttransaction.addToBackStack(null);
fragmenttransaction.commit();
}
Now Extending Fragment code as it is:
Bundle b = getArguments();
b.getSerializable(BUNDLE_KEY);
Dialogue fragment's public method
public void setBundle(final Bundle bundle) {
final Bundle arguments = getArguments();
arguments.clear();
arguments.putAll(bundle);
}
Show or update dialogue fragment
public void showMessageDialogue(final String tag, final Bundle bundle) {
final Fragment fragment = mFragmentManager.findFragmentByTag(tag);
if (fragment != null && fragment instanceof MessageDialogueFragment) {
((MessageDialogueFragment) fragment).setBundle(bundle);
} else {
final MessageDialogueFragment messageDialogueFragment = new MessageDialogueFragment();
messageDialogueFragment.setArguments(bundle);
messageDialogueFragment.show(mFragmentManager, tag);
}
}
I am attempting to create an app which has a Master/Detail flow using Fragments. Selecting an item will open a detail fragment which may then which to "open" another fragment and add it to the back stack.
I have renamed classes to help illustrate what they do.
public class ListOfDetails extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
//Callback method indicating that an item with the given ID was selected.
public void onItemSelected(String id) {
// Performing logic to determine what fragment to start omitted
if (ifTwoPanes()) {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class);
newIntent.putExtra("id", id);
startActivity(newIntent);
}
}
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.replace(R.id.aContainer, fragment);
transaction.commit();
}
}
An example of one of the detail fragments. There are many different Fragments that may be created in different circumstances.
public class DetailFragmentType1 extends Fragment {
private ListOfDetails parent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activity a = getActivity();
if (a instanceof ListOfDetails) {
parent = (ListOfDetails) a;
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button aButton = (Button) getActivity().findViewById(R.id.aButton);
aButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
parent.changeDetailFragment(new SubDetailFragment());
}
});
}
}
When on phone, a wrapper activity is used to hold the fragment
public class SinglePaneFragmentWrapper extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Duplicate logic must be performed to start fragment
// Performing logic to determine what fragment to start omitted
String id = getIntent().getStringExtra("id");
if(id == "DetailFragmentType1") {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
...
}
}
}
What is the proper way to change the fragment that is open in the detail pane in this circumstance? My method feels like a hack when using two panes and doesn't even work when using only one pane because getParent() from SinglePaneFragmentWrapper returns null, making me unable to call parent.changeDetailFragment().
This is a complicated question, hopefully I explained it well. Let me know if I missed something. Thanks
There are lots of opinions around this and lots of ways of doing it. I think in this case the problem is "who is responsible for changing the fragment?" on the surface it seems that a listener on the button is the obvious place, but then the fragment shouldn't know what it is hosted in (a symptom of that is getting an undesirable result like null from getParent()).
In your case I would suggest you implement a "listener" interface in the parent and "notify" from the fragment.. when the parent is notified, it changes the fragment. This way the fragment is not changing itself (so doesn't need to know how).. so.. for your case..
Add a new interface:
public interface FragmentChangeListener {
void onFragmentChangeRequested(Fragment newFragment);
}
Implement the interface in your ListOfDetails activity
public class ListOfDetails extends FragmentActivity implements FragmentChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
//Callback method indicating that an item with the given ID was selected.
public void onItemSelected(String id) {
// Performing logic to determine what fragment to start omitted
if (ifTwoPanes()) {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class);
newIntent.putExtra("id", id);
startActivity(newIntent);
}
}
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.replace(R.id.aContainer, fragment);
transaction.commit();
}
// This is the interface implementation that will be called by your fragments
void onFragmentChangeRequested(Fragment newFragment) {
changeDetailFragment(newFragment);
}
}
Added listener to detail fragment
public class DetailFragmentType1 extends Fragment {
private FragmentChangeListener fragmentChangeListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Actually you might not have an activity here.. you should probably be
// doing this in onAttach
//Activity a = getActivity();
//if (a instanceof ListOfDetails) {
// parent = (ListOfDetails) a;
//}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button aButton = (Button) getActivity().findViewById(R.id.aButton);
aButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// parent.changeDetailFragment(new SubDetailFragment());
notifyFragmentChange(new SubDetailFragment());
}
});
}
#Override
public void onAttach(Activity activity) {
// This is called when the fragment is attached to an activity..
if (activity instanceof FragmentChangeListener) {
fragmentChangeListener = (FragmentChangeListener) activity;
} else {
// Find your bugs early by making them clear when you can...
if (BuildConfig.DEBUG) {
throw new IllegalArgumentException("Fragment hosts must implement FragmentChangeListener");
}
}
}
private void notifyFragmentChange(Fragment newFragment) {
FragmentChangeListener listener = fragmentChangeListener;
if (listener != null) {
listener.onFragmentChangeRequested(newFragment);
}
}
}
And implement the same interface to your single pane activity...
public class SinglePaneFragmentWrapper extends FragmentActivity implements FragmentChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Duplicate logic must be performed to start fragment
// Performing logic to determine what fragment to start omitted
String id = getIntent().getStringExtra("id");
if(id == "DetailFragmentType1") {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
...
}
}
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.replace(R.id.aContainer, fragment);
transaction.commit();
}
// This is the interface implementation that will be called by your fragments
void onFragmentChangeRequested(Fragment newFragment) {
changeDetailFragment(newFragment);
}
}
Note the similarity between your single pane and your multi-pane activities.. this suggests that you could either put all of the duplicated code (changefragment etc) into a single activity that they both extend or that in maybe they are the same activities with different layouts...
I hope that helps, Good luck.
Regards,
CJ
I am using sliding menu/drawer pattern in my app. So the main activity has a leftView which is a ListFragment named topicsFragment() which loads set of topic items. When an item/topic is clicked it replaces the fragment on main view by calling the FeedsFragment(tag). FeedsFragment uses arraylist adapter to load the feeds which has various clickable items in each list item. I want to fetch another instance on the feedsFragment(tag) when an item is clicked within a list item.
holder.contextView= (TextView) newsView.findViewById(R.id.arcHeader);
if (item.hasArc()) {
holder.contextView.setVisibility(View.VISIBLE);
String arc;
try {
arc=item.getarc();
holder.contextView.setText(arc);
holder.contextView.setOnClickListener(new View.OnClickListener() {
//currently it loads a class
#Override
public void onClick(View v) {
Intent i = new Intent(context, SomeClass.class);
i.putExtra("tag", arc);
context.startActivity(i);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
} else {
holder.contextView.setVisibility(View.GONE);
}
Currently it loads a new class. I want to define a fragment and then pass to main activity to replace with the current view but I cant use getSupportFragmentManager() inside an adapter class but only in a fragment or fragment activity. What should be an alternative to sweeping in a fragment from an adapter?
What I did was create this method in my main activity and just called it from other classes to change the fragment:
public void switchContent(Fragment fragment) {
mContent = fragment;
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment).commit();
slidemenu.showContent();
}
Solved it by using the context passed in the list adapter:
#Override
public void onClick(View v) {
Fragment newFragment = new ListFragmentClass(tag);
if (newFragment != null)
switchFragment(newFragment);
}
private void switchFragment(Fragment newFragment) {
if (context == null)
return;
if (context instanceof MainActivity) {
MainActivity feeds = (MainActivity) context;
feeds.switchContent(newFragment);
}
}
Here switchContent is method defined in your main activity for switching/replacing fragment as given in answer by Justin V.
Pass getFragmentManager() as a parameter in constructor of your adapter and use that.
Use an Interface to connect your side drawer ListFragment to the main activity. For example:
public class LeftDrawer extends ListFragment{
private DrawerCallback mCallback;
public interface DrawerCallback{
public void onListClick(String tag);
}
public void setCallback(DrawerCallback callback){
mCallback = callback;
}
}
As Fragments should have an empty constructor, use a public method within your Fragment to set the callback before completing the FragmentTransaction adding it to your drawer. At this point all that is left is notifying your Fragment that a click has occurred. What you should do is actually catch the click in your ListFragment directly rather than adding on onClickListener to every view in your adapter.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
/*
* Get item at the clicked position from your adapter and
* get its string tag before triggering interface
*/
mCallback.onListClick(tag);
}
Use the onListItemClick method to do this. You will get the list position that was clicked and can easily then get that item from your adapter and get its tag value to pass back to your host activity.
I have tried many things, such as the 2 references: http://developer.android.com/training/basics/fragments/communicating.html & http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity along with some help from others on StackOverflow and I just cannot get my ListFragment (FragmentA) to send just a simple string to another ListFragment (FragmentB). My app keeps crashing telling me there is a NullPointerException when I click the list in FragmentA going into FragmentB. I have been stuck on this for 2 days now and I just feel like smashing my head against a wall. If anyone knows what is wrong please post code to fix my problem.
MainActivity:
import com.example.fragmentcommunication.FragmentB.OnDataPass;
public class MainActivity extends Activity implements OnDataPass{
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
String librarySelected = libraryList[position];
Fragment newFragment = null;
if (librarySelected.equals("Item1")){
newFragment = new FragmentA();
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.tab2, newFragment, "FragA");
transaction.addToBackStack(null);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.commit();
}
});
}
public void onDataPass(String data) {
// TODO Auto-generated method stub
FragmentB transaction1 = ((FragmentB) getFragmentManager().findFragmentByTag("FragB"));
transaction1.use(data);
}
}
FragmentA:
public class FragmentA extends ListFragment{
...
OnDataPass dataPasser;
public interface OnDataPass{
public void onDataPass(String data);
}
#Override
public void onAttach(Activity a) {
super.onAttach(a);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
dataPasser = (OnDataPass) a;
} catch (ClassCastException e) {
throw new ClassCastException(a.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
String libraryList;
//Get the position the user clicked.
Fragment newFragment = null;
libraryList = l.getItemAtPosition(position).toString();
dataPasser.onDataPass(libraryList);
if(libraryList.equals("Ranged")){
newFragment = new FragmentB();
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.tab2, newFragment, "FragB");
transaction.addToBackStack(null);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.commit();
}
}
FragmentB:
public class FragmentB extends ListFragment{
...
String getListInfo;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
//Testing to see if the String was passed from Fragment A.
System.out.println(getListInfo);
...
public void use(String data) {
// TODO Auto-generated method stub
this.getListInfo = data;
}
}
I just cannot get my ListFragment (FragmentA) to send just a simple string to another ListFragment (FragmentB).
Since the primary point behind fragments is that they might not both exist on the screen at the same time (e.g., on -normal screens), FragmentA should not be trying to "send just a simple string to" FragmentB. FragmentB may not exist.
If an event occurs in FragmentA that may have UI effects beyond FragmentA, FragmentA should tell its hosting activity about the event. The hosting activity can then route the event to the other fragment, either by directly calling a method on it (if the activity hosts FragmentB as well), creating it and adding it to the UI (and passing in the data using a factory method, like newInstance()), or by calling startActivity() to launch another activity (if there is insufficient room for FragmentA and FragmentB, and therefore we have a separate activity showing FragmentB).
With respect to your existing code, getListInfo is null because you never assign any value to it.