Get Data back to calling Fragment from 2nd Dialog Fragment - android

Scenario
I got multiple fragments that are going to call DialogFragment#1 which further got 3 Buttons and each button opens another DialogFragment#2.
What I want here is to get a String value that user selected via DialogFragment#2 and send it back to callingFragment(can be any one out of 6) to set textView Text.
I can do this by keeping different DialogFragment classes for each 6 fragments but I want to keep 1 class for DialogFragment #1 and 1 for DialogFragment #2 and use is for each fragment because the functionality is same for all...
Possible Solutions I tried but no success
1 - Tried to get callingFragment Name but when I try to call public Method of fragment its not working
callingFragment.setText(text)
callingFragment should be 1 of any 6 fragments...
2 - OnActivityResult as well but no success...

Actually this can be implemented as the following:
class MainActivity : AppCompatActivity(){
private lateinit var currentFragment: CurrentFragment?
private val host by lazy {
supportFragmentManager.findFragmentById(R.id.main_nav_fragment) as NavHostFragment
}
override fun showQuantityDialog() {
currentFragment= host.childFragmentManager.primaryNavigationFragment as CurrentFragment
navController.navigate(ViewProductFragmentDirections.actionViewProductFragmentToQuantityDialogFragment())
}
// This is a callback from your dialog
override fun setQuantity(quantity: Int) {
viewProductFragment?. ... set what ever you want
viewProductFragment = null
}
}
So you save a reference to the current fragment before you open the dialog.

Related

Android Kotlin: Fragment not attached to a context

I am trying to use a TabLayout with different fragments and have started with AndroidStudio's automatically generated code for the tabbed layout. I have not changed how the placeholder fragment is created, displayed, handled etc.: The fragment is handled by a FragmentPagerAdapter, which is used by a ViewPaper, which in turn is used to setup the TabLayout.
The layout already included a FAB. Its onClick looks like this:
fab.setOnClickListener { view ->
val currentFragment: Fragment = sectionsPagerAdapter.getItem(viewPager.currentItem)
when (viewPager.currentItem) {
0 -> doSomething()
1 -> (currentFragment as PlaceholderFragment).fabOnClick()
else -> doSomethingElse()
}
}
Eventhough the above code makes sure that fabOnClick() is only called on the currently visible fragment, when I am trying to get a context using requireContext() in the PlaceholderFragment, java throws the following exception:
java.lang.IllegalStateException: Fragment PlaceholderFragment{660c58b} (08f94c5f-64b3-4a50-a1d4-2f3a6c7b491c)} not attached to a context.
For some reason, the context is available in e.g. onResume() in the PlaceholderFragment:
override fun onResume() {
super.onResume()
// Works fine
Toast.makeText(requireContext(), "placeholder", Toast.LENGTH_LONG).show()
}
fun fabOnClick() {
// Throws exception
Toast.makeText(requireContext(), "placeholder", Toast.LENGTH_SHORT).show()
}
I found this thread, Fragment not attached to a context, in which the solution was to commit a fragment transaction but all of this seems to be handled automatically in this case.
I just fixed an error somewhat similar to you in my code. With the same error you are getting.
The idea is that I was initializing a string variable (NOT in MainActivity) with getResources().
However, the variable was not being initialized with context.getResources()
I was able to initialize this variable inside MainActivity and make it static so that I could just copy the value into the variable NOT inside MainActivity.
So, I think you should search for any variables that you are initializing with getResources and see if you are using a context or not.

Fragment already added exception with singleton dialog

I have a progress dialog which is a singleton because I want it to be shown only once if it gets called multiple times, and doesn't stack on itself. but somewhere in my code, it gets called simultaneously from two places and I get fragment already added exception.
I'm checking if the dialog is not added to activity then I call Dialog.show() but since the method gets called simultaneously from two places, before the first one is added to activity the other one is passed throw if statement and that causes the problem.
I want two thread-safe the function so that multiple threads cant call it simultaneously.
as you can see I've tried synchronizing it by #Synchronized annotation but it didn't work
class ProgressDialogFragment private constructor() : DialogFragment() {
companion object {
private var dialogInstance: DialogFragment? = null
#JvmStatic
#Synchronized
fun showDialog(fragmentManager: FragmentManager) {
if (dialogInstance == null) {
dialogInstance = ProgressDialogFragment().apply {
setStyle(STYLE_NORMAL, R.style.Dialog_FullScreen)
isCancelable = false
}
}
if (!dialogInstance!!.isAdded && fragmentManager.findFragmentByTag("progress_dialog") == null) {
dialogInstance!!.show(fragmentManager, "progress_dialog")
}
}
as said in the documentation, the DialogFragment#show()
Display the dialog, adding the fragment to the given FragmentManager. This is a convenience for explicitly creating a transaction, adding the fragment to it with the given tag, and committing it.
FragmentTransactions commits are async, to make sure it is committed before you move on just call executePendingTransactions() :
if (!dialogInstance!!.isAdded && fragmentManager.findFragmentByTag("progress_dialog") == null) {
dialogInstance!!.show(fragmentManager, "progress_dialog")
fragmentManager.executePendingTransactions()
}
As for the #Synchronized it's unnecessary in this situation because this function can only run in the UI Thread.

How to give back control from Fragment to Activity?

I have a simple CalculatorActivity with consecutively shown Fragments :
InputFragment //requests two input values for calclation
ResultFragment // show the result of the calculation
To keep it simple lets assume, I want to calculate the sum of two numbers.
I start the CalculatorActivity which immediately loads the InputFragment via:
fun replaceFragment(resId : Int, newFragment : Fragment){
this.supportFragmentManager?.beginTransaction()?.replace(resId, newFragment)?.addToBackStack(null)?.commit()
}
resId is the resource ID for the container to inflate the fragment in.
newFragment is the new Fragment to be inflated. Here in this first step I call in CalculatorActivity's onCreate():
replaceFragment(R.id.container, InputFragment.getInstance())
SO FAR this works fine.
But when I enter the required numbers in InputFragment, how can I forward the values from InputFragment back to the calling CalculatorActivity, so that it can continue and inflate the ResultFragment into the very same container to show the result of calculation?
You can always get the enclosing activity from any fragment (by using getActivity), then cast it to the proper type and use your own setValues method on it. Check out this: https://developer.android.com/training/basics/fragments/communicating
You can have the value as a variable in the activity and from the fragment do
CalculatorActivity myActivity = (CalculatorActivity)getActivity()
myActivity.setInputValues(fragmentInputValues)
myActivity.doSomethingWithTheValues()
If you are learning and this is just for fun, then feel free to do the direct cast to activity and access it's public methods. However, I would never do that in real world practice.
Best practice would be to use an interface implementation on the parent activity.
example:
INTERFACE CLASS
interface IBaseActivityCallback {
fun onInputValuesReady(myObjectOfValues: MyObject)
}
ACTIVITY CLASS
class CalculatorActivity : AppCompatActivity(), IBaseActivityCallback {
override fun onInputValuesReady(myObjectOfValues: MyObject){
//handle inflate or usage of values
}
}
FRAGMENT CLASS
lateinit var baseActivityCallback: IBaseActivityCallback
override fun onAttach(context: Context) {
super.onAttach(context)
//NOTE* wrap in try/catch if context can be something else
baseActivityCallback = context as IBaseActivityCallback
}
fun someButtonClicked(){
baseActivityCallback.onInputValuesReady(myObjectToPass)
}
I figured I don't need to explain creating an object to hold variables or buttons that were clicked etc.. so I just kept that part generic for you.

Android ViewModel recreated on screen rotation

I found a case when architecture components ViewModel isn't retained - in short it goes as follows:
Activity is started and ViewModel instance is created
Activity is put to background
Device screen is rotated
Activity is put back to foreground
ViewModel's onCleared method is called and new object is created
Is it normal behavior of Android that my ViewModel instance is getting destroyed in this case? If so, is there any recommended solution of keeping its state?
One way I can think of is saving it once onCleared is called, however, it would also persist the state whenever activity is actually finishing. Another way could be making use of onRestoreInstanceState but it's fired on every screen rotation (not only if the app is in background).
Any silver bullet to handle such case?
Yes #tomwyr, this was a bug from an android framework. Bug details
The fix is available in 28.0.0-alpha3 and AndroidX 1.0.0-alpha3
But if you don't want to update to above versions now itself, Then you can solve like this (I know this is a bad solution but I didn't see any other good way)
In your activity override onDestroy method and save all the required fields to local variables before calling super.onDestroy. Now call super.onDestroy then Initialize your ViewModel again and assign the required fields back to your new instance of ViewModel
about isFinishing
Below code is in Kotlin:
override fun onDestroy() {
val oldViewModel = obtainViewModel()
if (!isFinishing) { //isFinishing will be false in case of orientation change
val requiredFieldValue = oldViewModel.getRequiredFieldValue()
super.onDestroy
val newViewModel = obtainViewModel()
if (newViewModel != oldViewModel) { //View Model has been destroyed
newViewModel.setRequiredFieldValue(requiredFieldValue)
}
} else {
super.onDestroy
}
}
private fun obtainViewModel(): SampleViewModel {
return ViewModelProviders.of(this).get(SampleViewModel::class.java)
}
AFAIK, ViewModel's only purpose is to survive and keep the data (i.e. "save the state") while its owner goes through different lifecycle events. So you don't have to "save the state" yourself.
We can tell from this that it's "not normal behavior". onCleared() is only called after the activity is finished (and is not getting recreated again).
Are you creating the ViewModel using the ViewModelProvider, or are you creating the instance using the constructor?
In your activity, you should have something like:
// in onCreate() - for example - of your activity
model = ViewModelProviders.of(this).get(MyViewModel.class);
// then use it anywhere in the activity like so
model.someAsyncMethod().observe(this, arg -> {
// do sth...
});
By doing this, you should get the expected effect.
For others that may not be helped by previous answers like me, the problem could be that you haven't set up your ViewModelProvider properly with a factory.
After digging around I solved my similiar problem by adding the following method to my Activities:
protected final <T extends ViewModel> T obtainViewModel(#NonNull AppCompatActivity activity, #NonNull Class<T> modelClass) {
ViewModelProvider.AndroidViewModelFactory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(activity.getApplication());
return new ViewModelProvider(activity, factory).get(modelClass);
}
And then I did this in my Fragments:
protected final <T extends ViewModel> T obtainFragmentViewModel(#NonNull FragmentActivity fragment, #NonNull Class<T> modelClass) {
ViewModelProvider.AndroidViewModelFactory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(fragment.getApplication());
return new ViewModelProvider(fragment, factory).get(modelClass);
}
I already had some abstract super classes for menu purposes so I hid the methods away there so I don't have to repeat it in every activity. That's why they are protected. I believe they could be private if you put them in every activity or fragment that you need them in.
To be as clear as possible I would then call the methods to assign my view model in onCreate() in my activity and it would look something like this
private MyViewModel myViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myViewModel = obtainViewModel(this, MyViewModel.class);
}
or in fragment
private MyViewModel myViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getActivity() != null) {
myViewModel = obtainFragmentViewModel(getActivity(), MyViewModel.class);
}
}
Change support library/compileSDK/targetSDK to 28.
I had similar issue with multi-window. When switching to split screen, my viewModel is recreated. Support library 28 fixed my problem. (My lifecycle version is 1.1.1)

Communicating between a fragment and an activity - best practices

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'

Categories

Resources