MVVM - How to cleanly incorporate AlertDialog? - android

I have a FragmentOne which contains the MVVM pattern. The Fragment has it's own ViewModel, FragmentOneViewModel. I am unsure on how to cleanly incorporate an AlertDialog in my case.
In the Fragment it contains a RecyclerViewAdapter, with each row that contains a Button to create an AlertDialog. Do I need a ViewModel for the AlertDialog?
Or can I use an interface callback and implement it on my Fragment, which will be called when the user clicks on buttons in the AlertDialog. And then use that input to call the Fragment's ViewModel?:
public class ActivateUserDialog extends AlertDialog {
//Should I use callback methods?
private Context mContext;
public interface DialogOnClickListener{
void onCancelBtnClick();
void onConfirmBtnClick(String amount);
}
public ActivateUserDialog(#NonNull Context context) {
super(context);
this.mContext = context;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_activate);
ButterKnife.bind(this);
}
#OnClick(R.id.btn_cancel)
public void onBtnDismiss() {
this.dismiss();
}
#OnClick(R.id.btn_confirm)
public void onBtnConfirm() {
//Call the fragment
MainActivity activity = (MainActivity) mContext;
MembersFragment fragment = (MembersFragment) .... get the fragment;
fragment.onConfirmBtnClick(amount);
}
}
And then calling the viewmodel from the callback method:
//Fragment class:
public class MembersFragment extends Fragment implements ActivateUserDialog.DialogOnClickListener
#Override
public void onConfirmBtnClick(String amount){
mViewModel.activateUser(amount);
}
Is this a clean way to do it?

I don't think there's a single answer to it and would say it's opinionated. Both would be valid in my opinion.
You also need to consider that architecture components don't work "out of the box" with AlertDialogs, so even if you want a VM for it you will have to retrieve it through the fragment/activity (basically a ViewModelProvider) or use some other solution like DI.
You would also have to manage the lifecycle of the AlertDialog to gain some of the benefits of the ViewModel.
Here are possible solutions:
Consider the AlertDialog as part of the View of FragmentOne and communicate with it via callbacks. In the implementation communicate with FragmentOneViewModel.
Use a DialogFragment with a ViewModel instead. Since DialogFragment is basically just a Fragment you get all the benefits of the architecture components ViewModel. You can either share FragmentOneViewModel between the two or create a new ViewModel dedicated to the dialog. Whether to share or create a new one is up to your needs but I would consider the complexity of the Dialog and whether it should be stand-alone. If you decide to share the ViewModel, be careful which ViewModelProvider you use to make sure they are actually shared and not a different instance.
As for communicating back from the AlertDialog, I would suggest using an interface with callbacks instead of passing Context and casting directly.
You can accept an implementation in the constructor.

You want to use a dialog box on some click in recycler-view adapter which is based on MVVM pattern and want some callback on adapter after dialog box action right?
Yes this is what I am doing in my code above, but I don't know if
this is the best practice way to do it with Dialog – DIRTY DAVE
As I think its a good way easy to understand and maintainable. However, dialog box is part of the UI and so interaction with it. So it should be in the Fragment. The fragment then interacting with the view-model is a reliable way to do things as that's the main home for your logic.
As the MVVM design pattern is similar to the well known MVC pattern in that the M (Model) and V (View) are relatively the same. The only difference resides between the C (Controller) and the VM (View Model).
Model
Represents the Data + State + Business logic. It is not tied to the view nor to the controller, which makes it reusable in many contexts.
View
Binds to observable variables and actions exposed by the View Model. It is possible for multiple views to bind to a single View Model.
View Model
Responsible for wrapping the model and preparing observable data needed by the view. It also provides hooks for the view to pass events to the model. An important thing to keep in mind is that the View Model is not tied to the view.
Source

Related

Where to place Toasts, Snackbars etc. in era of Android Architecture Components

What is the recommendation about placing Toasts, Snackbars in Activity vs. Fragment in the era of ViewModel and one activity multiple fragments concept.
Its clear that things like
new Intents
Fragment transactions/fragment setup
permission requests
presenting navigational components (drawer, viewpager)
should be handled by activity but what about Toasts/Snackbars?
There are so many SO questions about how to show Toasts or Snackbars in Fragment and how + when to use getContext() that I tend to let the activity handle all showing messages.
With ViewModel and SingleLiveEvent its quite easy to have a sharedViewModel that will be observed by activity and fragments and when needed a fragment dispatchs an event that notifies the activity to show a message.
So what do you think is best practice/recommended while having resposibility and separation of concerns in mind?
Taking a cue from Google's codelabs on basic architecture components, it seems better to let fragments/activities react to changes in ViewModel.
From the Google link above, it thus means that one of the most efficient methods is to Observe() a LiveData property of a ViewModel object from your Activity
it depends. Probably you can always replace toasts/snackbars with some UI parts (TextViews, Buttons) or Dialogs / DialogFragments.
it really depends on your app architecture, but I guess you can design some dependency: activity- or fragment-scoped, so you can inject it as abstraction for your ViewModel and use like you want.
IMHO if you deal with Toasts, you can design MessageShower at activty/application scope and reuse it everywhere(dealing with single instance of toast), but Snackbars needs View to be shown(so they need lifecycle, and it is more easier to make memory leak).
interface MessageShower{
void showMessage(String message);
}
class LongToastMessageShower implements MessageShower{
private Toast toast;
public LongToastMessageShower(Context appContext){
this.toast = Toast.makeText(appContext,"",Toast.LENGTH_LONG);
}
#Override
public void showMessage(String message){
toast.cancel();
toast.setText(message);
toast.show();
}
}
You can create BaseActivity and BaseFragment where you create all common methods like Toast, Snackbar etc which is extended in YourActivity and YourFragment.
use AndroidViewModel()
e.g
class LoginViewModel(application: Application) : AndroidViewModel(application) {
private val context = getApplication<Application>().applicationContext
//use that context when toasting in your viewModel
}
for snackbar
if its a button in your xml use this attribute and value
android:onClick="#{() -> login.signIn(signIn)}"
signIn inside the brackets in that context refers to the id of your button on which you are setting the onClick attribute
Remember that login before .signIn refers to your variable name
In your viewModel
fun signIn(view: View){
//use view to display your snack bar
}

Multiple fragment in one Activity in Android MVP

I was working with Android MVP architecture and I am following Google MVP Architecture.
I am facing issue when Activity have multiple fragment,In my case activity having 2 tab. I am not able to understand, Should i create two Presenter for every Fragment OR should i create one Presenter for this? now same thing with View.
Even i did'nt found any solution on google-sample github repository.
Can anyone please suggest me or show me live code which accomplish my above requirement.
If those two fragments are same then you can use same view and presenter. If fragments are bot same then different view and presenter will be better.
I always create different presenter/view for different fragments in an activity unless they share the same methods over some certain percentage. but to make thing more clean I believe creating things separately would be better.
1- You have to initialise your presenter inside onCreate() method of your Fragment. For this I always have a dependency registry class that is in charge of injection.
public void inject(ReportingHistoryDetailsFragment fragment, ReportingHistoryResponse.TaskBean taskBean) {
ReportingHistoryDetailsPresenter presenter = new ReportingHistoryDetailsPresenter(modelLayer,fragment,taskBean);
fragment.configureWith(presenter);
}
2- inside my fragment I call
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DependencyRegistry.shared.inject(this, taskBean);
}
3- my Base view interface has
public interface BaseView <T> {
void configureWith(T presenter);
}
if you have multiple fragments being managed by an activity, each fragment implements your View interface and have a corresponding presenter which implements your Presenter interface.
hope this helps.

Android MVP WeakRefrence

In mvp we save that reference of an activity in a weak reference.
WeakReference<Activity> view = new WeakReference<Activity>(activity);
If we lose the reference. can we get it back?
If you lose the reference to your Activity it means the activity was garbage collected and it doesn't exist anymore. You can't get back what doesn't exist.
Ex. If this happens because of configuration change it means a new activity was created.
You need a way to attach the newly created view to the same presenter.
If you are looking for libraries to help you, take a look at mosby and nucleus.
I don't think you should be saving a reference to an Activity in MVP at all - doesn't matter if it's hard or weak!
I'm assuming you're storing this reference in the Presenter. To really decouple the layers you should create an interface that describes your View (Activity) and use it instead of the activity.
So you'd do:
public interface LoginView {
displayUsernameError(String error);
displayPasswordError(String error);
openMainScreen();
}
Your Activity should implement the interface from above.
public class LoginActivity implements LoginView {
...
}
In your presenter you'd have:
class LoginPresenter {
private LoginView mView;
public LoginPresenter(LoginView view) {
mView = view;
}
public onLoginButtonClicked(String username, char[] password) {
...
mView.openMainScreen();
}
}
Immediate benefits of doing this:
The different layers are really decoupled. You can change your Activity (say you decide to use Fragments instead) without touching your Presenter.
Your presenter is fully testable using JUnit only! No need to use anything fancy to verify your interactions are correct, just plain Mockito to mock the LoginView.
One other point to note - are you sure you want your Presenter to outlive your View? There are some situations when that can't be avoided, but in most cases they have the same life span - when the View is destroyed the Presenter should be as well.
How did you set the reference in the first place?
You should be setting it with a setter method in Activity's onCreate.
This "setter" method is often called "attach" or "bind".
fun attach(view: View) {
this.view = view
}
So when new Activity is created due to configuration change, it will set itself to the presenter again. Note that you might be dealing with a new instance of the presenter too. However, based on your question I believe you want to attach the newly created activity to the same instance of presenter. If you got your presenter scoped right, this will work for both cases :)

Android Fragments fundamentals: why? Is this conceptually wrong?

I have a question about "proper programming" in Android.
I am currently developing an app using fragments. It involves dynamically added fragments to Activity, fragments inflated from XML, nested fragments from XML or dynamically added. Let's just say, a bit of everything.
The concept this question focuses on is the communication process involved with fragments. So, I've read the docs and this is not the first time I try to use fragments.
The common sense (and docs) tell that if a Fragment wants to speak or communicate with it's activity, we should use an interface.
Example:
TestFragment
public class TestFragment extends Fragment {
private TestFragmentInterface listener;
public interface TestFragmentInterface {
void actionMethod();
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (getActivity() instanceof TestFragmentInterface) {
listener = (TestFragmentInterface) getActivity();
}
// sending the event
if (listener != null) listener.actionMethod();
}
}
TestActivity
public class Test implements TestFragmentInterface {
#Override
public void actionMethod() {
..
}
}
Everything fine here.
This improves re-usability, as my TestFragment this way can interact with any kind of Activity, given the Activity implements the interface I declare.
The other way around, the Activity can interact with the fragment by holding a reference and calling its public methods. This is also the suggested way to fragment-to-fragment communication, using the Activity as a bridge.
This is cool, but sometimes it feels like using an interface for this is just a bit "too much".
Question A
In the scenario the fragments I attach have a pretty focused role, meaning they are done for that particular activity and would not be used otherwise, is it conceptually wrong to ignore the interface implementation and just do something like
((TestActivity) getActivity().myCustomMethod();
?
This also goes to the scenario where (not my case, but just taking it as a "at its worst") my activity has to deal with a wide variety of these DIFFERENT fragments, meaning it should implement one method for every fragment it should handle. This brings the code to a big mess of "potentially not-needed lines".
Moving further on: still with the use of "focused" fragments, aimed to work only under certain way, what is with the use of nested fragments?
Added them like
public class TestFragment extends Fragment {
private void myTestMethod() {
NestedFragment nested = new NestedFragment();
getChildFragmentManager()
.beginTransaction()
.add(R.id.container, nested)
.commit();
}
}
this binds NestedFragment to TestFragment. I say it again, NestedFragment, just like TestFragment, is to be used only in this way, it has no meaning to work otherwise.
Back to the question, how should I behave in this situation?
Question B
1) should I provide an interface in NestedFragment, and make so that TestFragments implements NestedFragmentInterface? In this case I would act as following
NestedFragment
public class NestedFragment extends Fragment {
private NestedFragmentInterface listener;
public interface NestedFragmentInterface {
void actionMethodNested();
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (getParentFragment() instanceof NestedFragmentInterface) {
listener = (NestedFragmentInterface) getParentFragment();
}
// sending the event
if (listener != null) listener.actionMethodNested();
}
}
2) should (or could) I ignore interface, and just call
getParentFragment().publicParentMethod();
?
3) should I create the interface in NestedFragment, but let the activity implements it, so that the activity will call TestFragment ?
Question C
Regarding the idea of using the Activity as a bridge between fragments, I believe it is made so for proper handling lifecycle of all these objects. Is it still viable to do a direct fragment-to-fragment (using interface or directly call public methods) while trying to handle manually the exception the system might throw me?
Ill do my best to answer the wall of text here :)
Question A:
Fragments are designed to be reusable modules that can be plug and played with any activity. Because of this the only correct way way to interface with the activity is to have the activity inherit from a interface that the fragment understands.
public class MapFragment extends Fragment {
private MapFragmentInterface listener;
public interface MapFragmentInterface {
//All methods to interface with an activity
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// sending the event
if (listener != null) listener.anyMethodInTheAboveInterface();
}
}
Then have the activity implement the interface
public class MainActivity extends Activity implement MapFragmentInterface{
//All methods need to be implemented here
}
This allows your fragment to be used with any activity as long as the activity implements this interface. The reason why you need this interface is because the fragment can be used with any activity. Calling a method like
((TestActivity) getActivity().myCustomMethod();
relies on the fact that your fragment only can work within a test activity and therefore "breaks" the rules of fragments.
Question B and C:
Assuming that you are following the correct guidelines for fragments and that they are independent modules. Then you should never have a situation where fragments need to know about each other. 99% of the time that people think they need fragments to directly communicate they can re-factor their problem to the situation I gave above by using a MVC patten or something similar. Have the activity act like the controller and tell the fragments when they need to update and then create a separate data store.
I will try to clear it all a little bit.
First of all, consider you approach of setting listener for fragment. It is no good to set listener in onViewCreated method, because it leeds to excess reseting listener any fragment is created. It is enough to set it up into onAttach method.
I told about code lines. Take me to notice, it is good to have BaseFragment implemented common behavior in you app as setting FragmentListener creating view from resource.
And more than that to reduce code lines and to get part of code reuse you can use generic in BaseFragment. So look at next code snippet:
public abstract BaseFragment<T extends BaseFragmentListener> extends Fragment {
T mListener;
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Activity instanceof T)
mListener = (T) activity;
}
abstract int getLayoutResourceId();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(getLayoutResourceId(), null);
// you can use some view injected tools here, mb ButterKnife
return layout;
}
}
Answer A (For Question A):
If you have fragment only exactly one activity you need to decide: "Do you really need to use Fragment here?". But mb it is good to have fragment exactly for one activity to extract some view logic from activity and clearing base logic. But for clearing base architecture logic for your app use Listeners. This will make life easier for other developers
Answer B:
For nested fragments you need to solve, what they need to use exact activity or just fragments and use it as a bridge to other system. If you know that nested fragment will be nested all time you, you need to declare parent fragment as listener otherwise you have to use other approach.
Notice:
As the base approach to communicate between diff part of App you can use events, try too look at event bus for example. It gives you common approach for communication and you can extract logic of calling custom methods of listeners and more another, all logic will be situated into handling events and you will have one mediator system for cooperation.
Answer C:
I partially explain one of approaches to cooperating between fragments. Using one event dispatcher avoids you to have many listeners for all different communication. Some times it is very profitably.
Or I think it is more usable to use Activity, or other class lives in Activity, for a mediator for Fragments cooperation because there is many situation of Fragments changing during lifecycle both handling and system. And it focuses all this logic in one place and makes your code more clear.
Hope my considerations helps you.

What is the right way to communicate from a custom View to the Activity in which it resides?

I have a custom View class that extends Spinner. I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection. I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow, or if I should just talk directly to the context (for some reason that doesn't feel safe, even though I can't think of a way in which it might fail, offhand).
the right way to do that, is to "listen" to your custom view by exposing an interface which your view holding a reference to instance of him, and you hosting activity should implement. exactly like the OnItemSelected interface and any events which android views are exposing is been implemented. this is the observer design pattern.
for example:
public class MyCustomSpinner extends Spinner {
public MyCustomSpinner(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public interface IMyEventListener {
public void onEventOccurred();
}
private IMyEventListener mEventListener;
public void setEventListener(IMyEventListener mEventListener) {
this.mEventListener = mEventListener;
}
protected void someMethodWhichDoingSomthingAndShouldRaiseAlsoTheEvent() {
/*
* Some Code which the function doing //more code...
*/
if (mEventListener != null) {
mEventListener.onEventOccurred();
}
}
}
this is how you will use it from your activity:
mMyCustomSpinner.setEventListener(new IMyEventListener() {
#Override
public void onEventOccurred() {
// TODO Auto-generated method stub
}
});
I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection.
You don't want to "talk to the Activity that it's embedded in". You want to talk to the controller responsible for the View. Today, that might be an Activity. Tomorrow, that might be a Fragment.
I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow
That implies that the View knows the specific type of Adapter, since the Adapter interface does not have any sort of getContext() method. Moreover, it ties you to talking to the Activity, which is not a good plan at this point, as mentioned above.
Personally, I'm a bit dubious about having a custom Spinner subclass in the first place. But, assuming there's a good reason for it, you should follow Tal Kanel's advice (posted while I was writing this) and design a custom listener interface for this custom event that is being declared by your custom View. Have the controller (Activity or Fragment) supply an implementation of that interface -- this could be directly implemented on the controller, or implemented as an anonymous inner class (as in Tal Kanel's answer), etc. Have your custom View call method(s) on the listener interface as needed.
The correct way is using a listener of some sort. I think you can make direct reference, your code would just not be reusable for another project then...
A simple solution -
((ParentClass) context).functionToRun();
Where ParentClass is the class name of the activity.

Categories

Resources