any android developer here? i need to ask something regarding communication between different fragment like i select something from list of fragment one and on that even fragment two is replaced with another layout?
i have listview adapter in fragment 1 and its view in fragment two.
in fragment one i have already implement onClickListener and switch by getting the position from clicklistener method. i used fragment manager but not getting the way i want.
please reply with code and explanation!
any help will be appreciated!
If I understand your question, what you're trying to implement is a "master-detail" flow. Let us call your first Fragment the list Fragment and your second Fragment the detail Fragment and let us assume that they both exist within a parent Activity.
To establish communication between your list Fragment and your detail Fragment, you could define a callback interface that the parent activity of both Fragments implement. So, in your list Fragment, you would have:
public class MyListFragment extends ListFragment {
// Create a reference to your callback
private ListItemSelectedListener mCallback;
// Define your callback interface
public interface ListItemSelectedListener {
onListItemSelected(int position, Object data);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the parent activity
mCallback.onListItemSelected(position, getData(position));
}
}
Then, your parent activity implements the callback listener and triggers a change in the detail Fragment when necessary.
public class MasterDetailActivity extends Activity
implements MyListFragment.ListItemSelectedListener {
//....
// Implement the callback method defined in the interface
public void onListItemSelected(int position, Object data) {
// Get a reference to your detail Fragment
DetailFragment detailFragment = (DetailFragment)
getFragmentManager().findFragmentById(R.id.detail_fragment);
/* This is assuming both the list fragment and the detail fragment
* exist within the parent Activity
*/
if (detailFragment != null) {
detailFragment.changeDetailView(position, data);
}
}
}
Check out Google's document on Fragment communication to learn more.
Related
Is there any standard pattern for communication between two fragments where one fragment hosts another fragment? This article Communicating with Other Fragments explains how to communicate between two (independent?) fragments where Activity is communication mediator. However, this "pattern" does not suit my case.
I have created fragment which contains ViewPager. ViewPager's items, fragments, produces some events which need to be processed in hosting fragment. Since hosting fragment should be a standalone component, I would like to ommit Activity from the communication process.
What is the best place for registering of listeners in such case?
Our solution was to generalize the pattern you linked to from "communicate with my activity" to "communicate with my parent", which could be an Activity or another Fragment.
Instead of always expecting the Activity to implement an interface and casting it in onAttach, we have a utility method that looks for a valid callback for the current fragment and uses that at the point where we want to invoke the callback.
/**
* #param fragment The fragment to get the listener for.
* #param listenerClass The class of the listener to get.
* #param <T> Type of the listener to get.
* #return A listener object for the given fragment, cast from the owning parent fragment or
* Activity, or null if neither is a listener.
*/
#Nullable
public static <T> T getListener(#NonNull Fragment fragment, #NonNull Class<T> listenerClass) {
T listener = null;
if (listenerClass.isInstance(fragment.getParentFragment())) {
listener = listenerClass.cast(fragment.getParentFragment());
}
else if (listenerClass.isInstance(fragment.getActivity())) {
listener = listenerClass.cast(fragment.getActivity());
}
return listener;
}
This says: given some fragment and an interface callback type, does my owning fragment implement that interface? If so use that. If not, does my Activity implement the interface? If so, use that. Essentially, we walk up the hierarchy looking for a handler.
So in your child fragment:
private void invokeListener() {
Listener listener = getListener(this, Listener.class);
if (listener != null) {
listener.onThingHappened();
}
}
Then in your parent fragment:
public class ParentFragment extends Fragment implements Listener {
#Override
public void onThingHappened() {
// Handle view pager fragment event
}
}
If you later create an instance of the child and add it to an Activity, just have that Activity implements the callback, and it just works™.
Hope that helps!
I have two fragments, A and B let's say, where B contains a list. I would like to add a listener on Fragment B that notifies Fragment A of the chosen list item. I couldn't figure out how to initialize the listener in Fragment B since it is bad practice to pass arguments in fragment's constructors.
NOTE: Fragment B is contained inside Fragment A. i.e. I have a FrameLayout in Fragment A; and Fragment B covers that FrameLayout.
Any idea how I could do that?
If you're saying that Fragment B is a child fragment of Fragment A (that is, you've added it to Fragment A using Fragment A's getChildFragmentManager()), then you can use the same approach that you use for Activity interfaces, but using getParentFragment() instead of getActivity().
For example:
Fragment B:
#Override
public void onAttach(Context context) {
MyInterface myInterface = (MyInterface) getParentFragment();
}
Assuming that Fragment A implements MyInterface.
One convenience method we've used to avoid having to know whether a Fragment is hosted by another Fragment or an Activity is something like:
public static <T> getInterface(Class<T> interfaceClass, Fragment thisFragment) {
final Fragment parent = thisFragment.getParentFragment();
if (parent != null && interfaceClass.isAssignableFrom(parent)) {
return interfaceClass.cast(parent);
}
final Activity activity = thisFragment.getActivity();
if (activity != null && interfaceClass.isAssignableFrom(activity)) {
return interfaceClass.cast(activity);
}
return null;
}
Then you can just use:
MyInterface myInterface = getInterface(MyInterface.class, this);
and it doesn't matter whether Fragment B is hosted as a child Fragment or in an Activity directly.
A better approach for this situation, since what you want to do is communication between fragments, is to use an interface. You want to notify A when B has changed. This should be done through the parent activity. Here is the android documentation on the topic: https://developer.android.com/training/basics/fragments/communicating.html.
The gist of it is that you want to define an interface with a method called OnItemSelected (you can name it whatever you want). In B, you want a reference to this interface. When an item is selected, call your new OnItemSelected method. Implement this interface in the parent activity of the two fragments. In the implementation, you can put whatever code you want to modify A.
An example
CommunicationInterface
public interface CommunicationInterface {
public void onItemSelected(int position);
}
FragmentB
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
CommunicationInterface myInterface = (CommunicationInterface) getActivity();
// What ever else you want here
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
myInterface.onItemSelected(position);
}
MainActivity
public class MainActivity extends FragmentActivity implements CommunicationInterface {
// What ever other code you have
#Override
public void onItemSelected(int position) {
FragmentA fragA = (FragmentA)
getSupportFragmentManager().findFragmentById(R.id.fragment_a);
// Code to interact with Fragment A
}
Checkout the contract pattern https://gist.github.com/JakeWharton/2621173
If you are using multiple fragment, you dont have do it for every fragment, just add it to your BaseActivity if you have one.
This example shows the communication between activity and fragment. But for nested fragment you can replace the acitivy with getParentFragment();
How can I communicate a listview of a ListFragment after an event of a Fragment inside another Fragment?
In the ListFragment (fragment A) I have a method to refresh the ListView. But also, I need to refresh it after a click of a Button inside a Fragment, wich is child of another Fragment (fragment b)
Its like Fragment A (listFragment) | Fragment B (detailview)
(fragment C - child fragment of B)
How can I do it?
You can access another Fragment by its tag:
// find your fragment
YourFragment f = (YourFragment) getSupportFragmentManager().findFragmentByTag("yourFragTag");
// update the list view
f.updateListView();
The tag of your Fragment is set when it is attached to a container layout:
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().replace(R.id.frameBuy, YourFragment.newInstance(), "yourFragTag").commit();
So when you click your Button, find the Fragment you want to refresh by its tag, and then call your refresh method.
IF you are using a ViewPager, this is how to get the Fragments tag:
/**
* Gets the fragment tag of a fragment at a specific position in the viewpager.
*
* #param pos the pos
* #return the fragment tag
*/
public String getFragmentTag(int pos){
return "android:switcher:"+R.id.yourViewPagerId+":"+pos;
}
You can do it with a few simple steps:
Create a listener interface for component to listen to the button click event from FragmentC. For example:
public interface FragmentCButtonListener {
public void onButtonClicked();
}
Add a method in FragmentC to allow listener registration. FragmentC will keep track of the listener, and call the listener callback as soon as the button is clicked. For example:
public class FragmentC extends Fragment {
FragmentCButtonListener myListener;
public void registerListener (FragmentCButtonListener listener) {
myListener = listener;
}
}
FragmentA should implement FragmentCButtonListener interface, register itself as a listener to FragmentC, and refresh the list view when it receives the callback from FragmentC. For example:
public class FragmentC extends Fragment implements FragementCButtonListener {
FragmentC fragmentC;
public void onCreate() {
fragment = new FragmentC();
fragment.registerListener (this);
}
public void onButtonClicked() {
//refresh the list view
}
}
Please note, I assume the FragmentA has a reference to FragmentC in the sample. If not, just make sure the container class of all fragments registers itself as the listener of FragmentC. The container class can as FragmentA to update listview once it receives callback from FragmentC.
follow these steps
We have two fragments called AddFragmentand ListFragment, and upon adding an item on first fragment you want the updated list be shown on list fragment (what sort of sorcery is this!!!).
Step 1 create the listener interface on class level of AddFragment with a method that is going to be implemented by the other guy (ListFragment ) and create Interface type variable
public class AddFragment extends Fragment{
//listener varriable
//listener
public interface OnCategoryAddedListener{
public void refreshList();
}
private static OnCategoryAddedListener meAddListener;
}
Step 2 create register method on class level of the same AddFragment class and set listenter variable
public class AddFragment extends Fragment{
public void registerListener(OnCategoryAddedListener listener)
{
meAddListener = listener;
}
}
Step 3 upon any event cud be button click or yelling at ur application(that is considered rude event in android :-) ) check for listener object meAddListener variable and call the interface,
in a Shakespeare’s nutshell it means “for thou who implement ye interface and brought the method within ur class shelter, I shall give u my utmost privilege and blessing to …. ”
Step 4 On ListFragment implement the AddFragment’s interface,no turning back just go implement its method. Within that method u just call abracadabra to repopulate ur list or any sort of updatable android view object… and ur done
public class ListFragment extends Fragment implements AddFragment.OnCattegoryAddedListener{
//refer to AddFragment
AddFragment addFragment;
//once the fragment is within the memory scope u instantiate AddFragment
//and register listener with AddFragment context which inherently implement OnCategoryAddedListener
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
categoryAddFragment = new CategoryAddFragment();
categoryAddFragment.registerListener(this);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
fillList();
}
public void fillList() {
ArrayAdapter<String> catArrayAdapter = new ArrayAdapter<>(context,android.R.layout.simple_list_item_1,getItems());
setListAdapter(catArrayAdapter);
}
//le interface method u can count on to refresh ur list content
public void refreshList(){
fillList();
}
check this out for a little more
enjoy the java magic
the following: I'm using Swipe View with Tabs. Works quite smooth so far.
The thing is: I have two fragments/tabs. Each contains a ListView. I can remove an item from the left list. When I swipe to the right I want to update the list adapter so the left-deleted item is shown.
I tried onSwipeListerner, TabListener, onPageChangeListener (and on Resume() in the fragment itself). Nothing worked... Either the function is not called or I don't get the fragment object.
Does anybody know how I can call a function in my Fragment class when I swipe to this tab/fragment?
Thanks!
http://developer.android.com/training/basics/fragments/communicating.html#Deliver
I believe this is what you are looking to accomplish. But I don't think your plan of action is the best.
I would create an interface in my fragment where items will be deleted from such as
public class FragmentDeleteItemSharer extends Fragment{
// Interface in Fragment
public interface ShareDeletedItem{
// Interface method you will call from this fragment
public void shareItem(Object deletedItem);
}// end interface
// Instantiate the new Interface Callback
ShareDeletedItem mCallback = null;
// Override the onAttach Method
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
// Attaches the Interface to the Activity
// must add "implements ShareDeletedItem" in your
// Activity or this Exception is thrown
mCallback = (ShareDeletedItem) activity;
}catch(Exception ex){
ex.printStackTrace();
}
}// end onAttach()
// the method which you use to
// remove an item from the current fragment's listview
// where position is from yourlistViewAdapter.getSelectedItemPosition();
public void removeListItem(int position){
// using the item position, get the item in your object array
Object objectToDelete = myObjects[position];
// pass this information to removeItemFromArray
// a method that creates a new object array from the data
Object [] newObjectList = removeItemFromArray(myObjects, objectToDelete);
// Then use the interface callback to tell activity item was deleted
mCallback.shareItem(objectToDelete);
// Call to the method where you update the UI with the Objects
// Are you using an arrayList? Not sure but probably have
// an ArrayList<Objects> myObjects, as reference above
updateUiWithData(newObjectList);
}
}// end this fragment
Then in your activity create an interface
public class MyActivity extends FragmentActivity implements ShareDeletedItem{
// Interface Object , must attach this interface to Fragment2, when its created
UpdateFragment2 mCallback = null;
// You must attach this interface to your Fragment2, when its created
// you could do so with your view pager, create a method that adds each
// fragment to the view pager, then call a new method like
// addinterface(fragmentReference)
public interface UpdateFragment2{
// method to call in your Fragment that shows queue of deletes
public void addItemtoList(Object newObject);
}
// Interface Callback from the Fragment that deletes an item
public void shareItem(Object deletedItem){
// call the interface method to share the item with the Fragment2
mCallback.addItemToList(deletedItem);
}
}
Finally, Implement this interface in your Fragment2
public class Fragment2 extends Fragment implements UpdateFragment2{
// Interface Method in charge of giving this fragment the deleted item
public void addItemToList(Object deletedItem){
// TODO: Add the Item to the list you currently have
// If the mObjects array is an array list
mObjects.add(mObjects[mObjects.length + 1], deletedItem);
}
}
Depending on how you create your fragments with your view pager call
try{
// or however you hold reference to the fragment
mCallBack = (UpdateFragment2) Fragment2.class;
}catch(Exception ex){ex.printStackTrace();}
This is the Full of It. Hope this helps you understand that the interface way is the way to go. This is kind of difficult to help with no code from you but this is how it is done. The hardest part is adding the interface to the fragment when you create the fragments with you view pager. Good Luck
I'm using a master/detail pattern with one activity managing the 2-pane view and the selector list and the other activity managing the detail fragments. I'm using an interface to handle fragment callbacks to the Activities.
There seems to be a lot of code duplication though with the details activity copying many of the callback methods from the 2-pane activity. Where appropriate, I've used static methods where context isn't required, but where context is required I'm not sure how to remove the code duplication neatly.
Inheritance with an Abstract parent Activity is an option but seems like a lot of overhead.
Is there a better way of doing this?
I asked a similar question here: How many Activities vs Fragments?
I too was worried about the duplication of logic, and the answers I got caused quite a healthy debate.
In the end I chose to follow Stephen's answer, of putting as much of the logic into the Fragments themselves.
However other contributors seemed very keen on duplicating the logic as per the examples.
So lets say u have Activity AB that controls Frag A and Fragment B.
MY ANSWER:
If the variable is used by Frag A and Frag B, put it in Activity AB. Then pass it to Frag A or Frag B everything they need it. Or have Frag A or Frag B retrieve it from Activity AB.
If the variable is used by Frag A only or Frag B only, put it in Frag A or Frag B respectively.
For methods that are used by both Frag A and Frag B, put those methods in another class and create instances of that class inside Frag A and Frag B for each of the 2 fragments to use.
The following is an answer I gave to another person. However, it seems relevant to your question so I am re-posting it here.
Inside Fragment A u need an interface that Activity AB can implement.
In the sample android code, they have:
private Callbacks mCallbacks = sDummyCallbacks;
/*A callback interface that all activities containing this fragment must implement. This mechanism allows activities to be notified of item selections.
*/
public interface Callbacks {
/*Callback for when an item has been selected. */
public void onItemSelected(String id);
}
/*A dummy implementation of the {#link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity. */
private static Callbacks sDummyCallbacks = new Callbacks() {
#Override
public void onItemSelected(String id) {
}
};
The Callback interface is put inside one of your Fragments (let’s say Fragment A). I think the purpose of this Callbacks interface is like a nested class inside Frag A which any Activity can implement. So if Fragment A was a TV, the CallBacks is the TV Remote (interface) that allows Fragment A to be used by Activity AB. I may be wrong about the details because I'm a noob but I did get my program to work perfectly on all screen sizes and this is what I used.
So inside Fragment A, we have:
(I took this from Android’s Sample programs)
#Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
//mCallbacks.onItemSelected( PUT YOUR SHIT HERE. int, String, etc.);
//mCallbacks.onItemSelected (Object);
}
And inside Activity AB we override the onItemSelected method:
public class AB extends FragmentActivity implements ItemListFragment.Callbacks {
//...
#Override
//public void onItemSelected (CATCH YOUR SHIT HERE) {
//public void onItemSelected (Object obj) {
public void onItemSelected(String id) {
//Pass Data to Fragment B. For example:
Bundle arguments = new Bundle();
arguments.putString(“FragmentB_package”, id);
FragmentB fragment = new FragmentB();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction().replace(R.id.item_detail_container, fragment).commit();
}
So inside Activity AB, you basically throwing everything into a Bundle and passing it to B. If u are not sure how to use a Bundle, look the class up.
I am basically going by the sample code that Android provided. The one with the DummyContent stuff. When u make a new Android Application Package, it's the one titled MasterDetailFlow.