Anil Gorthy Anil Gorthy - 1 month ago 21
Android Question

ViewPager and updating Fragments

In my app's activity, I have a ViewPager implementation which loads a Map fragment (FragmentA) on launch and then there are 2 other fragments showing content based on the latitude and longitude selected in FragmentA.

I am unable to get the behavior where upon launch, the latitude/longitude from FragmentA, should be provided to the 2 other fragments and continue to provide whenever user clicks on the map in FragmentA.

Here are the approaches I tried:


  1. using interface to communicate between fragment -> activity -> fragment (sticking with this approach)

  2. using bundle to pass information between fragments when the fragments are getting initialized



However, neither of these approaches have worked for me.

MyActivity.java//update to show Daniel's suggestion

public class MyDemoActivity extends FragmentActivity
implements FragmentA.OnFragmentInteractionListener,
FragmentB.OnFragmentInteractionListener, IUserLatLong {

private MyPagerAdapter pagerAdapter;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.map_in_pager_demo);

ViewPager mPager = (ViewPager) findViewById(R.id.pager);
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), getApplicationContext());
mPager.setAdapter(pagerAdapter);

//always start w/ Maps View, FragmentA
mPager.setCurrentItem(1);

//
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//do something
}

@Override
public void onPageSelected(int position) {
// do this instead of calling, notifyDataSetChanged()
Fragment frag = pagerAdapter.fragments[position];
if (frag != null && frag instanceof FragmentB) {
Log.i(TAG, "::FragmentB:: Fetching data from Activity");
//here is my confusion, calling FragmentB with latlong from FragmentA
((FragmentB) frag).setNewLatLong(newUserLatLong);
}
}

@Override
public void onPageScrollStateChanged(int state) {
//do something
}
});
}

@Override
public void onFragmentInteraction(Uri uri) {
//do something
}

@Override
public void makeUseOfNewLocation(UserLatLong userLatLong) {
newUserLatLong = userLatLong;
Log.i(TAG, "LatLong from FragmentA is: " + newUserLatLong.getLat()
+ " and long is: " + newUserLatLong.getLng());
}

/**
* Used for fetching data from activity
* @return
*/
public UserLatLong getLLFromActivity() {
if(newUserLatLong == null) {
Log.e(TAG, "LatLong from FragmentA came up empty");
} else {

Log.i(TAG, "LatLong from FragmentA IS NOT empty: " + newUserLatLong.getLat()
+ " and long is: " + newUserLatLong.getLng());
}
return newUserLatLong;
}

}


MyPagerAdapter.java

public class MyPagerAdapter extends FragmentPagerAdapter {

private static int NUM_ITEMS = 3;
public MyPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
}

@Override
public int getCount() {
return NUM_ITEMS;
}

// not sure if this is really helping
@Override
public int getItemPosition(Object object) {
// POSITION_NONE makes it possible to reload the PagerAdapter
return POSITION_NONE;
}

@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
//uses location from FragmentA to get data
return new FragmentB();
case 1:
//loads map and gets location
return new FragmentA();
case 2:
//uses location from FragmentA to get data
return new FragmentC();
default:
return null;
}
}

//This populates your Fragment reference array:
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
fragments[position] = createdFragment;
Log.i(TAG, "::instantiateItem:: " + position + " " + createdFragment.toString());
return createdFragment;
}

}


FragmentA.java /**left out most of map specific code,
but I am interested in getting latitude/longitude whenever
onConnected()
or
onMapClick()
gets called*/

@Override
public void onConnected(@Nullable Bundle bundle) {
Log.i(TAG, "In onConnected(), Google API client is:: " + mGoogleApiClient.isConnected());

if (mGoogleApiClient != null) {
try {
// Get last known recent location.
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

if (mCurrentLocation != null) {
// Print current location if not null
final LatLng latLng = new LatLng(mCurrentLocation.getLatitude(),
mCurrentLocation.getLongitude());

//wrap LatLng into UserLatLong
userLatLong.setLat(latLng.latitude);
userLatLong.setLng(latLng.longitude);
//updating the value in the interface
mLLCallback.makeUseOfNewLocation(userLatLong);

CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 20);
mMap.animateCamera(cameraUpdate);
mMap.addMarker(new MarkerOptions().position(latLng));
Log.i(TAG, "::Google::My current location set::");

mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng1) {
mMap.clear(); //removes the previous marker
mMap.addMarker(new MarkerOptions().position(latLng1));

//updating the value in the interface
mLLCallback.makeUseOfNewLocation(userLatLong);
float x = (float) latLng1.latitude;
float y = (float) latLng1.longitude;
Log.d(TAG, "Map clicked w/ lat: " + x + " and long: " + y);

//wrap LatLng into UserLatLong
userLatLong.setLat(latLng1.latitude);
userLatLong.setLng(latLng1.longitude);

}
});

mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));

//for zooming automatically to the location of the marker
CameraPosition cameraPosition = new CameraPosition.Builder().target(latLng).zoom(12).build();
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

//TODO: Constrain the camera target to bounds defined by API
mMap.setMinZoomPreference(Constants.DEFAULT_MIN_ZOOM);
mMap.setMaxZoomPreference(Constants.DEFAULT_MAX_ZOOM);

} else {
Toast.makeText(getActivity(), "Current location is null", Toast.LENGTH_SHORT).show();
}
} catch (SecurityException se1) {
Log.e(TAG, "SecurityException1: " + se1.getLocalizedMessage());
}

} else {
Toast.makeText(getActivity(), "Google API client is null!", Toast.LENGTH_SHORT).show();
}


FragmentB /**in this fragment, I want to refresh content based on latitude/longitude from
FragmentA
*/

public class FragmentB extends Fragment {
/**
* Required empty public constructor
*/
public FragmentB() {
}


/**the getArguments() is always null*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
currLatLong = getNewLatLong();
}

/**I want latitude/longitude from FragmentA before onCreateView gets called*/
@Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {

LayoutInflater inflater1 = LayoutInflater.from(getContext());
// Inflate the layout for this fragment
view = inflater1.inflate(R.layout.fragment_a, container, false);
currLatLong = getNewLatLong();
//fetch my list based on LatLong
handleDataFetch(currLatLong);

return view;
}

//
private void handleDataFetch(UserLatLong newLatLong) {
final UserLatLong latLong = newLatLong;
final APIEndpointI apiEndpointI = APIRequests.getClient().create(APIEndpointI.class);

String userId = "XXXX-XXX-XXXXXXX";

currLatLong = new UserLatLong(newLat.doubleValue(), newLong.doubleValue());
if (currLatLong != null) {
Log.i(TAG, "Finally! Lat is: " + currLatLong.getLat() +
" and Long is:" + currLatLong.getLng());
/**using retrofit*/
Call<MyResp> call = apiEndpointI.getMyList(userId, currLatLong);
call.enqueue(new Callback<MyResp>() {
@Override
public void onResponse(Call<MyResp> call, Response<MyResp> response) {


Log.i(TAG, "Count: " + response.body().getBundles().size());
Resources res = getContext().getResources();
String cntString = String.format(res.getString(R.string.count), response.body().getBundles().size());
tv1.setText(cntString);

//Initialize with empty data
mGridData = new ArrayList<>();
mGridAdapter = new ProfilePicAdapter(getActivity(), R.layout.grid_item_layout, mGridData);
mGridView.setAdapter(mGridAdapter);

}
@Override
public void onFailure(Call<MyResp> call, Throwable t) {
//do something here
Log.d(TAG, "Failed to get response for GetMyList(): " + t.getMessage());
}
});
} else {
Log.e(TAG, "In onCreateView(), lat long are empty");
}
}


//called from activity to pass the latest latlong
public void setNewLatLong(UserLatLong userLatLong) {
currLatLong = userLatLong;
}

//called from activity to pass the latest latlong
private UserLatLong getNewLatLong() {
return currLatLong;
}

}

Answer

It looks like all you need to complete the implementation is to call handleDataFetch() from the setNewLatLong() method if it is a new location.

This is needed since onCreateView() won't be called due to the Fragment being in a ViewPager, so the best way to get the updated location info to FragmentB when it is displayed is to just use a public method called from the ViewPager.OnPageChangeListener in the Activity, as you are doing here.

Just be sure that your equals() method override is implemented correctly in the UserLatLong class, and add this to the setNewLatLong() method:

   //called from activity to pass the latest latlong
   public void setNewLatLong(UserLatLong userLatLong) {
      //Added:
      if (!currLatLong.equals(userLatLong)) {
          handleDataFetch(userLatLong);
      }
      currLatLong = userLatLong;
   }
Comments