I have two fragment class (#1 and #2) in my fragment activity.The fragment activity is used in view pager.
How can i call a method of fragment#2 in fragment#1?
Please, notice the attached picture.
http://upload7.ir/imgs/2014-09/92109715706282282966.jpg
The preferred way is to let all communication between Fragments go trough their common parent Activity:
public class MyActivity implements WantsACalledListener {
private Fragment fragment2;
#Override
public onWantsACalled() {
fragment2.a();
}
}
And, according to the Android documentation at: http://developer.android.com/guide/components/fragments.html
public static class Fragment1 {
WantsACalledListener mListener;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (WantsACalledListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement WantsACalledListener");
}
}
...
private void someMethod() {
mListener.onWantsACalled();
}
}
So, the Activity can easily keep a reference to all its child Fragments and the child Fragments get the reference to their parent Activity in their onAttach method.
Related
For example I have 2 fragments including 1 integer variable and 1 TextView for each. One of them has a button. I want this button to change all Integers and TextViews including the other fragment. How can I access to Variable and TextView of the other fragment ? Please explain with example code.
Fragment to Fragment Communication basically happens via an activity which generally hosts the Fragments, define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview(For example) with the received value:
// You Activity implements your interface which is defined in FragmentA
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get instance of Fragment B using FragmentManager
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}
// Fragment A defines an Interface, and calls the method when needed
public class FragA extends Fragment{
TextClicked mCallback;
public interface TextClicked{
public void sendText(String text);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
public void someMethod(){
mCallback.sendText("YOUR TEXT");
}
#Override
public void onDetach() {
mCallback = null; // => avoid leaking
super.onDetach();
}
}
// Fragment B has a public method to do something with the text
public class FragB extends Fragment{
public void updateText(String text){
// Here you have it
}
}
Let's consider a case where I have Fragment A and Fragment B.
B declares:
public interface MyInterface {
public void onTrigger(int position);
}
A implements this interface.
When pushing Fragment B into stack, how should I pass reference of Fragment A for it in Bundle so A can get the onTrigger callback when needed.
My use case scenario is that A has ListView with items and B has ViewPager with items. Both contain same items and when user goes from B -> A before popping B it should trigger the callback for A to update it's ListView position to match with B pager position.
Thanks.
Passing interface to Fragment
I think you are communicating between two Fragment
In order to do so, you can have a look into Communicating with Other Fragments
public class FragmentB extends Fragment{
MyInterface mCallback;
// Container Activity must implement this interface
public interface MyInterface {
public void onTrigger();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (MyInterface ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyInterface ");
}
}
...
}
For Kotlin 1.0.0-beta-3595
interface SomeCallback {}
class SomeFragment() : Fragment(){
var callback : SomeCallback? = null //some might want late init, but I think this way is safer
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
callback = activity as? SomeCallback //returns null if not type 'SomeCallback'
return inflater!!.inflate(R.layout.frag_some_view, container, false);
}
}
It is optimal for two fragments to only communicate through an activity. So you can define an interface in Fragment B that is implemented in the activity. Then in the activity, define in the interface method what you want to happen in fragment A.
In Fragment B,
MyInterface mCallback;
public interface MyInterface {
void onTrigger(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (MyInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyInterface");
}
}
Method for determining if user goes from B to A
public void onChangeFragment(int position){
//other logic here
mCallback.onTrigger(position);
}
In Activity,
public void onTrigger(int position) {
//Find listview in fragment A
listView.smoothScrollToPosition(position);
}
Goodluck!
Using #Amit's answer, and adapting to the OPs question, here is all the relevant code:
public class FragmentA extends BaseFragment implements MyInterface {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// THIS IS JUST AN EXAMPLE OF WHERE YOU MIGHT CREATE FragmentB
FragmentB myFragmentB = new FragmentB();
}
void onTrigger(int position){
// My Callback Happens Here!
}
}
...
public class FragmentB extends BaseFragment {
private MyInterface callback;
public interface MyInterface {
void onTrigger(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
callback = (MyInterface ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement MyInterface");
}
}
}
I think you should use communication, as I've written below. This code comes from this Android Dev page of communication between Fragments:
HeadlinesFragment
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
public void setOnHeadlineSelectedListener(Activity activity) {
mCallback = activity;
}
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
// ...
}
MainActivity
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
// ...
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof HeadlinesFragment) {
HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
headlinesFragment.setOnHeadlineSelectedListener(this);
}
}
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener {
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
You may create call back interface by this way.
var screenVisibility=activity as YourActivity
screenVisibility.setScreenVisibility("which screen you want")
Current my fragment A opens a new dialog fragment B. Is there any way to call method from a fragment A method from a dialog fragment B?
Regarding to android web site, you shouldn't call a fragment from another fragment, instead you should let activity to handle switching fragments.
To do that,
You can create an interface and implement it in activity,
public interface MyListener{
public void switchFragment(Fragment f);
}
public class MyActivity extends Activity implements MyListener{
#Override
public void switchFragement(Fragment f){
getSupportFragmentManager().beginTransaction
.replace(R.id.container, f)
.commit();
}
}
and in fragment classes,
public class MyFragment extends Fragment{
private MyListener listener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (MyListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyListener");
}
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
and simply use
listener.switchFragment(fragment);
You will need to one of 2 things:
Wire a reference to each Fragment in your Activity
Use an Enterprise Service Bus (Otto is an open-source option) to communicate directly between Fragments
There is NO way to talk direct Fragment to Fragment in the standard framework. You must manually create this linkage.
I am using a pattern where the activity is a listener to its fragments. The original way I have been creating attaching these fragments is like this:
MyFragment myFragment = new MyFragment(param1, param2)
myFragment.setListener(this);
getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, myFragment).commit()
But I have learned that this is wrong and that I should be creating the fragment with a static newInstance() method, in case the fragment is destroyed then it can recreate from a bundle. This will let me retain the parameters, but my question is how can I set the listener of a fragment to be its parent activity, after is has been destroyed then recreated?
Is it as simple as calling:
setListener((MyActivity) getActivity());
within the fragment? This seems wrong to me because it strongly couples the fragment to its parent activity.
This is definitely the wrong route to go. You should have your Activity implement the Fragment's interface (it looks as though you already are, but I'll show an example anyway). Then, within the Fragment, cast the Activity to a listener in onAttach() and release it in onDetach(). A quick example would be:
The Fragment
public class ExampleFragment extends Fragment {
private MyFragmentInterface mInterface;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof MyFragmentInterface) {
mInterface = (MyFragmentInterface) activity;
} else {
throw new IllegalArgumentException("Parent Activity must implement MyFragmentInterface.");
}
}
#Override
public void onDetach() {
super.onDetach();
mInterface = null;
}
private void fireOnSomethingHappened () {
if (mInterface != null) {
mInterface.onSomethingHappened();
}
}
public static interface MyFragmentInterface {
void onSomethingHappened();
}
}
The Activity
public class ExampleActivity extends Activity implements ExampleFragment.MyFragmentInterface {
#Override
public void onSomethingHappened() {
//Handle the event
}
}
newInstance() method should return a fragment instance so you can do something like this:
MyFragment myFragment = MyFragment.newInstance(param1, param2)
myFragment.setListener(this);
getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, myFragment).commit();
I have default Master-Detail flow, which was created automatically when creating new project. My question is. When I add a button to detail side. Is there a way to update my list side by pressing that button ? In other words, can ItemDetailFragment and ItemListFragment communicate ?
Yes just communicate through the activity with a listener.
Your activity:
public class MyActivity extends FragmentActivity implements OnFragmentClickListener {
#Override
public void OnFragmentClick(int action, Object object) {
switch(action) {
}
}
}
The listener class:
public interface OnFragmentClickListener {
public void OnFragmentClick(int action, Object object);
}
Your fragments will then have following somewhere in code in order to implement the interface:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentClickListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement listeners!");
}
}
Then your fragments communicate with each other like this: fragmentA -> activity -> fragmentB. Your activity can call methodes directly on the fragments without worrying about synchronization problems.
Example of a call from fragment a:
mListener.OnFragmentClick(GLOBAL_ACTION_KEY someObject);
Activity then handle:
public class MyActivity extends FragmentActivity implements OnFragmentClickListener {
#Override
public void OnFragmentClick(int action, Object object) {
switch(action) {
case GLOBAL_ACTION_KEY:
// you call fragmentB.someMethod();
break;
}
}
}