Inject extends services in Fragment in Android - android

I'd like to create a fragment using a generic Service, but can't find a way to define the service.
It could be represented like that.
Fragment MyGenericFragment<S service> extends Fragments
private S service;
Fragment FragmentA extends MyGenericFragment<SpecializedService>
In my FragmentA I try define my specialized service like this :
#Inject
SpecializedService service;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((App) getActivity().getApplication()).inject(this);
}
Is there a way to construct Fragments this way.
Objective is to avoid duplicate code for a same representation.

I find a first solution, but I don't know if it's a good practice (quite sure not, but do the job).
FragmentA can declare a service, receiving it by Dagger inject and set it as the service
Fragment FragmentA extends MyGenericFragment<SpecializedService>
#Inject
DedicatedService dedicatedService;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((App) getActivity().getApplication()).inject(this);
this.service = dedicatedService;
}

Related

ViewModel for fragment

I am applying MVVM pattern in native Android development. I have a LoginActivity and it will host two different fragments that are LoginFragment and RegisterFragment. I am not sure what to do in this case. A viewModel for the LoginActivity or two different ViewModel for two fragments. If I create one viewModel for activity, login and register view bindings and logics will mix up with each other. But in many tutorials developers are saying that we should create one viewmodel per activity and access those viewmodels from fragment. How should I handle things here?
My login fragment if necessary
public class LoginFragment extends Fragment {
private LoginViewModel loginViewModel;
public LoginFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentLoginBinding fragmentLoginBinding = DataBindingUtil.setContentView(getActivity(), R.layout.fragment_login);
loginViewModel = ViewModelProviders.of(this, new LoginViewModelFactory(getActivity().getApplication(),
LoginRepository.getInstance())).get(LoginViewModel.class);
fragmentLoginBinding.setViewModel(loginViewModel);
fragmentLoginBinding.executePendingBindings();
return inflater.inflate(R.layout.fragment_login, container, false);
}
}
and LoginActivity
public class LoginActivity extends AppCompatActivity {
private LoginViewModel loginViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
FragmentUtil.replaceFragment(getSupportFragmentManager(), R.id.entry_frame, new LoginFragment(), "login");
}
}
One way to access the Activity ViewModel is by calling getActivity in the Fragment, then casting it to the Class that you have and accessing the PUBLIC viewModel property.
I don't know your exact usecase but I might use multiple Fragments and have a repository above those that has some of the shared logic.
You can take away some of the boilerplate of multiple Fragments by doing them like so:
https://itnext.io/get-rid-of-fragment-activity-boilerplate-code-using-kotlin-1b103763baf8?sk=a4b037902f0fe4bfdec10cc9cc814426
In my opinion, you should create two different ViewModel. Because, ViewModel is designed to store and manage UI-related data in a lifecycle of Activity or Fragment. Do that help you independent and easy to management logic.
If you want to share data between fragment and activity or fragment and fragment, you can see:
NavigationGraph: https://developer.android.com/guide/navigation/navigation-programmatic#share_ui-related_data_between_destinations_with_viewmodel
Normally: https://developer.android.com/guide/navigation/navigation-pass-data

How to communicate from Activity to Fragment?

I understand that using an interface is a common way to communicate from Fragment to Activity. However, what are the good practices for communicating from Activity to Fragment?
In my project, my Activity hosts 3 Fragments using a ViewPager. My Activity currently listens from a backend server and I'd like to somehow notify my 3 Fragments whenever I get an update from the backend server. I tried implementing the interface approach but it already seems like a hassle as I'm already having difficulties getting references to my 3 Fragments in my Activity as the Fragments are created in a separate class which is a subclass of FragmentPagerAdapter.
Is there a better / cleaner way than using interace for communicating from Activity to Fragment?
You can try ViewModel to communicate between fragments and this is official android documentation.
Here is the link you can read it. Share data between fragments
And here is a blog to read about live data link
here is example
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}

Android MVP - How to communicate between activity presenter and fragment presenter

I have an activity with 3 fragments, currently I use ViewPager. I want to implement MVP and communicate between activity presenter and fragment presenters i.e:
Passing data from activity presenter to fragment presenters
Sending event from fragment presenters to activity presenter
...
But I don't know how to do it in official way. I can use BusEvent but I don't think it's a good practice.
Communication between fragments and activity or vice-versa can be done by using
nnn's answer or you could use ViewModel and LiveData witch provides a cleaner way and respect the lifecycle from fragments and activities which can save from writing a few lines of code in attempt to prevent a a non-visible fragment from receiving data on the background.
First you extend the ViewModel class, initialize the Livedata and some helper methods.
public class MyViewModel extends ViewModel {
private MutableLiveData<String> toFragmentA, toFragmentB;
private MutableLiveData<List<String>> toAllFragments;
public MyViewModel() {
toFragmentA = new MutableLiveData<>();
toFragmentB = new MutableLiveData<>();
toAllFragments = new MutableLiveData<>();
}
public void changeFragmentAData(String value){
toFragmentA.postValue(value);
}
public void changeFragmentBData(String value){
toFragmentB.postValue(value);
}
public void changeFragmentAllData(List<String> value){
toAllFragments.postValue(value);
}
public LiveData<String> getToFragmentA() {
return toFragmentA;
}
public LiveData<List<String>> getToAllFragments() {
return toAllFragments;
}
public LiveData<String> getToFragmentB() {
return toFragmentB;
}
}
Then you initialize the ViewModel on your activity.
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private TabLayout tabLayout;
MyViewModel mViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this)
.get(MyViewModel.class);
viewPager.setAdapter(new Adapter(getSupportFragmentManager()));
}
}
reading the data in the fragments:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
mViewModel.getToAllFragments().observe(this, new Observer<List<String>>() {
#Override
public void onChanged(List<String> s) {
myList.addAll(s);
//do something like update a RecyclerView
}
});
mViewModel.getToFragmentA().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
mytext = s;
//do something like update a TextView
}
});
}
to change the values of any of the live datas you can use one of the methods in any of the fragments or in the activity:
changeFragmentAData();
changeFragmentBData();
changeFragmentAllData();
Whats happing behind the scenes:
when you use mViewModel = ViewModelProviders.of(this).get(MyViewModel.class) you are creating a n instance of ViewModel and binding it to the lifecycle of the given activity of fragment so the view model is destroid only the the activity or fragement is stopped. if you use mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class)you are bindig it to the lifecycle if the parentactivity`
when you use mViewModel.getToFragmentA().observe() or mViewModel.getToFragmentB().observe() or mViewModel.getToAllFragments().observe() you are connecting the LiveData in MyViewModel class to the given fragment or activity an the value of the onChange() method is updated in all the classes that are observing the method.
I recomend for personal expirience a bit of research about Livedata end ViewModel which ou can on youtube or this link
As per my understanding, for your UseCase, suppose ActivityA have a viewPager having 3 Fragments(FragmentA, FragmentB, FragmentC).
ActivityA have ActivityPresenterA
FragmentA have FragmentPresenterA
As per MVP, FragmentPresenterA should be responsible for all the logical and business flows of FragmentA only and should communicate with FragmentA only. Therefore, FragmentPresenterA can not directly communicate with ActivityPresenterA.
For communication from Fragment to Activity, presenter should not be involved and this should be done as we would communicate in non-MVP architecture, i.e. with the help of interface.
Same applies for Activity to Fragment communication.
For communication between Activity and Fragment read here
You can use one presenter for that case.
Used your Activity Presenter to get all the data that your fragments need.
then create an interface class and implement it to your fragments.
For example:
Create a public interface for your PageAFragment (this interface will the bridge of your data from activity to fragment). and use the method of your interface to handle the result from your presenter to view.
This is the example of interface class that I created for received data. for the parameter you can choose what you want it depends on your need, but for me I choose model.
public interface CallbackReceivedData {
void onDataReceived(YourModel model);
}
In MainActivity Class check the instance of fragment that attached into your activity. put your checking instance after you commit the fragment.
public class MainActivity extends AppCompatActivity{
private CallbackReceivedData callbackReceivedData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
}
I assumed that the receivedDataFromPresenter is the received method of our view and get data to presenter.
And now we will pass the data from presenter to callbackReceivedData
In PageAFragment implement the CallbackReceivedData and Override the onDataReceived method. Now you can passed the data from activity to your fragment.
public class PageAFragment extends Fragment implements CallbackReceivedData{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onDataReceived(YourModel model) {
}
}
Note: Alternative way, you can use Bundle and pass the data with the use of setArguments.
If you want to send Event from Fragment to Activity you can follow this Idea.
Create an Interface class and implement it to your MainActivity and Override the method from interface to your activity, for My case I do it something like this.
Here's my CallbackSendData Class.
public interface CallbackSendData {
void sendDataEvent(String event);
}
Implement CallbackSendData interface to your MainActivity and Override the sendDataEvent method.
public class MainActivity extends AppCompatActivity implements CallbackSendData{
private CallbackReceivedData callbackReceivedData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
#Override
public void sendDataEvent(String event){
//You can now send the data to your presenter here.
}
}
And to your PageAFragment you need to use attach method to cast your interface. The attach method called once the fragment is associated with its activity. If you want to understand the lifecycle of fragment just click this link: https://developer.android.com/reference/android/app/Fragment.html.
public class PageAFragment extends Fragment implements CallbackReceivedData{
private CallbackSendData callbackSendData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onDataReceived(YourModel model) {
//Received the data from Activity to Fragment here.
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.PagerAFragment, container,
false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle
savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button Eventbutton;
Eventbutton = view.findViewById(R.id.event_button);
Eventbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callbackSendData.sendDataEvent("send Data sample");
}
});
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
callbackSendData = (CallbackSendData) context;
}catch (ClassCastException e){
e.printStackTrace();
}
}
}
And now you can use the CallbackSendData to send the data from activity to fragment.
Note: It's much easier if you are using Dependency Injection to your project, you can use Dagger2 library.
Goodluck.
To communicate between a Fragment and an Activity (whether between their presenters or their classes), you need an interface that your activity implements (like ShoppingInteractor).
This way you can call ((ShoppingInteractor)getActivity()).doSomething() in the fragments. If you want your activity's presenter to handle the task, you need to call the presenter in the doSomething inside the activity.
You can do the same with the fragments with another interface and call the fragment's interactor inside the activity.
You can even have a Presenter getPresenter() inside these interfaces to have access to the actual presenter. (((ShoppingInteractor)getActivity()).getPresenter().sendData(data)). Same goes for the fragments.
If you want to use MVP, the first step is to create one presenter for each View, I mean, If you have 3 fragments, then would have 3 presenters. I think that is a bad idea to create one presenter for 4 views (activity and 3 fragments).
Dynamic data:
Here is an example using rxjava2, dagger2 and moxy.
Conditionalities:
Presenters do not depend on the life cycle of the view
One presenter - one view. The views do not share the presenters among themselves and one view has only one presenter.
The solution is similar to the EventBus, but instead uses Subject with a limited lifetime. It is in the component that is created when the activity starts and is destroyed when it exits. Both activity and fragments have an implicit access to it, they can change the value and respond to it in their own way.
Example project: https://github.com/Anrimian/ViewPagerMvpExample
Static data:
Just use arguments in the fragment and that's it.

how to pass data from a java class to a fragment in android

So i have a FragmentPagerAdapater called SectionsPagerAdapter and a fragment called TeamFragment where I display data from a specific team. So basically I don't want to create different fragments for each team. That is an overkill. I just want 1 fragment which basically connects to the backend then collects the data based on the team then displays that data. But I dont know how to pass the Team name(a string type) from SectionsPagerAdapter to the TeamFragment so that in TeamFragment, I can easily know what to retrieve from the backend. My backend in parse.com. Please help me figure this out and learn. Thanks
So this is was solved my problem. In my sectionsPagerAdapter class i had the below code
Bundle args = new Bundle();
args.putString("TeamName", team);
TeamFragment teamFragment = new TeamFragment();
teamFragment.setArguments(args);
In onCreateView of my TeamFragment, i had the following
Bundle bundle = this.getArguments();
mTeam = bundle.getString("TeamName");
hope this can help someone else. Thanks
Communicating data into fragments is typically done through a simple setter function that is called by the activity that instantiates or contains the fragment:
public class MyActivity extends FragmentActivity {
#Override
protected void onCreate(Bundled savedInstanceState) {
// ...
TeamFragment fragment =
(TeamFragment) (getSupportFragmentManager().findFragmentById(fragmentId));
fragment.setTeamName(teamName);
// ...
}
For communicating data back to the activity, is typically done using a fragment-specific "Listener" interface. This listener can be attached using the same method (by calling a method on the fragment in the parent activity to register the listener) or it can be done by requiring that the parent Activity implement the listener interface, and casting the parent activity to this listener interface in onAttach() (though the latter approach is not as clean of an approach). Example:
public class MyActivity extends FragmentActivity {
#Override
protected void onCreate(Bundled savedInstanceState) {
// ...
TeamFragment fragment =
(TeamFragment) (getSupportFragmentManager().findFragmentById(fragmentId));
fragment.setTeamName(teamName);
fragment.setTeamSelectedListener(new TeamSelectedListenerImpl());
// ...
}
Or:
public class TeamFragment extends Fragment {
public interface TeamSelectedListener {
// ...
}
// ...
#Override
protected void onAttach(Activity activity) {
teamSelectedListener = (TeamSelectedListener) activity;
}
// ...
}
public class MyActivity
extends FragmentActivity
implements TeamFragment.TeamSelectedListener {
// ...
}

Android Fragment and Dependency Injection

as the title says, I'm trying to figure out which one is the best way to inject a dependency in a Fragment.
I want to be independent from external frameworks like RoboGuice etc.
Now, in the simplest way possible, I have an interface that abstracts some kind of logic, and, from an Activity, I want to inject an implementation of this interface. I know that I have to provide a default constructor for my fragment, since the system might need to recreate the fragment at some point, and that the usual way to create a new instance of the fragment is to provide static method that handles the creation like this:
public static Fragment newInstance() {
final Bundle bundle = new Bundle();
...
final Fragment fragment = new MyFragment();
fragment.setArguments(bundle);
return fragment;
}
How can I pass my dependency to the fragment? Should I make it implement the Parcelable or Serializable interfaces and then pack it in the Bundle? Is there some other way to achieve the result?
A simple solution is to declare an interface which is declaring the Dependencies required for the Fragment. Then let the Context of the Fragment implement this interface, and poll the dependencies when needed from the Context.
Contract:
public interface MyDependencies {
MyDep getDep();
}
Activity:
public MyActivity extends Activity implements MyDependencies {
#Override
public MyDep getDep(){
return createMyDependencyOrGetItFromASingletonOrGetItFromApplication()
}
}
Fragment:
public void onActivityCreated(Bundle b){
super.onActivityCreated(b)
if (getActivity() instanceOf MyDependencies) {
MyDep dep = ((MyDependencies) getActivity).getDep();
} else {
throw new RuntimeException("Context does not support the Fragment, implement MyDependencies")
}
}
So, in fact, there is no unnecessary coupling to the Activity because the contract is defined by an interface.
Why don't you grab the dependency from your activity?
public void onActivityCreated( Bundle b){
super.onActivityCreated(b)
DependencyClass c = ((MyActivity)getActivity()).getDependency();
}
If you can't pass in the dependency via the constructor (if you need a default constructor), and you don't want to use a dependency injection lib like Dagger or RoboGuice, the other classic way is to a setter to "inject" the dependency.
Fragment MyFragment {
Depend mDepend;
....
public void setDepend(Depend depend) {
mDepend = depend;
}
}
Then in your activity you can inject the dependency in the onCreate method.
so something like this in your activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
MapFragment wrapperFragment = new WrapperFragment();
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.map_container, wrapperFragment).commit();
// find the fragment
// call the setter
}
}

Categories

Resources