Pass arraylist with bundle - java

I have a class like this
class Message implements Serializable
{
public String message, sender;
public Message (String msg, String from)
{
// check here that msg and from are not null. If so, use a default value or throw an NullPointerException
message = msg;
sender = from;
}
// Just a utility method for printing out the Message to System.out
public String toString () { return sender+":"+message; }
}
I am defining this variable in main activity
Hashtable<String, ArrayList<Message>> table = new Hashtable<String, ArrayList<Message>>();
table.get(room_name).add(new Message("Hi", "Sender"));
Bundle bundle = new Bundle();
bundle.putSerializable("messages", table.get(sendTo));
And in fragment I am getting this data with this code
ArrayList<Message> extractedMessages = (ArrayList<Message>)getArguments().getSerializable("messages");
System.out.println(extractedMessages.size());
But my app is crashing,I am getting java nullpointer exception for extractedMessages.size()
How can I fix this ?

Solution is ::
Bundle.putParcelable/Bundle.getParcelable
Have a look at this answer.
Use those methods, putParcelable and getParcelable.
Also you can ::
Intent intent = new Intent(context, SecondActity.class);
intent.putIntegerArrayListExtra("arraylist",myArrayList); //myArrayList is ArrayList<Integer>
startActivity(intent);
To get the arrayList in second Activity.
ArrayList arrayList<Integer> = getIntent().getIntegerArrayListExtra("myArrayList")
{Edit} ---- You can use newInstance to pass the data from fragment to fragment
This is one of the way to achieve it, i use constructor to pass the data
FragmentOne.java
int myData=12;
FragmentManager manager = getActivity().getSupportFragmentManager();
Fragment frgObj=FragmentTwo.newInstance(myData);
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, frgObj,"FragmentTwo");
ft.addToBackStack(null);
ft.commit();
FragmentTwo.java
int myData;
public static FragmentTwo newInstance(int _myData){
FragmentTwo fragment = new FragmentTwo();
myData=_myData
return fragment;
}
{EDIT-3}
I have made a project for you download it from Here
Mount in in your editor and run it .... it has bunch of other stuffs
.... don't bother about it
Check how i am starting a fragment from activity ----> then
onClick of button how i am starting a fragment ---> there pass
the data with the constructor as i shown in edit2
ALSO REFER -- this -- StackOVERFLOW POST

Related

Can not add Fragment

I've been stuck on this for a while now and will appreciate any guidance on this. Also not sure if my title is accurate. Inside my adapter I create a new fragment:
Adapter code:
cardAdoptDetailsFrag nextFrag = new cardAdoptDetailsFrag();
android.support.v4.app.Fragment callingFrag = ((FragmentActivity)context).getSupportFragmentManager().findFragmentByTag("TagFeedFragment");
FragmentTransaction ft = ((FragmentActivity)context).getSupportFragmentManager().beginTransaction();
ft.hide(callingFrag);
ft.add(R.id.fram, nextFrag,"cardAdoptDetailsFrag");
ft.addToBackStack("TagFeedFragment");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
Then inside my Main Activity where I manage all the fragments, I'm trying to check if the "cardAdoptDetailsFrag" isAdded. For some reason I cannot do it as per the below.
Get Fragment by tag. The below is where it fails with null object reference.
Fragment frag = getSupportFragmentManager().findFragmentByTag("cardAdoptDetailsFrag");
Log.d(TAG, "Check if added: "+frag.isAdded());
Now I know I can just add it inside a method and do a null check and return a boolean, but I know I'm doing something wrong here. Because with my other fragments the isAdded and remove works, but they get initiated inside the Main Activity where "cardAdoptDetailsFrag" gets initiated inside the adapter.
Example of Main Activity:
public class MainActivity extends AppCompatActivity implements adoptFeedFragment.OnFragmentInteractionListener,
lostAndFoundFragment.OnFragmentInteractionListener,
servicesFragment.OnFragmentInteractionListener,
userMenuFragment.OnFragmentInteractionListener,
showUserAdoptPostsFrag.OnFragmentInteractionListener{
adoptFeedFragment adoptFeedFragment;
lostAndFoundFragment lostAndFoundFragment;
servicesFragment servicesFragment;
userMenuFragment userMenuFragment;
....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adoptFeedFragment = new adoptFeedFragment();
lostAndFoundFragment = new lostAndFoundFragment();
servicesFragment = new servicesFragment();
userMenuFragment = new userMenuFragment();
....
//Here I can do for example:
adoptFeedFragment.isAdded(); //Will simply return a boolean.
//Or I can do a remove:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.remove(adoptFeedFragment);
//But I cannot do the below:
Fragment frag = getSupportFragmentManager().findFragmentByTag("cardAdoptDetailsFrag");
Log.d(TAG, "frag.isAdded(): "+frag.isAdded());
*****Edited Post. Ok lets say the frag is added. Why can I not remove it using the below.
public boolean isAdoptDetailsFragAdded() {
Fragment frag = getSupportFragmentManager().findFragmentByTag("cardAdoptDetailsFrag");
if(frag == null){
return false;
} else {
return true;
}
}
public Fragment getAdoptDetailsFrag() {
return getSupportFragmentManager().findFragmentByTag("cardAdoptDetailsFrag");
}
//I'm unable to remove the fragment using the below:
Log.d(TAG, "showFeedFragment: "+isAdoptDetailsFragAdded()); <--returns true
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(isAdoptDetailsFragAdded()) {
ft.remove(getAdoptDetailsFrag());
}
ft.commit();
//Now I check again wether it is still added. And still returns true even though I just removed it.
Log.d(TAG, "showFeedFragment: "+isAdoptDetailsFragAdded()); <-- Still returns true.
}
This is my fragment adding or replacing method which i am using in 13 projects.
You can use it and get rid of managing fragments.
/**
* replace or add fragment to the container
*
* #param fragment pass android.support.v4.app.Fragment
* #param bundle pass your extra bundle if any
* #param popBackStack if true it will clear back stack
* #param findInStack if true it will load old fragment if found
*/
public void replaceFragment(Fragment fragment, #Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
String tag = fragment.getClass().getName();
Fragment parentFragment;
if (findInStack && fm.findFragmentByTag(tag) != null) {
parentFragment = fm.findFragmentByTag(tag);
} else {
parentFragment = fragment;
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null)
parentFragment.setArguments(bundle);
else parentFragment.setArguments(null);
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
ft.addToBackStack(parentFragment.getClass().getName() + "");
}
ft.replace(R.id.main_frame_container, parentFragment, tag);
ft.commit();
fm.executePendingTransactions();
}
Here R.id.main_frame_container is FrameLayout in activity layout on which fragment is placed.
Here findFragmentByTag() is returning null when you do getSupportFragmentManager().findFragmentByTag("cardAdoptDetailsFrag").
findFragmentByTag() will return null in two cases.
If fragment cardAdoptDetailsFrag is not yet attached to view (you did not call add or replace for this fragment)
You added that fragment, but you are using different TAG, while searching that fragment.
Make sure if non of the case in your problem.
Now the solution is, you can modify your logic, like
Fragment frag = getSupportFragmentManager().findFragmentByTag("cardAdoptDetailsFrag");
if (frag != null && frag.isAdded()) {
// Do you task. Fragment is added
} else {
// Fragment is not added yet
}
I did not went through the long post but I read only this part.
Now I check again wether it is still added. And still returns true even though I just removed it.
And the reason behind that is FragmentTransaction.commit is not synchronous.
Read the docs for commit method:
*Schedules a commit of this transaction. The commit does
* not happen immediately; it will be scheduled as work on the main thread
* to be done the next time that thread is ready.*
If you need that to be synchronous, use commitNow instead.

How to run the same fragment several times?

I'm creating an app that will allow the user to take little tests. There is therefore a TestActivity made up by a PreTestFragment (with a button "Start Test"), a TestFragment and a PostTestFragment (with results of the test). As TestFragment will be the fragment hosting a single question I want it to be displayed several times (let's say 10).
All I could come up with was making a static counter that is incremented every time the user presses NEXT in a TestFragment and that makes him quit after the counter reached 10.
I'm new to fragment management, so I feel like I may have done several errors in the implementation of the onClick() of the NEXT button.
static private int current_question;
public void onClick(View view) {
FragmentManager mng = getFragmentManager();
FragmentTransaction fgmTr = mng.beginTransaction();
Fragment currentFgm = mng.findFragmentByTag(FragmentTags.TEST_FTAG.toString());
Fragment nextFgm;
Bundle bundle = currentFgm.getArguments();
if (current_question < 10) { // test still running
current_question++;
nextFgm = mng.findFragmentByTag(FragmentTags.TEST_FTAG.toString());
// ...
// putting needed data in the bundle
// ...
if (nextFgm == null)
nextFgm = new TestFragment();
} else { // test is finished
nextFgm = mng.findFragmentByTag(FragmentTags.POST_FTAG.toString());
// ...
// putting different needed data in the bundle
// ...
if (nextFgm == null)
nextFgm = new PostTestFragment();
}
nextFgm.setArguments(bundle);
fgmTr.replace(R.id.test_body, nextFgm, FragmentTags.POST_FTAG.toString());
fgmTr.addToBackStack(null);
fgmTr.commit();
}
#Override
public void setArguments(Bundle args) {
super.setArguments(args);
// Should I use if-else here too to pass different data in the bundle?
}
Running it and pressing "NEXT" the first time I get the following error:
java.lang.IllegalStateException: Fragment already active
How to fix this code?
If your fragment (nextFgm) has already been created, you can't call setArguments.
take a look:
https://developer.android.com/reference/android/app/Fragment.html#setArguments%28android.os.Bundle%29
Usually i did it by create a new fragment's instance every time:
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, new myFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

Is the Activity onCreate called with each NewInstance of a fragment inside it?

I want to generate a new Fragment with the next object in a LinkedList.
When a user clicks next I want to reassign a global reference to the next one in the LinkedList and create a fragment with it.
Problem
Each time the user clicks next, the object will only be reassigned to the 2nd object in the LinkedList even if there is 5 objects in the LinkedList
ie. .get(1)
Possible cause I am thinking
Because I get the first object in my LinkedList in the Activity onCreate
parentFlow.getChildElements().get(0)
When the user clicks Next, the global reference is set to .get(1) in the Linkedlist, but when .newInstance() method gets called, the parent activity onCreatecalled again?
(reassigning the global reference back to .get(0) causing an endless cycle)
Is this the correct mechanism of fragment creation? (ie. Activity.onCreate() --> Fragment.onCreate --> Activity.onCreate())?
Code,
FlowStateActivity.java
public class FlowStateActivity extends AppCompatActivity
implements FlowElementFragment.OnFragmentSelectedListener {
private static final String TAG = FlowStateActivity.class.getName();
private Flow parentFlow;
// Parent Object Containing the LinkedList
private FlowElement fe;
// Global reference used to reassign object to next object in LinkedList
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flow_state);
parentFlow = getIntent().getParcelableExtra("parent");
// Gets the parent object which contains the LinkedList
fe = parentFlow.getChildElements().get(0);
// Assigns fe to the first element in the LinkedList
FlowElementFragment fragment = FlowElementFragment.newInstance(
fe
);
// Replace whatever is in the fragment_container view with this fragment,
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.flowstate_fragment_container, fragment)
.commit();
}
#Override
// User selects NEXT inside the Fragment
public void onNextSelected(View v) {
try {
fe = parentFlow.getChildElements()
.get(
fe.getNext()
);
// Assigns global reference to next element in the LinkedList
FlowElementFragment fgt = FlowElementFragment.newInstance(fe);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.flowstate_fragment_container, fgt)
.commit();
} catch (IndexOutOfBoundsException e) {
}
}
FlowElementFragment.java
public FlowElementFragment extends Fragment {
public static FlowElementFragment newInstance(FlowElement e) {
Bundle args = new Bundle();
args.putParcelable(FLOW_ELEMENT, e);
FlowElementFragment fragment = new FlowElementFragment();
fragment.setArguments(args);
return fragment;
}
}
Your code
fe = parentFlow.getChildElements() .get( fe.getNext() );
sounds very wrong.
Probably should be
fe = fe.getNext();
or else
fe = parentFlow.getChildElements().get( ++current );
Is the Activity onCreate called with each NewInstance of a fragment
inside it?
No.
It will be called only once.
No.
Fragment has its functions to manage its events:
onAttached() — Called when the fragment has been associated with the activity
onCreateView() — Called to create the view for the fragment
onActivityCreated() — Called when the activity’s onCreate() method has been returned

Fragment listview update from activity

I am pretty new to Android (3 days), but I have a pretty good background in PHP (which may be the cause of my confusion in a Java based environment). I started building an Android app using Android Studio (Beta). I created the default Android Studio activity with the Navigation Drawer Activity. I edited the activity fragment part to look like this:
#Override
public void onNavigationDrawerItemSelected(int position) {
Bundle bundle = new Bundle();
bundle.putStringArrayList("contacts", arr);
bundle.putStringArrayList("messages", messages);
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FriendsFragment();
break;
case 1:
fragment = new ChatsFragment();
break;
case 2:
fragment = new GroupsFragment();
break;
case 3:
fragment = new LogoutFragment();
break;
default:
break;
}
if (fragment != null) {
fragment.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, fragment).addToBackStack(null).commit();
}
}
As you can see I am passing a Bundle to my Fragments called "messages" and "contacts" when an item is selected in the Navigation Drawer. The "messages" bundle are XMPP messages received by the aSmack library from an OpenFire server. So basically I'm trying to create a XMPP client. When I run the app I can receive the messages in the "ChatsFragment".
Now my problem:
I have to press the "ChatsFragment" item on the drawer to have my messages updated (re-receive the bundle) everytime I feel like there are new messages received from the server. But I want this to be done automatically by the fragment.
First I would like to know if my procedure is correct (Activity listens to server, creates bundle, send bundle to fragment, bundle updates messages on receive**)
** = This part I haven't been able to understand how to implement.
1- If the procedure is correct tell me how I should get the messages to be updated by the fragment through the activity?
2- If this is not the correct way to do things in Android, recommend me a way of doing it.
My code for displaying the messages in fragment:
private void displayListView() {
// Messages array list
List<String> contacts = getArguments().getStringArrayList("messages");
//System.out.println("arr: " + contacts);
//create an ArrayAdaptar from the String Array
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(getActivity(),
R.layout.url_list, contacts);
ListView listView = (ListView) getView().findViewById(R.id.listView);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//enables filtering for the contents of the given ListView
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Send the URL to the host activity
//mListener.onURLSelected(((TextView) view).getText().toString());
}
});
}
Thanks in advance.
Typically for long running operations in the background, like listening to a server, incoming messages, etc, you need to use Services. You do so by subclassing the Service class in Android.
Now for your problem - the design approach should be that you have a background service listening to incoming messages. Anytime a message is received (an input stream in your socket operator) you should send a "broadcast" an intent that a message was received. A custom broadcast receiver that you create should wait for this broadcast. Within the onReceive() method of this receiver, you should trigger the creation of the bundle and updating your message.
Remember you should always delegate your long running operations in Android to services. That is exactly what they are for.
So basically if you're already listening for new messages to come in your activity, then you must have some kind of callback like onMessageRecieved() or something like that.
If you do, you can then notify your fragment in this way.
Create a field (goes under your class declaration) called curFrag, so something like this:
public class MyActivity extends Activity {
private Fragment curFrag;
//other code...
}
then in the code you posted you would initialize the curFrag there, but you also need to set a tag for the current fragment. This will be based on your case statement. Make some final string variables as tags.
i.e.
public static final String CHATSTAG = "chatstag";
#Override
public void onNavigationDrawerItemSelected(int position) {
Bundle bundle = new Bundle();
bundle.putStringArrayList("contacts", arr);
bundle.putStringArrayList("messages", messages);
Fragment fragment = null;
String tag = null;
switch (position) {
case 0:
tag = FRIENDSTAG;
fragment = new FriendsFragment();
break;
case 1:
tag = CHATSTAG;... and so on through the switch statement.
fragment = new ChatsFragment();
break;
case 2:
fragment = new GroupsFragment();
break;
case 3:
fragment = new LogoutFragment();
break;
default:
break;
}
if (fragment != null) {
fragment.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
//remember to set the tag.
if(tag != null) {
fragmentManager.beginTransaction()
.replace(R.id.container, fragment, tag).addToBackStack(null).commit();
} else {
fragmentManager.beginTransaction()
.replace(R.id.container,fragment,DEFAULTTAG).addToBackStack(null).commit();
}
//new code
curFrag = fragment;
}
}
Now in your activity when a new message comes in, check the tag of the fragment and then if it matches a certain fragment, notify the fragment that new data has come in and retrieve it in the fragment.
public void onMessageRecieved() {
if(curFrag.getTag().equalsIgnoreCase(CHATSTAG)) {
ChatsFragment frag = (ChatsFragment) curFrag;
frag.notifyDataRecieved();
}
}
Once you have a reference to your fragment in the activity, you have access to any public methods in that fragment.
If your fragment cannot access the data on its own, then you'll need to get a reference to the activity and create a method in the activity that returns the new data.
So in your activity:
public String getMessageData() {
String newData = ...//get stuff from server;
return newData;
}
then in your fragment
public void notifyNewMessage() {
try {
MyActivity activity = (MyActivity) getActivity();
} catch (ClassCastException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
String message = activity.getMessageData();
//do something with the message.
}
It's not necessarily pretty but it works pretty well. You should also check to make sure your fragments are attached when you do this so that you avoid null exceptions.
Hope this helps!

use a object of a fragment in another fragment

I got a fragment, where I'm receiving messages, which a written into a message object demoRxMsg
In the receiver thread I call demoRxMsg = new MessageStructure(); data is received and written, everything is fine.
Now I want to use this demoRxMsg in another Fragment, to read out the containing data and "convert" them into a nice UI element.
The Question is: How do I do this. I tried to write a Getter, but when I call it via
demoRxMsg = ((Diagnostics) getActivity()).getMessage();
I get Cannot cast from Activity to Diagnostics.
Is it because a Fragment is not an Activity? But the Fragment (as I understood the fragment tutorials) is part of the activity, isn't it ?
I hope someone can help :)
Fragment is not an activity, it's a part of activity means they work together, without activity, fragment won't work etc.
If you want to use your data in another fragment you need to use Bundle
Bundle b = new Bundle();
b.putInt("my_data",YourData);
YourFragment f = new YourFragment();
f.setArguments(b);
And in your fragment to get value,
Bundle b = getArguments();
int v = b.getInt("my_data",0);
if it's an object that you want to pass, you need to serialize it and deserialize in fragment again
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(key, value)// The value arrayList object must be a parcel able object
fm = getFragmentManager();
if (fm != null) {
FragmentTransaction ft = fm.beginTransaction();
frag.setArguments(bundle);
ft.replace(R.id.fragment_main_container, frag, tag);
ft.commit();
}

Categories

Resources