Send Object created from MainActivity to Fragments TabbedView [duplicate] - java

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'

Related

How to use instance of API from Main Activity in Fragments?

If i create an instance of an API in Main Activity, can i use it in fragments so i don't need to create that instance again? Example:
public class MainActivity extends AppCombatActivity {
CustomApi customApi;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
customApi = new CustomApi(this, key);
}
}
How can i access this customApi instance in fragments created by the MainActivity without creating an instance of this CustomApi again?
Fragment has access to its Activity, so you can do something like this in your Fragement:
CustomApi customApi = ((MainActivity) getActivity()).customApi;
customApi.method1(...);
This is a bit brute force, but is a simple and easy way to do the job. This assumes that your Fragment is only used by a single Activity. If your Fragment is used by multiple activities, you could define an Interface and have all parent activities implement that interface and access the customApi that way.
A cleaner, more Android-approved, more professional approach would be to use a ViewModel. The ViewModel is shared by the Activity and all of its Fragments and you could manage the custom API reference in the ViewModel. For some examples of how to use ViewModel to communicate between Activity and Fragment, you can look here:
https://developer.android.com/guide/fragments/communicate

Passing data between Parent fragment and Child fragment

I'm struggling with a communication between 2 or more fragments
My application architecture looks like this:
MainActivity (Menu Drawer)
----> MealsFragment
----> ProfileFragment
----> StatsFragment
----> SportFragment
----> ContactUsFragment
(By clicking on menu drawer main_container is replaced with selected Fragment)
Earlier, I had single activities instead of fragments but I have read that I need to convert them into Fragments to correctly implement menu drawer (so I did it).
The problem is each of the activities had its own child-activities which were communicating with parents with using onActivityResult. Now I don't know how to do it with fragments.
The scenario is like this:
Open MealsFragment from MainActivity (it works)
Open AddMealFragment from MealsFragment (with data from MealsFragment), fill the form and then return all information provided there by user to MealsFragment
Use received data for further actions
I have already seen posts recommending using settargetFragment() and getTargetFragment() but I don't know how to do it and - what is more important - I don't know how to receive the data afterwards
I hope you are aware of how to do Fragment transactions, i.e. using Fragment Manager class to add or remove fragments from the backstack. If you don't know then it's a good idea to learn that first.
Now that you know how to add or remove fragments, passing data among them is a simple thing of all of that in fragment's Bundle that you can access in the receiver fragment. Here's an example:
class ConversationFragment : Fragment() {
companion object {
const val JOB_REQUEST_ID = "jobRequestId"
#JvmStatic
fun newInstance(jobRequestId: String) =
ConversationFragment().apply {
arguments = Bundle().apply { putString(JOB_REQUEST_ID, jobRequestId) }
}
}
}
In order to create ConversationFragment, I expose a newInstance method that the FragmentManager or any other entity can use to create one. However, they would need to pass a JOB_REQUEST_ID in order to create one. I simply put this id in a Bundle and pass it on to the fragment as its argument. On the receiver side(fragment), you can get a handle to this bundle and retrive the value:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
jobRequestId = it.getString(JOB_REQUEST_ID)
}
}

How to handle communication between different viewModels?

I have an activity that contains a number of fragments. Each fragment has a viewModel scoped to its lifetime with some logic inside. The host activity has a viewModel too, including some code to show a popup-style message.
I want my fragment viewModels to be able to post messages to this popup. However, how could I access the activity-viewModel from inside one of my fragment-viewModels?
I'll write some exemplary Kotlin code, but the question is not specific to Kotlin since it's more of an architectural issue.
class MyActivityViewModel {
...
popupMessage = MutableLiveData<String>("") // is observed by my activity
fun postMessage(text: String) {
popupMessage.value = text
}
}
class MyFragmentAViewModel {
...
fun someFunctionA() {
// want to call ActivityViewModel's postMessage from here
}
}
class MyFragmentBViewModel {
...
fun someFunctionB() {
// want to call ActivityViewModel's postMessage from here too
}
}
I can't easily call ViewModelProvider since I'd rather not keep a reference to an Activity in my viewModel. The only direct option I see is to pass the activity-viewModel to the fragment-viewModels through the constructor or an init() method. That should be safe since the parent viewModel's lifetime should exceed the fragment viewModels' lifetime. I think.
Still, that solution rubs me the wrong way.
Are there any other alternatives? Or perhaps an entirely different approach to the issue?
Here's the thought of a greenhorn:
Can't you tell the activity that your fragment wants to use its method?
If you
Make an interface with a method a la "fragmentAWantsToUsePostMessage" in your fragment
Implement the interface in the activity, so that every time fragmentAWantsToUsePostMessage is called, the activity calls postMessage
Get a reference to the implementation of the interface in your fragment
Use that reference when the fragment needs to call "post message"
Shouldn't that work? Or is that against your "not keeping a reference"?
As I said: I'm new to all of this, so I might be completely wrong.
I can see that there's a post on medium that might be relevant: How to Communicate between Fragment and Activity

Is it possible to abstract away repeated parts of XML code?

I am developing an android app, and I am trying to implement HorizontalScrollView on almost all the activities in the app. (almost 50+).
I am looking to define a class/variable/function in the java file, then call it on the XML file, so that I don't have to retype/copy and paste the code more than 50 times.
Also, if I define a function in my MainActivity, is it possible to use it in other java/XML files?
In xml file you can include other xml layout. I think you can use it.
First of all, you should only have ONE Activity in your app as an entry point and use Fragments for different windows, this is an idea which Google is pushing at the moment.
Second of all, YES, you can reuse MainActivity function in other java files, but not in XML files. Example:
Define an interface:
public interface MyInterface {
void triggerMainActivityFunction();
}
Then implement this interface in your MainActivity:
public class MainActivity extends Activity implements MyInterface {
...
#Override
public void triggerMainActivityFunction() {
// Do something
}
...
}
Define listener in all of the java classes where you want to trigger this function:
public class SomeClass extends Fragment {
...
private MyInterface listener;
#Override
void onCreate(...) {
listener = (MyInterface) getActivity();
}
// your function to trigger a reusable
// function from Activity when user clicks on something
public void onClick() {
listener.triggerMainActivityFunction();
}
...
}
Third, YES, you can reuse XML layouts by using include tag.
Hope this helps. Good luck :)

How does an interface communicate - Need a detailed answer

I have a fragment class:
public class UploadFragmentOne extends Fragment {}
I have subclassed:
public interface Communicator {
void communicate(int position);
}
In the onCreateView:
((Communicator) getActivity()).communicate(1);
The hosting activity signature:
public class DetailsPager extends FragmentActivity implements UploadFragmentOne.Communicator {}
Member function in the above activity:
public void communicate(int position) {
Toast.makeText(DetailsPager.this, "Clicked " + position, Toast.LENGTH_LONG).show();
}
This works like a charm, but I dont understand HOW? Sorry this might be too dumb, but I want to know how the control flows in this?
You should have a look on the following link once.-
Now come to your query
(MyActivity)activity.some_method()
Now suppose you are going to attach the same fragment for another activity say MainActivity then you need to do like -
(MainActivity)activity.some_method()
Now suppose some other activity are implementing the same fragment then each time you need to use the instanceOf check and then call the method and also add it.
Again suppose a trivial case when you are going the create a library and you are going to offer some result delivery for the particular event then?
How will you get the instance type ?
interface as the name suggests offer a way to be communicated without the actual class instance. They only need to be implemented. You can have a look on the OnClickListener in the View class of android API sources.

Categories

Resources