简体   繁体   中英

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;
   }

}

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;
   }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM