Which Fragment Manager is needed for TimePickerDialogue Fragment kotlin - android

I am trying to implement a simple TimePickerDialogue fragment, which displays when a button in pressed in a layout which is also a fragment. The Android developer guide shows this in Java:
public void showTimePickerDialog(View v) {
DialogFragment newFragment = new TimePickerFragment();
newFragment.show(getSupportFragmentManager(), "timePicker");
}
and says:
The show() method requires an instance of FragmentManager and a unique name for the fragment.
When I converted this to Kotlin there is no getSupportFragmentManager method offered. What should I use instead?
class AlertsFragment : Fragment() {
fun showTimePickerDialog(v: View) {
val newFragment = TimePickerFragment()
newFragment.show(FragmentManager(), "timePicker") // WHAT FRAGMENTMGR???
}
}
I am importing android.support.v4.app.Fragment
My MainActivity will display the TimePickerDialogue fragment as well as the Fragment that has a button to open the TimePickerDialogue. MainActivity has a tablayout using fragments.
Does anything need to be changed in MainActivity to make the TimePickerDialogue show() function work in the AlertsFragment?
class MainActivity : FragmentActivity(){
private lateinit var pagerAdapter: TabPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pagerAdapter = TabPagerAdapter(supportFragmentManager)
pagerAdapter.addFragments(WorkoutGridFragment(), "Workouts")
pagerAdapter.addFragments(AlertsFragment(), "Reminders")
pagerAdapter.addFragments(AboutFragment(), "About")
// customViewPager is the viewpager created in the activity_main xml layout
customViewPager.adapter = pagerAdapter
// set up the viewpager with the tablayout
customTabLayout.setupWithViewPager(customViewPager)
}
}

Fragments don't have a SupportFragmentManager field or getter. This applies whether you write it in Kotlin or Java.
Activities, however, do. So call the activity and get the supportFragmentManager:
fun showTimePickerDialog(v: View) {
val newFragment = TimePickerFragment()
newFragment.show(activity.supportFragmentManager, "timePicker")
}
Also, if this is what you were reading in the docs, you'll see this:
Also make sure that your activity that displays the time picker extends FragmentActivity instead of the standard Activity class. (emphasis mine)
Which implies the showTimePickerDialog method is defined in an Activity. And don't get this wrong, I'm not saying you have to define it in an activity, but since that's what they do in the docs, they can call the SupportFragmentManager directly. But if you call it from a fragment or anywhere outside an activity, you need an activity instance.

Related

should I use newInstance function somewhere?

I'm new in kotlin and android development.
I was following a tutorial, in that tutorial in a fragment class, companion object, defined a method named newInstance() that returned a fragment, the method was never used.
class myFragment : Fragment(){
companion object {
fun newInstance(foo:Int): myFragment {
val fragment = myFragment()
val args = Bundle()
args.putString("foo", foo)
fragment.arguments = args
return fragment
}
}
}
Is that okay?
Is that going to call it automatically or should I call it somewhere?
(sorry if the explanation isn't good)
It not gonna be used automatically, it is just one of the ways to create fragments.
Basically you need to call this function in the place where you wish to add/ replace this fragment into it's container with the help of FragmentManager
You need to use supportFragment manager in your activity to replace the fragment
val transition = supportFragmentManager.beginTransaction()
transition.addToBackStack("Your_fragment_unique_tag")
transition.replace(containerViewId, fragment).commit()
containerViewId will be FrameLayout id in your activity which is the container for replacing fragment i.e R.id.mainContainer

Can an Activity access a Fragment to know if a button has been pressed?

The Objective: I'm trying to make a notepad application. What my app does is, a button is pressed to create a new note. This pops up a fragment in which the user types his note. Within the same fragment, I have another button that signifies when the user is done typing.
Question 1: Is there a way by which pressing the other button in the Fragment could trigger a method in my Activity?
Question 2: Would this cause the app to become too bloated? Should I keep the button within my activity itself?
Thank you for your help.
Question 1: Is there a way by which pressing the other button in the Fragment could trigger a method in my Activity?
Sure, the simplest way to do it is:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = MyFragmentBinding.bind(view) // viewBinding enabled
binding.myButton.setOnClickListener {
(requireActivity() as MyActivity).doSomething() // <--
}
}
However, if this Fragment can be used in different Activity instances, then it should expose a Listener with which it exposes its potential events, and doesn't need to know the actual Activity instance it is talking to.
interface ActionHandler {
fun onMyButtonClicked()
}
lateinit var actionHandler: ActionHandler
override fun onAttach(context: Context) {
super.onAttach(context)
actionHandler = context as ActionHandler
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = MyFragmentBinding.bind(view) // viewBinding enabled
binding.myButton.setOnClickListener {
actionHandler.onMyButtonClicked()
}
}
This way, your Fragment will always have a listener to talk to even after config changes / process death, which seems to not be the case for most other answers here.
Question 2: Would this cause the app to become too bloated? Should I keep the button within my activity itself?
This depends on whether the button actually belongs in the Activity, though it probably doesn't. Most modern apps are written as single-Activity anyway, and unless the view is shared among all screens, it's put inside a Fragment, possibly maybe even using <include tags from a common layout resource.
There is an easy way of doing this as your fragments have access to activity (Kotlin) | getActivity() (Java) and by casting it you can use it.
But this is not the proper way of doing this because it affects the modularity of fragments.
The proper way of doing this:
Your activity wants to listen to Fragments events without any overhead:
In Fragment
class MyFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is MyFragment.Listener) {
listener = context
} else {
throw ClassCastException(context.toString() + " You need to implement MyFragment.Listener")
}
}
interface Listener {
fun onSomethingHappened()
}
private var listener: MyFragment.Listener? = null
fun aMethodInsideFragmentThatHandlesButtonEvents() {
listener?.onSomethingHappened()
}
}
And in your activity:
class MyActivity : AppCompatActivity(), MyFragment.Listener {
override void onSomethingHappened() {
// do your work here
}
...
}
For triggering a method on click of a button in fragment, there are number of ways to achieve this. Try this.
If (getActivity() instanceof MainActivity){
//Getting instance of your activity
MainActivity instance = ((MainActivity)getActivity());
//Using the instance calling the method in your activity
instance.methodName();
}
Use the above code in your fragment on button click.
Another way is using Interface, calling its abstract methods in fragment and overriding it MainActivity; on button click those methods will be called.
Or you can also try using RxEventBus. You can publish it in the fragment and listen in the MainActivity.
Hope this resolves your issue.
Just make your activity implement View.OnClickListener and on your fragment set your activity as onClickListener of your button.
your fragment:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
myButton.setOnclickListener((MyActivity)getActivity));
}

Get Data in Fragment through Retrofit

In this problem I want to get Data from My API
In which I get Data from Retrofit
I want to show data in RecyclerView in Fragment Tabs but how can I send data from activity to Fragment
This is all I have tried
Retrofit call which provide me ArrayList of my posts
getMainApp().swiftAPI.getPosts().enqueue(object : Callback<ArrayList<Post>>{
override fun onFailure(call: Call<ArrayList<Post>>?, t: Throwable?) {
Toast.makeText(this#DashboardActivity, t?.message, Toast.LENGTH_SHORT)
}
override fun onResponse(call: Call<ArrayList<Post>>?, response: Response<ArrayList<Post>>?) {
if (response?.isSuccessful!!){
}
}
PagesFragment
val rootView = inflater.inflate(R.layout.fragment_page, container, false)
val video_recyclerview = rootView.findViewById(R.id.pages_rcv) as RecyclerView // Add this
video_recyclerview.layoutManager = LinearLayoutManager(activity)
video_recyclerview.adapter = PagesAdapter()
return rootView
I want to Know if there is any way possible to send ArrayList to fragment cause my data is in ArrayList
You can define an interface in your activity and let the fragment implement the interface. You can follow this example on my github: ActivityToFragmentCommunication
Basically, in your activity define:
public interface DataLoadedListener {
public void onDataLoaded(ArrayList<Post> posts);
}
Then, make your fragment implement the interface like below:
public class ExampleFragment extends Fragment implements MainActivity.DataLoadedListener {
// your fragment code
}
Finally in the onCreate() method of your activity:
// Create new fragment and transaction
mExampleFragment = new ExampleFragment();
// setting mExampleFragment as data load listener
mDataLoadedListener = (DataLoadedListener) mExampleFragment;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.flContainer, mExampleFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
// load data after click
btLoadData.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
loadData();
// notify attached fragment
mDataLoadedListener.onDataLoaded(myStrings);
}
});
First check currentFragment in your activity class. You will get the fragment reference object in your activity class . so the example will like below:
Suppose you have a fragment called DataFragment and you have a reference mDataFragment in your activity class. now when you get data in your activity class you will call ((DataFragment)mDataFragment).passData(yourDataList). Remember passData() is a public method in your fragment class. Then you can add data in adapter and call notifyDataSetChanged()
Since you are working on Android, I would recommend the ViewModel component which makes it really easy to communicate between a activity and it's fragments.
First add the package to your app
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
Then create a ViewModel class
public class MyViewModel extends ViewModel {
public MutableLiveData<ArrayList<Post>> posts = new MutableLiveData<ArrayList<Post>>();
}
Now in the fragment subscribe to it.
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.posts.observe(this, { posts ->
// Update the UI.
});
}
}
Then set the value in your MainActivity as shown below and voila you have the data in your fragment. You can read more about it here
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.users.setValue(posts);
}
}

How can I pass data to the first Fragment whilst using the Navigation Architecture?

I'm trying to pass a bundle of object instances down from my main activity to the first fragment in a chain of other fragments using the NavHostFragment. I've tried all sorts but the bundle always seems to be null once it reaches the first fragment.
Here's how I'm initiating the NavHostFragment (frameContainer is a Frame Container element in my layout xml)
NavHostFragment navHost = NavHostFragment.create(R.navigation.claim_nav_graph);
getSupportFragmentManager().beginTransaction()
.replace(R.id.frameContainer, navHost)
.setPrimaryNavigationFragment(navHost)
.commit();
The documentation says there are 2 different .create functions, one of them you can pass a second arguments to as a bundle, but Android Studio doesn't allow me to use this version.
Does anyone have any ideas?
Thanks in advance!
It does seem to be a flaw with the NavHostFragment, passing data down to the first fragment does not seem to be possible, as the Bundle you can set as a second argument on the create function is overwritten along the way.
In the end I resolved this by building the bundle in the first fragment of the activity instead. I was able to access the activities intent properties using the below.
// Kotlin
activity.intent?.extras?.getBundle(KEY_BUNDLE_ID)
// Java
getActivity().getIntent().getBundleExtra(KEY_BUNDLE_ID)
This was enough of a workaround for me in this situation, but it would be great if it was possible
If you're using viewModels, you can do this:
your viewmodel:
class NiceViewModel: ViewModel() {
var dataYouNeedToPass = "initialValue"
}
your activity:
class MainActivity : AppCompatActivity() {
val niceViewModel: NiceViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
niceViewModel.dataYouNeedToPass = "data You Need To Pass"
}
}
your fragment:
class YourFragment : Fragment() {
private lateinit var niceViewModel: NiceViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
niceViewModel = (activity as MainActivity).niceViewModel
niceViewModel.dataYouNeedToPass //do whatever you need to do with this
}
}

Fragment Backstack Toolbar title

Is there an efficient way to automatically set the toolbar's title when adding/replacing fragments as well as popping fragments from the backstack?
I have implemented this abstract method in my BaseFragment class:
abstract fun header() : String
override fun onResume() {
super.onResume()
(activity as SSBaseActivity).header.text = header()
}
and I modify the header in each Fragment that inherits from my BaseFragment class and displays the value in onResume but I noticed that when I press back, the last title set isn't being replaced from the fragment currently in the stack.
You could do this by using an OnBackStackChangedListener in your Activity:
supportFragmentManager.addOnBackStackChangedListener {
val fragment = supportFragmentManager.findFragmentById(R.id.yourFragmentFrame)
if (fragment is BaseFragment) {
header.text = fragment.header()
}
}

Categories

Resources