Android, how to obtain state in activity from fragment - java

I am following the one activity/ many fragment approach for this application. From the picture, you can see there is a main activity with a toolbar spinner with sorting preferences. The option selected by the user will dictate how the recyclerView sort itself.
My question is, what is the best approach for fragment to obtain the information about what the user has selected in the toolbar spinner while keeping separation of concern?
Here is how the activity gets notified of user event in the spinner:
#Override
public void onSortMenuItemClicked(MenuItem id) {
mFragmentScreenNavigator.swapToJobRequestFragment(mUserProfilePersistence.getUserProfile().getEmail());
}
Once the user has selected a sorting preference, I have a navigator that navigates to the recyclerView fragment
Basically, what I want to achieve is instead of 'telling' the fragment what the sorting preference is, is there a way for the fragment to 'go out' and get that information? thank you

You could use ViewModel for the purpose of communication between Activity and Fragment. To make it work you also can use LiveData to observe for variable changes and updating if it happens.
1) Don't forget to add corresponding dependencies.
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
2) Create a class that extends from ViewModel. In this class, we have MutableLiveData object that allows up to work with LiveData. Probably it's better to use Integer type so you could change it like id.
public class ExampleViewModel extends ViewModel {
private MutableLiveData<String> sortingMode = new MutableLiveData<>();
public LiveData<String> getSortingMode() {
return sortingMode;
}
public void setSortingMode(String mode) {
sortingMode.postValue(mode);
}
}
2) Get a reference to your ViewModel inside Activity so you could update the value with sorting mode from (I believe) your options menu
ExampleViewModel viewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
3) To change the value you simply need to call your ViewModel method that will update the value o variable depending on item/id/title (whatever sounds better for you).
viewModel.setSortingMode("Example Mode");
4) Get the Activity ViewModel inside Fragment
ExampleViewModel viewModel = ViewModelProviders.of(getActivity()).get(ExampleViewModel.class);
5) Subscribe to LiveData object to observe value changes
viewModel.getSortingMode().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
// Provide needed logic depending on sorting preferences
}
});
It's just a little example of how it works. Architecture Components are really powerful so I strongly recommend you to read about it.
Result (Green sector is Fragment inside Activity, they both are observing):
link

Related

How to use search view defined in menuoptions of the MainActivity in fragments?

I am trying to use the searchview in the toolbar (menu) in multiple fragments. So, if I switch to various fragments, the searchview would apply to and search only that fragment then. After defining it in the MainActivity, how do I use it in the fragments?
You should keep reference to your fragments like you want. Then depending from your situation, when you typing the request or any other way, you need to make search in your fragment, like this:
myFragment.doSearch(query)
If you have a list of frargments and you need to make a call only in currently visible, you can define the isActuallyVisible variable in each fragment(on in BaseFragment if you have one) and change it like this:
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
isActuallyVisible = visible;
}
Then you can make search like this:
for (Fragment frag : fragmentsList) {
if (frag.isActuallyVisible) {
frag.doSearch(query);
break;
}
}
Hope this will help you to find the correct solution

Load a single Fragment with different arguments in navigation drawer

Suppose I have a navigation drawer which contains three menu item i.e cat1, cat2, and cat3. Whenever a user clicks on this it will open a Fragment which will fetch the data from the web server and parse the JSON data and show into recycler view.
Now my question is.
Do I need to create separate fragments for each menu item of navigation drawer i.e cat1, cat2 and cat3? Or I can use one fragment and pass an argument like this
http://example.com?cat=1 to that fragment and load the recycler view item?
So which procedure should I follow to achieve this goal, separate fragment for each menu item or a single fragment? Thanks.
If you want to fetch data every time you click on a category, it's better to have one fragment and make it call the API. But if you use three fragments, it reduces number of API calls in addition to faster switching between categories. However, there is a trade-off between them. Also in second case, you should care about updating contents that is fetched from API every time.
You should definitely use a single fragment and avoid some boilerplate code. Here is a how you should do it:
public class CategoryFragment extends Fragment {
public static CategoryFragment newInstance(int categoryId) {
CategoryFragment fragment = new CategoryFragment();
Bundle extras = new Bundle();
extras.putInt("categoryId", categoryId);
fragment.setArguments(extras);
return fragment;
}
...
// Determine which category you're on
private int getCategoryId() {
return getArguments().getIntExtra("categoryId", 1);
}
}
And to instantiate your fragment you can simply use:
// Construct a cat2 fragment
CategoryFragment categoryFragment = CategoryFragment.newInstance(2);
You should use only one fragment in this case this is how you can reuse design and code by using just one fragment for each category cat1, cat2, cat3.
You can pass category id to the fragment via a bundle. check this tutorial

get the value from a method in another class [duplicate]

This question is mostly to solicit opinions on the best way to handle my app. I have three fragments being handled by one activity. Fragment A has one clickable element the photo and Fragment B has 4 clickable elements the buttons. The other fragment just displays details when the photo is clicked. I am using ActionBarSherlock.
The forward and back buttons need to change the photo to the next or previous poses, respectively. I could keep the photo and the buttons in the same fragment, but wanted to keep them separate in case I wanted to rearrange them in a tablet.
I need some advice - should I combine Fragments A and B? If not, I will need to figure out how to implement an interface for 3 clickable items.
I considered using Roboguice, but I am already extending using SherlockFragmentActivity so that's a no go. I saw mention of Otto, but I didn't see good tutorials on how to include in a project. What do you think best design practice should be?
I also need help figuring out how to communicate between a fragment and an activity. I'd like to keep some data "global" in the application, like the pose id. Is there some example code I can see besides the stock android developer's information? That is not all that helpful.
BTW, I'm already storing all the information about each pose in a SQLite database. That's the easy part.
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!
The suggested method for communicating between fragments is to use callbacks\listeners that are managed by your main Activity.
I think the code on this page is pretty clear:
http://developer.android.com/training/basics/fragments/communicating.html
You can also reference the IO 2012 Schedule app, which is designed to be a de-facto reference app. It can be found here:
http://code.google.com/p/iosched/
Also, here is a SO question with good info:
How to pass data between fragments
It is implemented by a Callback interface:
First of all, we have to make an interface:
public interface UpdateFrag {
void updatefrag();
}
In the Activity do the following code:
UpdateFrag updatfrag ;
public void updateApi(UpdateFrag listener) {
updatfrag = listener;
}
from the event from where the callback has to fire in the Activity:
updatfrag.updatefrag();
In the Fragment implement the interface in CreateView do the
following code:
((Home)getActivity()).updateApi(new UpdateFrag() {
#Override
public void updatefrag() {
.....your stuff......
}
});
To communicate between an Activity and Fragments, there are several options, but after lots of reading and many experiences, I found out that it could be resumed this way:
Activity wants to communicate with child Fragment => Simply write public methods in your Fragment class, and let the Activity call them
Fragment wants to communicate with the parent Activity => This requires a bit more of work, as the official Android link https://developer.android.com/training/basics/fragments/communicating suggests, it would be a great idea to define an interface that will be implemented by the Activity, and which will establish a contract for any Activity that wants to communicate with that Fragment. For example, if you have FragmentA, which wants to communicate with any activity that includes it, then define the FragmentAInterface which will define what method can the FragmentA call for the activities that decide to use it.
A Fragment wants to communicate with other Fragment => This is the case where you get the most 'complicated' situation. Since you could potentially need to pass data from FragmentA to FragmentB and viceversa, that could lead us to defining 2 interfaces, FragmentAInterface which will be implemented by FragmentB and FragmentAInterface which will be implemented by FragmentA. That will start making things messy. And imagine if you have a few more Fragments on place, and even the parent activity wants to communicate with them. Well, this case is a perfect moment to establish a shared ViewModel for the activity and it's fragments. More info here https://developer.android.com/topic/libraries/architecture/viewmodel . Basically, you need to define a SharedViewModel class, that has all the data you want to share between the activity and the fragments that will be in need of communicating data among them.
The ViewModel case, makes things pretty simpler at the end, since you don't have to add extra logic that makes things dirty in the code and messy. Plus it will allow you to separate the gathering (through calls to an SQLite Database or an API) of data from the Controller (activities and fragments).
I made a annotation library that can do the cast for you. check this out.
https://github.com/zeroarst/callbackfragment/
#CallbackFragment
public class MyFragment extends Fragment {
#Callback
interface FragmentCallback {
void onClickButton(MyFragment fragment);
}
private FragmentCallback mCallback;
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt1
mCallback.onClickButton(this);
break;
case R.id.bt2
// Because we give mandatory = false so this might be null if not implemented by the host.
if (mCallbackNotForce != null)
mCallbackNotForce.onClickButton(this);
break;
}
}
}
It then generates a subclass of your fragment. And just add it to FragmentManager.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), "MY_FRAGM")
.commit();
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag(), Toast.LENGTH_SHORT);
mToast.show();
}
}
Google Recommended Method
If you take a look at this page you can see that Google suggests you use the ViewModel to share data between Fragment and Activity.
Add this dependency:
implementation "androidx.activity:activity-ktx:$activity_version"
First, define the ViewModel you are going to use to pass data.
class ItemViewModel : ViewModel() {
private val mutableSelectedItem = MutableLiveData<Item>()
val selectedItem: LiveData<Item> get() = mutableSelectedItem
fun selectItem(item: Item) {
mutableSelectedItem.value = item
}
}
Second, instantiate the ViewModel inside the Activity.
class MainActivity : AppCompatActivity() {
// Using the viewModels() Kotlin property delegate from the activity-ktx
// artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.selectedItem.observe(this, Observer { item ->
// Perform an action with the latest item data
})
}
}
Third, instantiate the ViewModel inside the Fragment.
class ListFragment : Fragment() {
// Using the activityViewModels() Kotlin property delegate from the
// fragment-ktx artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by activityViewModels()
// Called when the item is clicked
fun onItemClicked(item: Item) {
// Set a new item
viewModel.selectItem(item)
}
}
You can now edit this code creating new observers or settings methods.
There are severals ways to communicate between activities, fragments, services etc. The obvious one is to communicate using interfaces. However, it is not a productive way to communicate. You have to implement the listeners etc.
My suggestion is to use an event bus. Event bus is a publish/subscribe pattern implementation.
You can subscribe to events in your activity and then you can post that events in your fragments etc.
Here on my blog post you can find more detail about this pattern and also an example project to show the usage.
I'm not sure I really understood what you want to do, but the suggested way to communicate between fragments is to use callbacks with the Activity, never directly between fragments. See here http://developer.android.com/training/basics/fragments/communicating.html
You can create declare a public interface with a function declaration in the fragment and implement the interface in the activity. Then you can call the function from the fragment.
I am using Intents to communicate actions back to the main activity. The main activity is listening to these by overriding onNewIntent(Intent intent). The main activity translates these actions to the corresponding fragments for example.
So you can do something like this:
public class MainActivity extends Activity {
public static final String INTENT_ACTION_SHOW_FOO = "show_foo";
public static final String INTENT_ACTION_SHOW_BAR = "show_bar";
#Override
protected void onNewIntent(Intent intent) {
routeIntent(intent);
}
private void routeIntent(Intent intent) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case INTENT_ACTION_SHOW_FOO:
// for example show the corresponding fragment
loadFragment(FooFragment);
break;
case INTENT_ACTION_SHOW_BAR:
loadFragment(BarFragment);
break;
}
}
}
Then inside any fragment to show the foo fragment:
Intent intent = new Intent(context, MainActivity.class);
intent.setAction(INTENT_ACTION_SHOW_FOO);
// Prevent activity to be re-instantiated if it is already running.
// Instead, the onNewEvent() is triggered
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(intent);
There is the latest techniques to communicate fragment to activity without any interface follow the steps
Step 1- Add the dependency in gradle
implementation 'androidx.fragment:fragment:1.3.0-rc01'

Android Display 2 fragments inside another one

I've got an Application with a MainActivity with a Navigation Drawer MenĂ¹.
Inside the MainActivity View I've got a Frame layout which takes the whole space.
When the user select something from the menĂ¹ I call a method which handle the fragments transaction inside the Frame Layout.
My MainActivity is the Fragment manager and from here i can handle all the changes I want and i can handle the communications between the fragments.
Here's the problem:
In one of my Fragment i'd like to show 2 new fragments, one with a PieChart and one with the data.
I could have done only one Fragment with all inside its view but i'd like to use a modular managing way.
So, first of all i created 1 fragment, the Container one (we can call it A).
In its view i put the fragment B and fragment C, directly from Xml.
In fragment B i've got the PieChart, HERE i call the methods to download data from a database, and then i put this data inside the chart.
I wrote a simple Interface which contains a method which is called when the user interact wich the chart; it build an object whith the selected data.
My target is to send this data to Fragment A and then pass them to Fragment C, so that the user can see what is he selecting.
In Fragment A i've Implemented the Fragment B inteface, then i set up this code inside OnAttach of B:
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
Interface = (InterfaceB) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement InterfaceB");
}
}
Everithing seems to works, but in the moment tha Fragment A is created, OnAttach of B is called, and the context which arrives refers to MainActivity and not to Fragment A.
This causes a Crash.
Fragment is not an instance of Context, so it is impossible to pass it to onAttach(Context Context) anyway.
There are two ways how you can do what you want.
1) Use onAttachFragment(Fragment fragment) inside fragment A, and catch events when fragments B and C are attached;
2) Use getParentFragment() inside fragments B and C, to access A.

How to update UI for callback received while app is in background?

I am creating an android application using MVP architecture. I have created Presenter and Interactor classes. I am struggling to update UI when app is in background and comes back to foreground.
Let's consider following scenario.
Button on UI is clicked and presenter is notified about it. Presenter asked Interactor to provide data from backend. By the time Interactor provides result to presenter somehow UI is in background. How can I save the state of it and change UI components once app is in foreground.
I tried doing following things:
Using flags (turn on the flag and check in onResume to call the method that supposed to be called when callback received. But problem is that let's say if I have 5 different services that can be called by presenter which has different callback then I will require 5 flags which I don't like it)
Using JAVA reflection (Store the name of method be called in HashMap with parameter)
Is there any better way to achieve this?
As name MVP Suggests Model View Presenter
Model : Model is responsible for getter setter methods also known as POJO.
View : Contains activities/fragments with views.
Presenter : Actual Business Logic where you can communicate network calls OR relate with backend databases such as SQLite / MySql.
When button click you need to call presenter which will perform background tasks once it is done you need to notify your view that the response is Success/Failure via CallBacks which done with the help of interfaces.
Example:
interface BaseContract {
interface BaseView {
//Methods for View
void onDoSomething();
}
interface BasePresenter {
void doSomething();
}
}
class BaseMainPresenter implements BaseContract.BasePresenter {
BaseContract.BaseView view;
BaseMainPresenter(BaseContract.BaseView view) {
this.view = view;
}
#Override
public void doSomething() {
if (view != null)
view.onDoSomething();
}
}
class DemoClass implements BaseContract.BaseView {
//Create object of Presenter
/****
* Example :
* BaseMainPresenter baseMainPresenter = new BaseMainPresenter(this);
*/
#Override
public void onDoSomething() {
//Deal with Context here.
}
}
You could add some "pause" logic to your presenter:
Whenever the UI goes to background, you tell the presenter to "pause" and to save any data being received to a cache (could be as simple as a list inside your presenter).
Whenever the UI is back to foreground, you tell your presenter to unpause and to unload its cache to the UI (if the cache list is not null it pushes it to the UI, just like a regular response received by your interactor)

Categories