I have a arraylist in my fragment class. I pass the array to the adapter. After the adapter, I pass the array to another fragment. Now in the current fragment, I change a value of the array object. I want this change to change in the first fragment array. How is this possible?
look at the example code:
public class Fragment1 extends Fragment {
ArrayList<Model> array = new ArrayList<>();
// send array to Adapter
}
public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ArrayList<Model> array = new ArrayList<>();
array.addAll(getArrayFromFragment1); // use this.array = getArrayFromFragment1 // I don't want to use (this)
// send array to Fragment2
}
public class Fragment2 extends Fragment {
ArrayList<Model> array = new ArrayList<>();
array.addAll(getArrayFromAdapter); // use this.array = getArrayFromAdapter // I don't want to use (this)
// NOW! update object value from array --> array.get(10).setUserPhoneNumber(987654321)
// i need update automatically Fragment1 array object 10 UserPhoneNumber
}
In instagram app:
fragment 1 --> gragment 2 --> explore tab --> fragment 3 like one post --> back to fragment 1 same post liked. how do this relations?
I would use a separate class with a static ArrayList so you only need to use a single source for the data and can read it from any class/fragment.
public class StaticUtils {
public static ArrayList<Model> models = new ArrayList<>();
//or make it private and add static getter/setter methods here
}
public class Fragment2 extends Fragment {
StaticUtils.models.get(10).setUserPhoneNumber(987654321)
}
Related
I have a Parent Fragment, called WaterFountainFragment, that has a nested Fragment progamatically inflated inside a FrameLayout that is dependant of an user's choice in a RadioGroup. If the user chooses one option, it inflates one Child and, when chosen the other option, it inflates another child (in the name of being the most concise as possible, I'll only list one of those child fragments, since the problem happens in both of them).
The user enters the data he wants and save it on the database, using LiveData and Room dependencies to do so. However, if the user wants to go back and check which data was saved in an specific entry, then we start to face trouble.
The problem is, the saved data is shown in the Parent Fragment but, unfortunately, it does NOT load inside the child fragment nested on it.
First, let me show the parent class in which I think everything is working normally:
WaterFountainFragment Class
public class WaterFountainFragment extends Fragment {
(...)
#Override
public void onStart() {
super.onStart();
//modelEntry is a ViewModel, which class ViewModelEntry will be shown later
modelEntry = new ViewModelEntry(requireActivity().getApplication());
//The ID of an entry is sent to this fragment through a bundle (via setArgument());
if (waterFountainBundle.getInt(FOUNTAIN_ID) > 0) {
//getOneWaterFountain is a method inside modelEntry to obtain an entry from the database through
//LiveData and Room
modelEntry.getOneWaterFountain(waterFountainBundle.getInt(FOUNTAIN_ID))
.observe(getViewLifecycleOwner(), this::loadFountainInfo);
}
}
//this is a RadioGroup Listener where it inflates the specific child fragment inside a FrameLayout
public void typeFountainListener(RadioGroup group, int checkedID) {
int index = getCheckedFountainType(group);
switch (index) {
case 0:
getChildFragmentManager().beginTransaction()
.replace(R.id.water_fountain_info, new WaterFountainSpoutFragment()).commit();
break;
case 1:
getChildFragmentManager().beginTransaction()
.replace(R.id.water_fountain_info, new WaterFountainOtherFragment()).commit();
break;
default:
break;
}
}
//method to fill all fields inside this fragment
private void loadFountainInfo(WaterFountainEntry waterFountain) {
fountainLocationValue.setText(waterFountain.getFountainLocation());
typeWaterFountain
.check(typeWaterFountain.getChildAt(waterFountain.getTypeWaterFountain()).getId());
if (waterFountain.getFountainTypeObs() != null)
fountainTypeObsValue.setText(waterFountain.getFountainTypeObs());
//At this point the RadioGroupListener is active already and, when checking a RadioButton, it will
//inflate the child and send a fragmentResult to this child fragment
getChildFragmentManager()
.setFragmentResult(InspectionActivity.LOAD_CHILD_DATA, waterFountainBundle);
}
}
Now we have one of the 2 nested child fragments that are inflated:
WaterFountainSpoutFragment Class
public class WaterFountainSpoutFragment extends Fragment {
#Override
public void onStart() {
super.onStart();
//the ViewModel is instantiated here with the same name and method as the
//one instantiated in the ParentFragment
modelEntry = new ViewModelEntry(requireActivity().getApplication());
//FragmentResultListener is active and it does recieve the signal from the parent Fragment.
getParentFragmentManager()
.setFragmentResultListener(InspectionActivity.LOAD_CHILD_DATA, this, (key, bundle) ->
//However, inside this resultListener, this observer seems to not be triggered
modelEntry.getOneWaterFountain(bundle.getInt(WaterFountainFragment.FOUNTAIN_ID))
.observe(getViewLifecycleOwner(), this::loadSpoutFountainData)
);
}
}
Here we have the ViewModel, the repository and the Dao classes/interfaces that were used on those 2 classes above
ViewModelEntry class
public class ViewModelEntry extends AndroidViewModel {
public ViewModelEntry(#NonNull Application application) {
super(application);
repository = new ReportRepository(application);
allEntries = repository.getAllSchoolEntries();
}
public LiveData<WaterFountainEntry> getOneWaterFountain(int waterFountainID) {
return repository.getOneWaterFountain(waterFountainID);
}
}
ReportRepository Class
public class ReportRepository {
private ReportDatabase db;
private final WaterFountainDao waterFountainDao;
public ReportRepository(Application application) {
waterFountainDao = db.waterFountainDao();
}
public LiveData<WaterFountainEntry> getOneWaterFountain(int waterFountainID) {
return waterFountainDao.getOneWaterFountain(waterFountainID);
}
}
WaterFountainDao Interface
#Dao
public interface WaterFountainDao {
#Query("SELECT * FROM WaterFountainEntry WHERE waterFountainID == :waterFountain")
LiveData<WaterFountainEntry> getOneWaterFountain(int waterFountain);
}
What I know/tested so far
Using a Toast, I confirmed that getParentFragmentManager().setFragmentResultListener() is being called. Even more so, the bundle recieve the right data.
If I use the modelEntry.getOneWaterFountain(bundle.getInt(WaterFountainFragment.FOUNTAIN_ID)).observe(getViewLifecycleOwner(), this::loadSpoutFountainData) outside the resultListener, it does load the correct data into the child Fragment fields.
The data entered by the user IS being stored in the database. I confirmed that using the Database Inspector, so it is not a case where "the data is not being stored properly, hence why is not loading".
I use the same method in other Parent/Child Fragments, using the same format of resultListener and it DOES load the data.
Using another method for creating this ViewModel, like modelEntry = new ViewModelProvider.AndroidViewModelFactory(requireActivity().getApplication()).create(ViewModelEntry.class); in both parent and child fragments results in the same problem
What I SUPPOSE it might be the case
I have wondered that it might be a situation where I am choosing the wrong LyfecycleOwner but I don't know if that is the case, mainly because of what I put on item 4.
Any help would be greatly appreciated.
My Adapter requires a context in order to apply resources to views, therefore when instantiating it, I might do the following within my Activity:
myAdapter = new MyAdapter(this);
As my adapter also needs data from an activity, I might do this:
myAdapter = new MyAdapter(myItemsArrayList,this);
As my adapter might also need to know which items in the ArrayList are selected, I might pass it that list too:
myAdapter = new MyAdapter(myArrayItemsList,mySelectedItemsArrayList,this);
And as there may be other states (e.g. whether to display photos in a list of people, the constructor call is starting to get quite lengthy:
myAdapter = new MyAdapter(myArrayItemsList,mySelectedItemsArrayList,myPreference1,myPreference2,this);
Given that the only place this adapter will be used is from a particular activity, how bad would it be to just make those attributes in the activity public, so that I can access them via the activity that has been passed (e.g myActivity.myArrayItemsList)?
Many thanks in advance for any advice!
Given that the only place this adapter will be used is from a particular activity, how bad would it be to just make those attributes in the activity public, so that I can access them via the activity that has been passed (e.g myActivity.myArrayItemsList)?
That's a bad code and bad behavior. You're code will be tightly coupled. And usually, you will borrow the same behavior to your next project.
Instead of passing each state to your constructor, you can simplify it by passing a State object to your adapter. Create the State class something like this:
public class State {
List<String> selectedItems;
boolean displayPeople;
}
then you can create a simple constructor like this:
State state = new State();
state.selectedItems = mSelectedItems;
state.displayPeople = true;
myAdapter = new MyAdapter(items, state, this);
So, whenever you need to update a new state, you just need to add it the State class and update the Adapter according to it.
Considering you are using the Item object for myArrayItemsList.
So your list should look like this:
ArrayList<Item> myArrayItemsList = new ArrayList();
and then you want to add the selected items in the list you could add a boolean to the Item object ex:
public class Item {
private String itemName;
private boolean selected = false;
public Item(){}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName= itemName;
}
public boolean isSelected () {
return selected;
}
public void setSelected(boolean selected) {
this.selected= selected;
}
}
and just check your item list in the adapter if the item is selected.
So your adapter would only pass two parameters:
MyAdapter myAdapter = new MyAdapter(myArrayItemsList, this);
But then again you want to pass only one parameter in adapter, you can set your ArrayList to static
public static ArrayList<Item> myArrayItemsList = new ArrayList();
and pass only this your adapter
MyAdapter myAdapter = new MyAdapter(this);
used the static ArrayList in your adapter but it is not advisable using those static data because the data could be Garbage Collected in the memory.
public void PrintRecordToResultTA() {
int i = 0;
Log.d("data","\nCodec: " + avlRecordCollection.getCodecID());
Log.d("data","\nRecord Count: " + avlRecordCollection.getRecordCount());
I have used log to see if my program woks, but now I need to display this data on UI thread in MainActivity, this method was used to display data in java program, I was thinking should I recreate this class as Activity to reach data from another Activity to Main?
EDIT:
I have created ArrayList of AVL Records
public List<AVLRecord> avlRecords = new ArrayList<>();
public AVLRecordCollection CreateCollection() { // private
return new AVLRecordCollection(codec, recordC, avlRecords);
}
And method to Create Records, which get all data I need to display... And I use avlRecord.add(AVLRecord) to pass all data.
public void CreateRecord() {
AVLRecord AVLRecord;
RecordHeader recordHeader = GetRecord_Data();
RecordGPS_Element recordGPS_element = GetRecord_GPS();
RecordIO_Element recordIOElement = GetRecord_IO();
AVLRecord = new AVLRecord(recordHeader, recordGPS_element, recordIOElement);
avlRecords.add(AVLRecord);
}
Can someone give me an example how can I display data in MainActivity
If you are calling this method from MainActivity then you can use return to send back data to Activity class or can call a method to display with your data arrayList in Activity class.
To use return change your method return type from void to arraList of your data type.
public ArrayList<DataType> PrintRecordToResultTA() {
ArrayList<DataType> avlRecordCollectionArray = new ArrayList<DataType>;
// add data into avlRecordCollectionArray arraylist
return avlRecordCollectionArray;
}
And in Activity class change method calling,
ArrayList<DataType> avlRecordCollectionArray = ClassName.PrintRecordToResultTA();
Then you will have arraylist of data in Activity class. Display data.
I have three fragments inside an activity. Three text views inside this activities. I have three API calls inside each of the fragments. I need the count of ArrayList created in the three fragments. I tried to make the API call in activity and tried to find out the ArrayList from there and passing it to fragment. But I don't know how to pass this type of ArrayList (Arraylist array) to fragment. I only need three integer values in the activity. So the questions are,
1) Please give me a way to pass value from three fragments to activity by actually loading the view of one fragment and loading the rest of the fragment without views.
2) Or give me a way to pass an ArrayList of model-class created at the activity to three fragments.
1 - Get the size of Array from fragment:
In this case you need to get the instance of the added fragment, and retrieve the values you need from it's global variables.
In fragment:
public List<MyObject> myList;
In Activity:
int size;
MyFragment fragment = (MyFragment)getSupportFragmentManager().findFragmentByTag("myFragmentTag");
if(fragment != null && is fragment.isAdded())
size = fragment.myList.size();
PS: in the above case don't forget to add a TAG to the fragment when you add it
2 - Pass the Array to fragment:
In this case, you need to make the Object Serializable, add it as an argument to the fragment that is about to be added, and then, when the fragment is added, within the fragment, retrieve the previously added Object
Make the Object Serializable and add it to the Arguments from Activity
In Activity:
public MySerializableObjectList myList;
Add the array to fragment in Activity:
MyFragment myFragment = new MyFragment();
fragmentManager = getSupportFragmentManager();
Bundle bundle = new Bundle();
bundle.putSerializable("myArrayTag", myList);
myFragment.setArguments(bundle);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_container, myFragment,"myFragmentTag").commit();
In Fragment:
public MySerializableObjectList myList;
if(getArguments != null)
myList = (MySerializableObjectList)getArguments().getSerializable("myArrayTag");
List<MySerializableObject> myListObject;
if(myList != null)
myListObject = myList.getMySerializableObjectList();
putParcelableArrayList and getParcelableArrayList trough Bundle in the Fragment arguments.
Okey, lets see. In my opinion the best way to do it is query the info in the activity and passing to the fragment, it means, the second way you proposse.
to do this follow this steps
1) Obtain the List in activity onCreate method.
2) In the fragment, Create a variable and a method that recieve the object from the activity.
public class FormFragment extends Fragment {
List<Object> data;
public FormFragment() {
// Required empty public constructor
}
public void initFragment(List<Object> data){
this.data = data
}
}
3) After create the fragment, use this method to pass the info to the fragment
public class Activity extends AppCompat{
List<Object> data = //Query your data here
/....
FormFragment fragment = new FormFragment()
fragment.init(data)
}
and that is all, you can pass the info this way and works smoothly, also, you can use your data from the activity as you bless.
So, I have Client class, which stores Category ArrayList (Client can have a couple of categories). Each category stores Expense ArrayList. So, every category can have a couple of expenses.
Here is the issue: I want to set adapter for one listView, in which we would print out all of the expenses. I know how to create adapter for one ArrayList, but if we have ArrayList inside ArrayList - then I am lost.
Any tips or tricks you might be able to share with me?
Use ExpandableListView with ExpandableListAdapter
You can use an adapter inside another adapter. I have used this approach in a dummy project. I put the following code as a starting point.
class Category{ List<Expense> expenseList; }
//this adapter will be used in Activity
class CategoryAdapter extends ArrayAdapter<Category>{
//your other adapter code here
ArrayAdapter<Expense> expenseAdtr =new ExpenseAdapter(context, category.getExpenseList());
expenseListView.setAdapter(expenseAdtr );
expenseListView.setOnClickListner(....);
}
//this adapter will be used inside CategoryAdapter
class ExpenseAdapter extends ArrayAdapter<Expense>{
//your adapter code here, should have no issue here
}
class MyActivity extends Activity{
//somewhere onCreate or .....
List<Category> categoryList;
ArrayAdapter<Category> categoryAdtr =new CategoryAdapter(context, categoryList);
expenseListView.setAdapter(categoryAdtr);
}