i have 3 ArrayList> that I want to pass to 3 fragments. Besides making them static, what is the best approach to do this?
You can use the setArguments in the Fragment. Take a look at http://developer.android.com/guide/components/fragments.html, basically, you create a Bundle before create your Fragment and then setup as an Argument.
Example from the Android Documentation:
public static class DetailsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// If the screen is now in landscape mode, we can show the
// dialog in-line with the list so we don't need this activity.
finish();
return;
}
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
}
}
}
Instead of use getIntent().getExtras(), you create you bundle and set the arguments
Bundle bundle = new Bundle();
bundle.putSerializable(YOUR_KEY, yourObject);
fragment.setArguments(bundle);
And for your Fragment:
public static class DetailsFragment extends Fragment {
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
ScrollView scroller = new ScrollView(getActivity());
TextView text = new TextView(getActivity());
int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4, getActivity().getResources().getDisplayMetrics());
text.setPadding(padding, padding, padding, padding);
scroller.addView(text);
text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
return scroller;
}
}
You can create listener callback interfaces and implement them in your fragments. Something like this:
#Override
public void onSomeEvent(List<SomeData> data) {
//do something with data
}
In your activity create this interface:
public interface OnSomeEventListener {
onSomeEvent(List<SomeData> data);
}
then obtain your fragment by using findFragmentById or findFragmentByTag and assign it to a listener:
this.onSomeEventListener = fragment;
You can then call methods of that interface and your fragment will receive callbacks.
The second and more easier way of communication between fragments and activities is BroadcastReceivers. You can register some BroadcastReceiver in your fragments and then call sendBroadcast() from activity. Your list of data can be put in a bundle of that broadcast message.
Related
I have an Activity using a FragmentStatePagerAdapter. If I launch another activity that changes some data involved with what is displayed, the view is not updated.
If the adapter is handling tabs, each to show different aspects of the same object via Fragments,
if an object attribute is changed by an activity launched from a page handled by the adapter,
and the adapter notifyDataSetChanged is called in onActivityResult, the data in the tab view is not getting updated, as I expect it should be.
I cannot figure out why.
In the activity class:
public class EventDetailActivity extends AppCompatActivity
{
public ViewPager viewPager;
public PagerAdapter adapter; // This extends FragmentStatePagerAdapter
public TabLayout tabLayout;
public Event currentEvent; // ****** Contains the data to display in tabs
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_event_detail);
currentEvent = (Event)getIntent().getSerializableExtra(Event.EVENT_KEY); // ***** The object on display was serialized to pass in the intent.
// Serializing it in the initial intent is not a problem, because it is saved in the database within this activity,
// and the calling activity gets the update via the database.
tabLayout = (TabLayout) findViewById (R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().
setText(getResources().getString(R.string.details)));
... Add other tabs. ...
tabLayout.setTabGravity (TabLayout.GRAVITY_FILL);
viewPager = (ViewPager) findViewById (R.id.pager);
adapter = new PagerAdapter(currentEvent, getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter (adapter);
viewPager.addOnPageChangeListener (new TabLayout.TabLayoutOnPageChangeListener (tabLayout));
tabLayout.setOnTabSelectedListener (new TabLayout.OnTabSelectedListener () {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem (tab.getPosition ());
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId ())
{
case R.id.menu_edit:
Intent intent = new Intent(EventDetailActivity.this, EditEventActivity.class);
intent.putExtra(Event.EVENT_KEY, currentEvent); // TODO Event via serialization, or event id only ?
//intent.putExtra("id", currentEvent.getId());
startActivityForResult(intent, EDIT_EVENT_REQUEST);
return true;
... other cases
}
}
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
if (requestCode == EDIT_EVENT_REQUEST)
{
switch (resultCode)
{
case RESULT_CANCELED:
// Nothing to do
return;
case RESULT_EVENT_MODIFIED:
// Cause event view for this activity to update.
// When the edit activity was started, the Event was serialized.
// An updated Event is passed back in the result.
//currentEvent = (Event)data.getSerializableExtra(Event.EVENT_KEY);
//System.out.println("Modified event returned: " + currentEvent.getEventTitle());
// Alternatively, Load the Event from the database:
try
{
HashMap attr = MyApp.getDatabase().getEventById(currentEvent.getId());
currentEvent.load(attr);
System.out.println("Event reloaded: " + currentEvent.getEventTitle());
}
catch (PersistenceException ex)
{
// TODO handle error
}
// FIXME: In both cases the received event is correct, but the UI is not updated.
// The adapter still references the object that was passed to the edit activity as serialized data
// So must give the adapter the object just deserialized/loaded here.
adapter.setEvent(currentEvent); // ***** notifyDataSetChanged() is called within this, but not updating the view !!!!!!!!
return;
case RESULT_EVENT_UPDATE_FAILED:
// Nothing to do
return;
}
}
}
...
}
The adapter:
public class PagerAdapter extends FragmentStatePagerAdapter
{
/** The event on display */
private Event m_event;
public PagerAdapter (Event event, FragmentManager fm)
{
super(fm);
m_event = event;
}
public void setEvent (Event event)
{
m_event = event;
notifyDataSetChanged(); // ****** Attempting to trigger update of displayed data, but the view does not update.
}
#Override
public Fragment getItem (int position)
{
Fragment f;
switch (position)
{
case 0:
f = new DetailsFragment();
break;
... other tab fragments
default:
return null;
}
// ******* FIXME?: The problem with passing serialized event to the fragment is that the fragment does not reference our event.
... each fragment references a COPY of the event.
// The updated event is passed back in the result... then set in the adapter.... BUT NOT IN FRAGMENTS
... BUT FRAGMENTS GET CREATED HERE AS NECESSARY TO VIEW, AND WILL GET THE MODIFIED EVENT IN THIS ARGUMENTS BUNDLE:
Bundle bundle = new Bundle();
bundle.putSerializable(Event.EVENT_KEY, m_event);
// Maybe just pass the event id in arguments, and the fragment gets the event from the database?? Sounds inefficient, and I think should not be necessary.
//bundle.putLong(Event.EVENT_ID_KEY, m_event.getId());
f.setArguments(bundle);
return f;
}
...
}
public class DetailsFragment extends Fragment
{
/** Event to display */
private Event m_event = null;
... UI TextView object declarations to show various attributes ...
public DetailsFragment() {
// Required empty public constructor
}
private void update ()
{
if (m_event == null)
{
... set views empty ...
return;
}
... set views for attributes of m_event ...
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate (R.layout.fragment_event_details, container, false);
//initalize widgets ...
// Get event for view
Bundle args = getArguments();
m_event = (Event)args.getSerializable(Event.EVENT_KEY); // ***** Get the event passed in arguments to this fragment
update();
return view;
}
}
Try override method in your FragmentStatePagerAdapter
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}
This is the answer that I got from other topic and applied it to my codes:
From Activity you send data with intent as:
Bundle bundle = new Bundle();
bundle.putString("edttext", "From Activity");
// set Fragmentclass Arguments
Fragmentclass fragobj = new Fragmentclass();
fragobj.setArguments(bundle);
and in Fragment onCreateView method:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String strtext = getArguments().getString("edttext");
return inflater.inflate(R.layout.fragment, container, false);
}
These are the codes that I applied, it is not working somehow. The fragment is already opened at the start.
public void onInfoWindowClick(Marker marker) {
if (tag.equals("Click to show all routes in this point")) {
Bundle bundle = new Bundle();
bundle.putString("route1", "Divisoria - San Juan");
// set Fragmentclass Arguments
hideShowFragment fragobj = new hideShowFragment();
fragobj.setArguments(bundle);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
Fragment intersectionFragment = manager.findFragmentById(R.id.fragmentContainer2);
ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
ft.add(R.id.fragmentContainer2, fragobj);
ft.show(intersectionFragment);
ft.commit();
}
}
The codes in my onCreateView method:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_hide_show, container, false);
if (!routes.equals(none)) {
routes = getArguments().getString("route1");
} else {
routes = "Food";
}
return view;
}
What I want to happen is that the fragment will always update to what marker Tag that I click on the map. In other words, pass the string to the fragment (that is opened) and update it.
I do not want to use startActivityForResult because I can't move around the map if I don't use fragments. Is there a way to send result from activity to fragment that is already opened and running? If none, then how can I make the fragment not running from the start (using supportFragmentManager)? I only know is to hide it
If you have running Fragment and want to pass some data to it, you should create some way to communicate. For that purposes, you can use Observer pattern.
First of all create interface inside Activity if you want to pass data to Fragment:
public interface OnInfoClickedListener {
void onInfoClicked(String info);
}
Implement this interface inside Fragment:
#Override
public void onInfoClicked(String info) {
infoTextView.setText(info);
}
Now, inside your Activity, create variable to store this interface implementation:
private OnInfoClickedListener listener;
And when instantiating Fragment, save instance of it to variable:
InfoFragment fragment = InfoFragment.newInstance();
listener = fragment;
And when needed just provide data through this interface:
listener.onInfoClicked("Info - " + UUID.randomUUID());
I am new to android and I am trying to call my MapFragment from adapter after on click using intent below is my code
Below is adapter code:
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
final BusInfo info = getItem(position);
View view = LayoutInflater.from(context).inflate(R.layout.bus_only_list,null);
TextView busname;
busname = (TextView) view.findViewById(R.id.busname);
busname.setText(info.name);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
pref = context.getSharedPreferences("busInfo",Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString("bus_name",info.name);
editor.commit();
Intent intent = new Intent(context, MapsFragment.class);
intent.putExtra("name",info.name);
context.startActivity(intent);>
}
});
return view;
}
I want to pass to mapfragment using intent but it redirect to MainActivity instead of MapFragment. How can I stop transferring to MainActivity?
Thank you.
A common pattern to passing a value to a Fragment is using newInstance method. In this method you can set Argument to fragment as a means to send the value.
First, create the newInstance method:
public class YourFragment extends Fragment {
...
// Creates a new fragment with bus_name
public static YourFragment newInstance(String busName) {
YourFragment yourFragment = new YourFragment();
Bundle args = new Bundle();
args.putString("bus_name", busName);
yourFragment.setArguments(args);
return yourFragment;
}
...
}
Then you can get the value in onCreate:
public class YourFragment extends Fragment {
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get the value from arguments
String busName = getArguments().getString("bus_name", "");
}
...
}
You can set the value to the Fragment from your activity with:
FragmentTransaction fragTransaction = getSupportFragmentManager().beginTransaction();
YourFragment yourFragment = YourFragment.newInstance("bus_name_value");
fragTransaction.replace(R.id.fragment_place_holder, yourFragment);
fragTransaction.commit();
You can use the above codes to send the value in Fragment initialization.
If you want to set the value to the already instantiated fragment, you can create a method then invoke the method to set the value:
public class YourFragment extends Fragment {
...
public setBusName(String busName) {
// set the bus name to your fragment.
}
...
}
Now, In the activity, you can invoke it with:
// R.id.yourFragment is the id of fragment in xml
YourFragment yourFragment = (YourFragment) getSupportFragmentManager()
.findFragmentById(R.id.yourFragment);
yourFragment.setBusName("bus_name_value");
You cannot pass an intent to a Fragment. Try using a Bundle instead.
Bundle bundle = new Bundle();
bundle.putString("name", info.name);
mapFragment.setArguments(bundle)
In your Fragment (MapsFragment) get the Bundle like this:
Bundle bundle = this.getArguments();
if(bundle != null){
String infoName = bundle.getString("name");
}
As guys mentioned before:
1. Use callback or just casting on your context (your activity must handle changing fragments itself).
2. To change fragments use activity's FragmentManager - intent is used to start another activity.
Fragments Documentation
I'm making an app with actionbar tabs, and the code of each fragment is almost the same... So i thought about using 1 fragment (passing the tab position to the fragment so it will know what to do on onCreateView) but some developer said it was a pain to save the tab state.
I also thought about making a class and extend each fragment from there, still, the used code is almost the same and i ran into some troubles trying this.
So I'm not sure about the best way to do this, the app is working... but i hope you can help me to improve my design. Thanks in advance.
You create similar fragments by creating a static method to create the fragment and set arguments. When oncreate runs you access the arguments. Pretty much the same as viewpager.
public class MainActionBarTabListFragment extends ListFragment {
public static MainActionBarTabListFragment newInstance(int sortOrder,ArrayList<String> tabsList) {
MainActionBarTabListFragment f = new MainActionBarTabListFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt(SORT_ORDER, sortOrder);
args.putStringArrayList(TAB_NAME, tabsList);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle b = this.getArguments();
if (b != null) {
mSortOrder = b.getInt(SORT_ORDER, 0);
tabsList = b.getStringArrayList(TAB_NAME);
}
}
public class MainActionBarTabsPagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<String> tabsList;
public MainActionBarTabsPagerAdapter(FragmentManager fm, ArrayList<String> tabsList) {
super(fm);
this.tabsList = tabsList;
}
/** This method will be invoked when a page is requested to create */
#Override
public Fragment getItem(int fragmentPage) {
MainActionBarTabListFragment fragment0 = MainActionBarTabListFragment
.newInstance(fragmentPage, tabsList);
return fragment0;
}
#Override
public int getCount() {
return tabsList.size();
}
}
I can store the value in one variable. Now I want pass that variable into a fragment
Using the code below, I am able to load fragments:
public class AndroidListFragmentActivity extends Activity {
Fragment2 f2;
public static String itemname;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.apetiserfragement);
itemname=getIntent().getStringExtra("itemname");
Bundle args=new Bundle();
args.putString("itemname", itemname);
f2=new Fragment2();
f2.setArguments(args);
}
} /* (Here I load fragment using xml page) itemname */
The output is splitted into 2 windows one for extend for listfragment (for listview) and one for fragments.
Fragment2.xml
public class Fragment2 extends Fragment {
String itemname;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(getArguments().getString("itemname"));
return inflater.inflate(R.layout.fragment2, container, false);
}
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
}
AndroidListFragmentActivity in this class itemname I want pass Fragment2.class.. please help me
If both fragment are on other activity then can use intent
if on same activity then can do operation on that particular activity
as in this link
see onListItemClick of class TitlesFragment
**
* Helper function to show the details of a selected item, either by
* displaying a fragment in-place in the current UI, or starting a
* whole new activity in which it is displayed.
*/
void showDetails(int index) {
mCurCheckPosition = index;
if (mDualPane) {//<---------------------f on same activity then can do operation on that particular fragment
// We can display everything in-place with fragments, so update
// the list to highlight the selected item and show the data.
getListView().setItemChecked(index, true);
// Check what fragment is currently shown, replace if needed.
DetailsFragment details = (DetailsFragment) //<------------------------see use getFragmentManager
getFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
// Make new fragment to show this selection.
details = DetailsFragment.newInstance(index);
// Execute a transaction, replacing any existing fragment
// with this one inside the frame.
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.details, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
} else { //<----------If both fragment are on other activity then can use intent
// Otherwise we need to launch a new activity to display
// the dialog fragment with selected text.
Intent intent = new Intent();
intent.setClass(getActivity(), DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
See this response here : data sharing between fragments and activity in android