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);
}
}
Related
I wondered if it's possible to pass a String data which has declared in Activity class and pass the String data to ViewModel class then pass the data to Fragment class.
ViewModel Class
class TimeTableViewModel extends ViewModel {
private MutableLiveData<String> start_time_str = new MutableLiveData<>();
void send_StartTime(String start_Time){
start_time_str.setValue(start_Time);
}
LiveData<String> get_StartTime(){
return start_time_str;
}}
In ViewModel Class, I have MutableLiveData<String> start_time_str and it has been initialized as new MutableLiveData<>();
I would like to use void send_StartTime(String start_Time) function in Activity class to set value of argument String start_Time and call the start_time_str in Fragment class.
Activity Class
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case android.R.id.home:
finish();
break;
case R.id.add_schedule_save:
String start_time_str = startTime.getText().toString();
Intent intent_restart0 = new Intent(TimeTable_Add_New_Schedule.this, MainActivity.class);
startActivity(intent_restart0);
TimeTableViewModel timeTableViewModel = new TimeTableViewModel();
timeTableViewModel.send_StartTime(start_time_str);
Toast.makeText(this,""+start_time_str,Toast.LENGTH_LONG).show();
break;
}
return super.onOptionsItemSelected(item);
}
Fragment Class
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TimeTableViewModel timeTableViewModel = new TimeTableViewModel();
timeTableViewModel.get_StartTime().observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(String s) {
mon_textView_11.setText(s);
}
});
}
In the Fragment class I call get_StartTime() function to get start_time_str and set the String value to my TextView. I think the start_time_str has been successfully set by function of timeTableViewModel.send_StartTime(start_time_str); in the Activity Class because of Toast.maketext is worked like a charm. However the TextView is not shown anything. I have tested Text Color is not white so that if the string value is correctly called, it should be appear on screen. If you have any suggestions, I would love to hear your advice.
Thank you very much.
It really depends on how do you create your ViewModel instance. Now you are creating ViewModel by its constructor, but that is not a proper way. You should use ViewModelProvider or extension methods that were created by Google team.
If you go with ViewModelProvider you should do it like this:
TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);
It is important to pass the correct context to ViewModelProvider constructor call. If you are in fragment and you will just use getContext() instead of getActivity(), you will not get the same instance as it was created in Activity. You will create a new instance of ViewModel, that will be scoped only inside of fragment lifecycle. So it is important to use in both parts activity context to get the same instance.
Activity part:
TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);
Fragment part:
TimeTableViewModel viewModel = new ViewModelProvider(getActivity()).get(TimeTableViewModel.class);
Is important that your fragment is located inside the same activity that is using this ViewModel.
But guys at Google has make it easier for us with some extension methods. But as far as I know, they are working only in Kotlin classes. So if you have Kotlin code, you can declare your ViewModel simply like this:
private val quizViewModel: TimeTableViewModel by activityViewModels()
For Fragment scoped ViewModel you need to write something like this:
private val quizViewModel: TimeTableViewModel by viewModels()
But you have to add Kotlin ktx dependency to your project build.gradle file. For example like this:
implementation 'androidx.fragment:fragment-ktx:1.1.0'
If you are using Android Architecture and want to share activityViewModel in your fragments.
To get viewModels in fragment use below code:
private val fragmentViewModel: Fragment1ViewModel by viewModels()
private val activityViewModel: MainActivityViewModel by activityViewModels()
and in MainActivity use below code:
private val activityViewModel: MainActivityViewModel by viewModels()
I'm using kotlin language
I'm used same as tutorial but not working
Here's example codes how to declare my ViewModel
add dependencies in build.gradle:
implementation 'androidx.fragment:fragment-ktx:1.4.1'
example of Activity class
class HomepageActivity : AppCompatActivity() {
private lateinit var sharedViewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomepageBinding.inflate(layoutInflater)
setContentView(binding.root)
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
sharedViewModel.isMenuOpen.observe(this, {
onMenuOpen(it)
})
}
example in Fragment class
class HomeFragment : Fragment() {
private lateinit var sharedViewModel: SharedViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel = activity!!.run{
ViewModelProvider(this).get(SharedViewModel::class.java)
}
}
I found the simplest fix to this problem, instead of giving the owner as "this" change it to getActivity().
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mvm == null) {
mvm = new ViewModelProvider(getActivity()).get(CounterFgViewModel.class);
}
}
I just started using the architecture component of Android in my app, and I have a use case when:
A fragment is an observer of ViewModel's LiveData (which comes from a Room request)
This fragment starts an activity at some point
The activity needs to use the same data (as LiveData in the fragment) and update it
The user then returns to the fragment
I tried using the same ViewModel class and adding the fragment and activity as observers, thinking that somehow updating from the activity will affect the fragment when returning to it, but it doesn't work.
The two ViewModels seem independent.
How can I force refresh the fragment data when returning from the activity (in onActivityResult for example)?
Or is it possible to share the same ViewModel between the fragment and activity? (though I doubt it since the ViewModel is linked to the lifecycle of the observer)
Fragment
public void onAttach(#NonNull Context context) {
...
mViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
mViewModel.getData().observe(this, new Observer<List<Data>>() {
#Override
public void onChanged(List<Data> data) {
mAdapter.setData(data);
}
});
}
// in an onClick method
startActivity(intent); // go to the Activity
Activity
protected void onCreate(Bundle savedInstanceState) {
...
mViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
mViewModel.getData().observe(this, new Observer<List<Data>>() {
#Override
public void onChanged(List<Data> data) {
mPagerAdapter.setData(data);
}
});
}
Any help will be appreciated!
EDIT:
View Model
public class MyViewModel extends AndroidViewModel {
private Dao dao;
private ExecutorService executorService;
public MyViewModel (#NonNull Application application) {
super(application);
dao = AppDatabase.getInstance(application).getDao();
executorService = Executors.newSingleThreadExecutor();
}
public LiveData<Data> getData() {
return dao.getAllData();
}
// other methods to update and delete with executorService
}
I managed to get the result you want by seting a common lifecycle owner for the viewmodel, and using the same viewModel at the different fragments.
Inside my fragment, i get the view model like this:
INSIDE FRAGMENT:
var userViewModel = activity?.run{ViewModelProviders.of(this, SharedUserViewModel.MainActivityViewModelFactory(applicationContext))[SharedUserViewModel::class.java]}
Did you see i use the activity as the "ViewModelProviders.of" parameter?
This way, the view model have the same owner, its working alright for me.
Yes you can share ViewMode between activity and Fragment Using SharedViewModel.
let say for example I have 1 Activity that contains 5 Fragments and those Fragments presents a 1 flow of payment process so each Fragment depends on the previous Fragment by passing data of what the user chooses
I'm planning to make 1 ViewModel in the Activity that handles the data between fragments but I've read that it is a bad idea to expose MutableLiveData outside of the view model. so I can't say viewModel.setdata(example) in the Activity the best solution was is to use navigation component with safe args and create a ViewModel for each Fragment and create ViewModelFactory for each fragment too.
but this will make me write too many classes.
is there an optimal way to pass data between views using 1 ViewModel without violating the MVVM architecture rules?
Yes, this is good decision to use ViewModel to share data between fragments. Look this
SharedViewModel
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;
}
}
MasterFragment
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
DetailFragment
public class DetailFragment extends Fragment {
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}
Here is the black magic:
val viewModel: MyViewModel by activityViewModels()
I have MainActivity which contains TabLayout with tabs: each tab is a fragment and each one has a RecyclerView. When I tap FAB in Main Activity, NewReminderActivity is opened.
I use Architecture Components: Entity(Reminder), DAO, Room, ViewModel, LiveData and Repository.
The question is:
Which methods should I use to deliver a new created reminder item into the fragment (which contains as mentioned above a RecyclerView?
I have some ideas, but could you help me please and give me a right direction for implementing:
1) I guess, I should deliver data to MainActivity, then from MainActivity to the fragment and use ViewModel as mentioned in https://developer.android.com/topic/libraries/architecture/viewmodel.html#sharing , am I right?
2) I guess I should use setResult() in NewReminderActivity, am I right?
If you are using Room, there is no reason for you to use setResult to transfer new item to any of these previous Fragments/Activities, because Room manages invalidation automatically.
#Dao
public interface MyDao {
#Query("SELECT * FROM ITEM")
LiveData<List<Item>> getItemsWithChanges();
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertItem(Item item);
}
Then
public class MyViewModel extends ViewModel {
private final LiveData<List<Item>> items;
public LiveData<List<Item>> getItems() {
return items;
}
public MyViewModel(MyDao myDao) {
items = myDao.getItemsWithChanges();
}
}
Then
public class MyFragment extends Fragment {
MyViewModel myViewModel;
#Override
protected void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myViewModel = ViewModelProviders.of(getActivity(), viewModelFactory).get(MyViewModel.class);
myViewModel.getItems().observe(getViewLifecycleOwner(), (items) -> {
if(items != null) {
adapter.submitList(items);
}
});
}
}
In which case all you need to do in your second Activity is insert the new item, then finish out:
// imagine this is on background thread
myDao.insertItem(item);
runOnUiThread(() -> {
finish();
});
And all your RecyclerViews will update with the new item (if they are part of the results as the condition matches it).
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.