Getting values (EditTexts, Spinners, ToggleButtons) from multiple fragment instances? - java

I'm going to be having multiple instances of the same fragments, and I'm trying to get values from within these fragments. The problem I'm having is that, because these are instances of the same fragment/s, the EditTexts/Spinners/Toggle Buttons naturally have the same id's if there are several instances of them. How would I go about getting values from them?
If I use something like
EditText exampleEditText = (EditText) findViewById(R.id.exampleId);
exampleEditText.getText().toString()
I get the value of the first instance, and none of the others.

it depends on for how long you need the data to be there, i mean if u need it even after the app restarts, the best approach would be to create a database and second approach would be TinyDB.
Hope it helps

Just to make sense how to get data from multiple instances of same fragment.
To simulate your example. You,
Have a SampleFragment
Have three instances of SampleFragment
Get value of EditText from an instance
Sample fragment
public class SampleFragment extends Fragment {
private EditText mSampleEditText;
public String getSampleValue(){
String value;
if(mSampleEditText != null){
value = mSampleEditText.getText().toString();
}
return value;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSampleEditText= (EditText) getActivity().findViewById(R.id.editText_sample);
}
}
Sample activity
public class SampleActivity extends AppCompatActivity {
String mInstance1Tag = "instance1";
String mInstance2Tag = "instance2";
String mInstance3Tag = "instance3";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
// First instance
transaction.replace(R.id.fragment_container1, new SampleFragment(), mInstance1Tag);
// Second instance
transaction.replace(R.id.fragment_container2, new SampleFragment(), mInstance2Tag);
// Third instance
transaction.replace(R.id.fragment_container3, new SampleFragment(), mInstance3Tag);
transaction.commit();
}
private String getValueFromInstance(String instanceTag) {
SampleFragment fragment = (SampleFragment) getSupportFragmentManager()
.findFragmentByTag(instanceTag);
return fragment.getSampleValue();
}
}
EDIT
After your comment, the question is more clear and also you are right on the questions about tags like 'what would", "where you have".
I'm going to be having multiple instances of the same fragments,
Before start my post, i had supposed you'll have specified fragment instance count. So i set instance count to 3 in my example and then paired instances and tags. Because if you add fragment instances via unique tags, then you can get them.
And dont get stuck on their tags, to make simple i defined them as "instance1", "instance2"...

Related

Getting data from fragment without loading the view

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.

Getting a value from parent activity returns null

I have an activity and a fragment working with.
In the activity, i'm initializing the current user like this(manually):
User currentUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
currentUser = new User("email", "name", "secondName", "age");
\\.....}
the MainActivity contains a method like this :
public User getCurrentUser() {
return currentUser;
}
now, here is the problem :
in the child fragment i'm calling getCurrentUser from mainActivity like this :
User u = MainActivity.newInstance().getCurrentUser();
MainActivity.newInstance() - defined in mainActivity like this, returning a static instance of activity :
private static MainActivity mainActivityInstance = new MainActivity();
public static MainActivity newInstance() {
return mainActivityInstance;
}
the problem is that getCurrentUser returns a null object reference even though currentUser is initialized in mainActivity with valid data.
So, calling for example
u.getName();
will throw me a NullPointerException
How can i solve this? i feel like i'm freaking out
Thanks.
MainActivity.newInstance() - defined in mainActivity like this, returning a static instance of activity :
Never create an instance of an activity directly yourself.
the problem is that getCurrentUser returns a null object reference even though currentUser is initialized in mainActivity with valid data
Those activity instances are separate Java objects.
From a fragment, call getActivity() to retrieve the activity object that is hosting the fragment. This is covered in any good book or course on Android app development.
As many people said: "You can't instantiate an Activity in a Fragment".
Second: there are many options you can implement in order to get the User in a Fragment. Here are the common options:
1 - Pass the user as a parameter in the fragment:
public static FragmentChild newInstance(User user){
Bundle b = new Bundle();
b.putSerializable("USER_PARAM",user);
FragmentChild fragment = new FragmentChild();
fragment.putArguments(b);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
User user = getArguments().getSerializable("USER_PARAM")
}
public class User implements Serializable {}
2 - Use the method ((MainActivity)getActivity()).getCurrentUser(). It will work but if you want to do it in the right way you should use an interface instead of using the MainActivity directly.
Interface option:
public class MainActivity extends AppCompatActivity implements UserDeliver {
#Override
public User getUser(){
return user;
}
}
public class MyFragment extends Fragment {
private UserDeliver userDeliver;
private User user;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
user = userDeliver.getUser();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
userDeliver = (UserDeliver) context;
}
#Override
public void onDetach() {
super.onDetach();
userDeliver = null;
}
public interface UserDeliver {
User getUser();
}
}
First off- you can't create an Activity via new. It will compile, but not be properly initialized because only the framework can do that.
Secondly- never hold an activity in a static variable. This will leak memory. Lots of memory, because the entire view hierarchy has a reference from the Activity. You will cause OOM errors doing this.
U are generating a newInstance of the MainActivity everytime you do MainActivity.newInstance()
Try to retrieve your user with ((MainActivity)getActivity).getCurrentUser();
Or create the instance of your user in the fragment

Editing and saving an ArrayList<> that has been passed to another activity

I am making a frisbee logger and have an ArrayList of Team objects. Each Team has an ArrayList of Player objects. Everything is using Serializable properly to be sent using Intent.
In my main activity I am displaying the list of Team objects in a ListView and an option to add another Team (only a name is needed). Once a Team is selected I pass the object to another activity using Intent. On this second activity I have it display the list of Player objects and have fields to enter another player object into the passed list.
When I return to the main activity and go back to the add Player activity, what I have added is gone.
I cannot use static because there is obviously more than one Team object. I think passing back the changed ArrayList could work but that seems a little lame, time-consuming, and frustrating.
Is there a built-in way in Android Studio that does this or am I on my own?
Note: I am not using SQLite as suggested in the comments
There's not a whole lot to show on this but here it is I guess:
MainActivity.java
private static ArrayList<Team> listOfTeams = new ArrayList<>();
private static ArrayList<Game> listOfGames = new ArrayList<>();
private ListView gameList, teamList;
.....
teamList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Team t = (Team)teamList.getItemAtPosition(position);
viewTeam(t);
}
});
.....
//Item select in teamList. Start the TeamViewActivity
public void viewTeam(Team t)
{
Intent i = new Intent(this, TeamViewActivity.class);
i.putExtra("teamView",t);
startActivity(i);
}
TeamViewActivity.java
private Team team;
private ListView rosterList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_team_view);
rosterList = (ListView) findViewById(R.id.playerList);
Intent i = getIntent();
Bundle extras = i.getExtras();
if(extras!=null)
{
if(extras.get("teamView")!=null)
{
team = (Team) extras.get("teamView");
}
}
populateRosterList(team.getRoster());
}
public void addPlayerToRoster(View view)
{
String checkFirst = ((EditText) findViewById(R.id.firstText)).getText().toString();
String checkLast = ((EditText) findViewById(R.id.lastText)).getText().toString();
String checkNumber = ((EditText) findViewById(R.id.numberText)).getText().toString();
if(!checkNumber.equals(""))
{
team.addPlayer(checkFirst, checkLast, Integer.parseInt(checkNumber));
((EditText) findViewById(R.id.firstText)).setText("");
((EditText) findViewById(R.id.lastText)).setText("");
((EditText) findViewById(R.id.numberText)).setText("");
populateRosterList(team.getRoster());
}
}
public void returnToMain(View view)
{
Intent i = new Intent(this, MainActivity.class);
i.putExtra("teamView", team);
startActivity(i);
}
private void populateRosterList(ArrayList<Player> list)
{
ArrayAdapter<Player> adapter = new ArrayAdapter<>(this,
R.layout.activity_list, R.id.genericText, list);
rosterList.setAdapter(adapter);
}
Consider your concept:
You serialize an object, i.e. you transform it into a transferrable format which is then copied over to the other activity and reconstructed as a new instance.
Consequently, you alter another instance, which is not available in the previous activity, if you do not return it - again, serialized - and finally reconstruct and copy it back into the respective instance.
What you need is a shared memory storage in your application, which can alter and retrieve data cross-activity OR a proper data routing using Intents w/ ISerializable.
Options:
Always serialize objects and pass and copy them around.
-> No multithreaded alteration, possibly slow, unbeautiful
Singleton application with global data storage ir Context Object (I do NOT recommend the due to memory management and Garbage
Collection inbetween Activity Switches BUT for consistency I'd
wanted to mention this option)
SQLite3
-> Quick, Simple and Scalable, But a bit cumbersome to get started with
Any other file-structure stored and maintained in the data folder
-> I'd expect a lot of boilerplate code here, and low performance
Webservice and remote database
Proper component setup, i.e. initialize all accessing components in your software with the appropriate reference to the data structs using for example fragments (Thanks to #mismanc, I actually missed that option initially)
In general you could abstract all that away using services and repositories, which allows you to under-the-hood test options like 3. 4. And 5. and find your best solution, and in addition, keeo the accessing code simple and clean.
in your case, you can use startActivityForResult instead of startActivity, then get your modified Team object from onActivityResult(int requestCode, int resultCode, Intent data) to update your list.
startActivityForResult example
You can use fragments. You hold the list in the MainActivity and pass its reference to ShowListFragment and AddPlayerFragment by interfaces. And you can also do other operations over them. If you dont want to use json or sqlite it can be a good way for you.
MainActivity.java
public class MainActivity extends Activity implements ShowListener{
public interface ShowListener{
ArrayList<Team> getTeamList();
}
private ArrayList<Team> listOfTeams = new ArrayList<>();
#Override
public ArrayList<Team> getTeamList() {
return listOfTeams;
}
}
ShowListFragment.java
public class ShowListFragment extends Fragment {
private ArrayList<Team> listOfTeams;
private ShowListener listener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listener = (ShowListener)getActivity();
listOfTeams = listener.getTeamList();
}
}
As #Kingfisher Phuoc mentioned you could use srartActivityForResult in case you don't want to change your approach.
Otherwise I will suggest you use either :
SharedPreference to store your arraylist object (by converting the arraylist to json then store it as string in json format). In the PlayerActivity you retrieve the data, manipulate it then save it. see this post
SQLite

Can a fragment object created in MainActivity be passed as intent extra to other activity?

I have a class WeatherFragment that extends Fragment class. I created an instance of it in the launcher activity and inflated it in a layout. Is it possible for me to to send the fragment object as an intent extra to some other activity in my project instead of creating a new instance of WeatherFragment?
Don't have a code for this. Its just an interview question.
I think you can, but it will not be good. A quick search brought me to this question with an answer that said:
You wouldn't. At most, you would follow #Ribose's answer -- pass a flag into the activity via an extra to indicate what set of fragments to create.
Your question is not so specific. This question is specific to what the OP wants, but maybe one of the answers could help you.
P.S. If you would like to experiment though, you can have your WeatherFragment implement Parcelable. Then pass it from one activity to another activity through intent. This answer will tell you how and you could do it like so (modified to extend Fragment class)
public class WeatherFragment extends implements Parcelable {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
/* everything below here is for implementing Parcelable */
// 99.9% of the time you can just ignore this
public int describeContents() {
return 0;
}
// write your object's data to the passed-in Parcel
public void writeToParcel(Parcel out, int flags) {
//code
}
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
// example constructor that takes a Parcel and gives you an object populated with it's values
private MyParcelable(Parcel in) {
//code
}
//other methods
}
Then, from the answer again, you can use it like so:
Intent intent = new Intent();
intent.putExtra(KEY_EXTRA, weatherFragment);
From the answer again (You really should read this answer), you get it like so:
Intent intent = getIntent();
WeatherFragment weatherFragment = (WeatherFragment) intent.getParcelableExtra(MainActivity.KEY_EXTRA);
I have not tested this so I'm not sure if it would work.
Between different Acitvities you can not since Fragment does not implement Serializable or Parcelable.
Sure you can make your Fragment implement those interfaces but this way you won't actually be passing Fragment, just some state of that Fragment which you then serialize yourself.
Within the same Activity you can have your fragment back when the Activity gets recreated if you use FragmentManager.putFragment() in onSaveState() and getFragment() in onCreate(). This is not needed usually.
Possible but I won't recommend it. But you can get the fragment object by using findFragmentById or findFragmentByTag to get the object.

Access fragment method from fragment

I've a fragment register. this one has a view pager inside. I need every page to put a data into register hashmap variable . this what i've tried ..
this is a method that i want to access from every pages
public void addData(String key, String data){
Toast.makeText(this.getActivity(), data, Toast.LENGTH_LONG).show();
}
i've tried like :
FragmentManager fm = getFragmentManager();
fragment = (ArtistRegister)fm.findFragmentById(R.id.asd);
fragment.addData("asd", asd);
but it always return null pointer ..
First solution:
ArtistRegister artistRegister = ((ArtistRegister) getParentFragment());
artistRegister.addData("asd", "asd");
Second solution:
Create interface and pass it as argument for PagerAdapter constructor.
EDIT:
OP used getFragmentManager() instead of getChildFragmentManager() to initialize adapter, so he was constantly getting NullPointerExpception

Categories