Communicating Between a Fragment and an Activity - Best Practices

Communicating between a fragment and an activity - best practices

The easiest way to communicate between your activity and fragments is using interfaces. The idea is basically to define an interface inside a given fragment A and let the activity implement that interface.

Once it has implemented that interface, you could do anything you want in the method it overrides.

The other important part of the interface is that you have to call the abstract method from your fragment and remember to cast it to your activity. It should catch a ClassCastException if not done correctly.

There is a good tutorial on Simple Developer Blog on how to do exactly this kind of thing.

I hope this was helpful to you!

What is the best way of communicating between a Fragment and an Activity in Android?

the problem is that during your application life cycle the system may need to destroy and recreate your fragment again but it will call the constructor with no parameters so your onBtnAddCategoryClick will not set. so you have to set that from your activity. This is also why you should pass the data between activities and fragments using bundle

Android: Communication between an Activity and a Fragment

So, you already created an interface in the fragment and implemented it in the activity? Then you just have to create the interface following your needs. The onFragmentInteraction(Uri uri) method is just an example, which you can remove, if you don't want to pass an Uri from the fragment to activity.

So, to pass a number, your interface could look like this:

interface Listener {
void onNumberChanged(int number);
}

The relevant fragment code:

listener.onNumberChanged(someNumber);

The relevant activity code:

@Override
public void onNumberChanged(int number) {
someTextView.setText(Integer.toString(number));
}

Good practice for communicate between viewModel and fragment

Usually, I have two observable live data in my view model. First is represent the state of the whole screen. Second I use for "single-shot" events like toasts, navigation, showing dialogs.

My view model:

class PinCreateViewModel(...) : ViewModel() {

val event = MutableLiveData<BaseEvent<String>>()
val state = MutableLiveData<PinCreateViewState>()
}

I have a single state object for the whole screen:

sealed class PinCreateViewState {

object FirstInput : PinCreateViewState()

data class SecondInput(val firstEnteredPin: String) : PinCreateViewState()

object Error : PinCreateViewState()

object Loading : PinCreateViewState()
}

I think with this approach it's easy to think about my screen states, easy to design my screen as a finite state machine, and easy to debug. Especially, I like this approach to very complex screens. In this case, I have a single source of truth for my whole screen state.

But sometimes I want to show dialogs, toast or open new screens. These things are not part of my screen state. And this is why I want to handle them separately. And in this case, I use Events:

sealed class BaseEvent(private val content: String) {

var hasBeenHandled = false
private set

fun getContentIfNotHandled(): String? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}

fun peekContent(): String = content
}

class ErrorEvent(content: String) : BaseEvent(content)

class MessageEvent(content: String) : BaseEvent(content)

And my Fragment interaction with ViewModel looks like this:

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
observe(viewModel.event, this::onEvent)
observe(viewModel.state, this::render)
}

private fun render(state: PinCreateViewState) {
when (state) {
PinCreateViewState.FirstInput -> setFirstInputState()
is PinCreateViewState.SecondInput -> setSecondInputState()
PinCreateViewState.Error -> setErrorState()
PinCreateViewState.Loading -> setLoadingState()
}
}

fun onEvent(event: BaseEvent<String>) {
event.getContentIfNotHandled()?.let { text ->
when (event) {
is MessageEvent -> showMessage(text)
is ErrorEvent -> showError(text)
}
}
}

I really like Kotlin Sealed classes because it forces me to handle all possible cases. And I can find unhandled states even before compilation.

Basic communication between two fragments

Have a look at the Android developers page:
http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface

Basically, you 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 with the received value

Your Activity implements your interface (See FragmentA below)

public class YourActivity implements FragmentA.TextClicked{
@Override
public void sendText(String text){
// Get Fragment B
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, thanks @Deepscorn
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
}
}

Android best practices for Fragment to Activity communications

The second solution is the preferred one, because it allows your fragment to be more independent of its hosting activity.

If in the future you decide to put your fragment on a different activity, there are no changes needed on the fragment, and you will only need to implement the interface on your activity.

I'll add a third solution which is using an event bus (Otto for instance), which also works, although some might argue that it makes your code a little less readable.



Related Topics



Leave a reply



Submit