使用 volley 获取位置数据并在 Google 地图中创建多个标记

Fetching location data using volley and creating multiple markers in Google maps

这是我第一次在 android.I 中使用 google 地图能够创建显示我当前用户位置的地图。

但是,我想显示多个标记,我正在使用 volley 获取其数据。为此,我使用了带有单例 class 的 backgroundTask class。 backgroundTask class 是返回位置数据数组列表的任务。

出于这个原因,我需要在我的 MapActivity 激活之前用 arrayList 填充我的 BackgroungTask class 并访问 onCreate 中的对象数组列表,但似乎 MapActivity 加载速度非常快并且引用了 arrayList来自 BackgroungTask class 的对象始终为空。

这是正在获取数据的 BackgroundTaskclass

package com.example.carwashplaces;

import android.content.Context;
import android.widget.Toast;


import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

public class BackgroundTask {
private Context context;
private ArrayList<ModelLocation> locationArrayList = new ArrayList<>();
String json_url = "http://histogenetic-exhaus.000webhostapp.com/location.php";

public BackgroundTask(Context context) {
    this.context = context;
}

public ArrayList<ModelLocation> getLocationArrayList(){
    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest (
            Request.Method.POST,
            json_url,
            null,
            new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                   int count = 0;
                   while (count<response.length()){

                       try {
                           JSONObject jsonObject = response.getJSONObject(count);
                           ModelLocation modelLocation = new ModelLocation(
                                   jsonObject.getString("id"),
                                   jsonObject.getString("name"),
                                   jsonObject.getString("latitude"),
                                   jsonObject.getString("longitude"),
                                   jsonObject.getString("staff"),
                                   jsonObject.getString("comment"),
                                   jsonObject.getString("phone"));
                           locationArrayList.add(modelLocation);
                           count++;

                       } catch (JSONException e) {
                           e.printStackTrace();
                       }
                   }

                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

            Toast.makeText(context, "Error fetching car wash places...  ", Toast.LENGTH_SHORT).show();
            error.printStackTrace();
        }
    });

    MySingleton.getInstance(context).addToRequestque(jsonArrayRequest);

    return locationArrayList;
   }
}

这是我要显示位置的 MapActivity。

package com.example.carwashplaces;

import android.content.Intent;
import android.content.IntentSender;
import android.location.Location;
import android.os.Bundle;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;

import com.google.android.libraries.places.api.model.AutocompletePrediction;
import com.google.android.libraries.places.api.model.AutocompleteSessionToken;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.TypeFilter;
import com.google.android.libraries.places.api.net.FetchPlaceRequest;
import com.google.android.libraries.places.api.net.FetchPlaceResponse;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsRequest;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsResponse;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.mancj.materialsearchbar.MaterialSearchBar;
import com.mancj.materialsearchbar.adapter.SuggestionsAdapter;
import com.skyfishjy.library.RippleBackground;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {

private GoogleMap mMap;
private FusedLocationProviderClient mFusedLocationProviderClient;
private PlacesClient placesClient;
private List<AutocompletePrediction> predictionList;
private ArrayList<ModelLocation> locations;
private BackgroundTask backgroundTask;

private Location mLastKnownLocation;
private LocationCallback locationCallback;

private MaterialSearchBar materialSearchBar;
private View mapView;
private Button btnFind;
private RippleBackground rippleBg;

private final float DEFAULT_ZOOM = 18;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_map);

    //init views
    materialSearchBar = findViewById(R.id.searchBar);
    btnFind = findViewById(R.id.btnFind);
    rippleBg = findViewById(R.id.ripple_bg);

    //an object of backgroung task
    backgroundTask = new BackgroundTask(MapActivity.this);
    locations = new ArrayList<>();

    //getting location data from background task
    locations = backgroundTask.getLocationArrayList();



    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
    mapView = mapFragment.getView();

    mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(MapActivity.this);
    Places.initialize(MapActivity.this, "MY_KEY");
    placesClient = Places.createClient(this);
    final AutocompleteSessionToken token = AutocompleteSessionToken.newInstance();

    materialSearchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() {
        @Override
        public void onSearchStateChanged(boolean enabled) {

        }

        @Override
        public void onSearchConfirmed(CharSequence text) {
            startSearch(text.toString(), true, null, true);
        }

        @Override
        public void onButtonClicked(int buttonCode) {
            if (buttonCode == MaterialSearchBar.BUTTON_NAVIGATION){
                //opening or closing a  navigation drawer
            }else if (buttonCode == MaterialSearchBar.BUTTON_BACK){
                materialSearchBar.disableSearch();
            }
        }
    });

    materialSearchBar.addTextChangeListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            FindAutocompletePredictionsRequest predictionsRequest = FindAutocompletePredictionsRequest.builder()
                    .setCountry("ke")
                    .setTypeFilter(TypeFilter.ADDRESS)
                    .setSessionToken(token)
                    .setQuery(s.toString())
                    .build();
            placesClient.findAutocompletePredictions(predictionsRequest).addOnCompleteListener(new OnCompleteListener<FindAutocompletePredictionsResponse>() {
                @Override
                public void onComplete(@NonNull Task<FindAutocompletePredictionsResponse> task) {
                    if (task.isSuccessful()){
                        //predictions found, find out what suggestions we have from google
                        FindAutocompletePredictionsResponse predictionsResponse = task.getResult();
                        if (predictionsResponse != null){
                            predictionList = predictionsResponse.getAutocompletePredictions();
                            //converting predictionList into a list of string
                            List<String> suggestionsList = new ArrayList<>();
                            for (int i = 0; i < predictionList.size(); i++){
                                AutocompletePrediction prediction = predictionList.get(i);
                                suggestionsList.add(prediction.getFullText(null).toString());
                            }
                            //pass suggestion list to our MaterialSearchBar
                            materialSearchBar.updateLastSuggestions(suggestionsList);
                            if (!materialSearchBar.isSuggestionsVisible()){
                                materialSearchBar.showSuggestionsList();
                            }
                        }
                    }
                    else {
                        //some error
                        Log.i("mytag", "prediction fetching task failed");
                    }
                }
            });
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });

    materialSearchBar.setSuggstionsClickListener(new SuggestionsAdapter.OnItemViewClickListener() {
        @Override
        public void OnItemClickListener(int position, View v) {
            // seek the longitude and latitude of that suggestion
            if (position >= predictionList.size()){
                return;
            }
            AutocompletePrediction selectedPrediction = predictionList.get(position);
            String suggestion = materialSearchBar.getLastSuggestions().get(position).toString();
            materialSearchBar.setText(suggestion);

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    materialSearchBar.clearSuggestions();
                }
            },1000);

            InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
            if (imm != null){
                imm.hideSoftInputFromWindow(materialSearchBar.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
            }
            String placeId = selectedPrediction.getPlaceId();
            List<Place.Field> placeFields = Arrays.asList(Place.Field.LAT_LNG);// get latitude and longitude of a place

            FetchPlaceRequest fetchPlaceRequest = FetchPlaceRequest.builder(placeId, placeFields).build();
            placesClient.fetchPlace(fetchPlaceRequest).addOnSuccessListener(new OnSuccessListener<FetchPlaceResponse>() {
                @Override
                public void onSuccess(FetchPlaceResponse fetchPlaceResponse) {
                    // place found, update camera Factory
                    Place place = fetchPlaceResponse.getPlace();
                    Log.i("mytag", "place found: "+place.getName());
                    LatLng latLngOfPlace = place.getLatLng();
                    if (latLngOfPlace != null){
                        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLngOfPlace, DEFAULT_ZOOM));
                    }
                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    if (e instanceof ApiException){
                        ApiException apiException = (ApiException) e;
                        apiException.printStackTrace();
                        int statusCode = apiException.getStatusCode();
                        Log.i("mytag", "place not found: " +e.getMessage());
                        Log.i("mytag", "status code: "+statusCode);
                    }
                }
            });

        }

        @Override
        public void OnItemDeleteListener(int position, View v) {

        }
    });

    btnFind.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //get the current location of the marker
            LatLng currentMarkerLocation = mMap.getCameraPosition().target;
            rippleBg.startRippleAnimation();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    rippleBg.stopRippleAnimation();
                    Toast.makeText(MapActivity.this, "Nearest Car Wash", Toast.LENGTH_SHORT).show();
                }
            },3000);
        }
    });

}
//Called when the map is loaded
@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    mMap.setMyLocationEnabled(true);
    mMap.getUiSettings().setMyLocationButtonEnabled(true);


    if (mapView != null  && mapView.findViewById(Integer.parseInt("1")) != null){
        View locationButton = ((View) mapView.findViewById(Integer.parseInt("1")).getParent()).findViewById(Integer.parseInt("2"));
        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) locationButton.getLayoutParams();
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 0);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
        layoutParams.setMargins(0, 0, 40, 180);
    }

    //check if gps is enabled or not and then request user to enable it
    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setInterval(10000);
    locationRequest.setFastestInterval(5000);
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);

    SettingsClient settingsClient = LocationServices.getSettingsClient(MapActivity.this);
    Task<LocationSettingsResponse> task = settingsClient.checkLocationSettings(builder.build());

    task.addOnSuccessListener(MapActivity.this, new OnSuccessListener<LocationSettingsResponse>() {
        @Override
        public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
            getDeviceLocation();
            setMarkersToCarWashes();
        }
    });

    task.addOnFailureListener(MapActivity.this, new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            if (e instanceof ResolvableApiException){
                ResolvableApiException resolvable = (ResolvableApiException) e;
                try {
                    resolvable.startResolutionForResult(MapActivity.this, 51);
                } catch (IntentSender.SendIntentException ex) {
                    ex.printStackTrace();
                }
            }
        }
    });

    mMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
        @Override
        public boolean onMyLocationButtonClick() {
            if (materialSearchBar.isSuggestionsVisible())
                materialSearchBar.clearSuggestions();
            if (materialSearchBar.isSearchEnabled())
                materialSearchBar.disableSearch();
            return false;
        }
    });


    setMarkersToCarWashes();

}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 51) {
        if (resultCode == RESULT_OK) {
            getDeviceLocation();
            setMarkersToCarWashes();
        }
    }
}

private void setMarkersToCarWashes() {
    if (locations != null){
        for(int i = 0; i < locations.size(); i++){
            double lati = Double.parseDouble(locations.get(i).getLatitude());
            double longLat = Double.parseDouble(locations.get(i).getLongitude());

            //set markers to car wash places
            mMap.addMarker(new MarkerOptions().position(
                    new LatLng(lati, longLat))
            .title(locations.get(i).getName())
            .snippet(locations.get(i).getComment()));

        }
    }
}

private void getDeviceLocation() {
    mFusedLocationProviderClient.getLastLocation()
            .addOnCompleteListener(new OnCompleteListener<Location>() {
                @Override
                public void onComplete(@NonNull Task<Location> task) {
                    if (task.isSuccessful()){
                        mLastKnownLocation = task.getResult();
                        if (mLastKnownLocation != null){
                            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                        }else {
                            final LocationRequest locationRequest = LocationRequest.create();
                            locationRequest.setInterval(10000);
                            locationRequest.setFastestInterval(5000);
                            locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
                            locationCallback = new LocationCallback(){
                                @Override
                                public void onLocationResult(LocationResult locationResult) {
                                    super.onLocationResult(locationResult);
                                    if (locationResult == null){
                                        return;
                                    }
                                    mLastKnownLocation = locationResult.getLastLocation();
                                    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                                    mFusedLocationProviderClient.removeLocationUpdates(locationCallback);
                                }
                            };
                            mFusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, null);
                        }
                    }else {
                        Toast.makeText(MapActivity.this, "Unable to get last Location", Toast.LENGTH_SHORT).show();
                    }
                }
            });
    }
}

这是 MapActivity 中的代码部分,我试图从 BackgroundTask 中获取数组列表 class

 //an object of backgroung task
    backgroundTask = new BackgroundTask(MapActivity.this);
    locations = new ArrayList<>();

    //getting location data from background task
    locations = backgroundTask.getLocationArrayList();

如何在 onMapReady 方法中显示标记,即在 onMapReady 方法执行之前填充 BackgroundTask?

这是我的方法,基于相关线程的 this 解决方案。首先让我们创建一个接口来处理 volley 的响应。

public interface LocationsCallback {
    void onSuccess(ArrayList<ModelLocation> fetchedLocations);
}

然后修改你的getLocationArrayList方法如下:

public ArrayList<ModelLocation> getLocationArrayList(final LocationsCallback locationsCallback)

getLocationArrayListonResponse 方法中,在最后添加回调:

public ArrayList<ModelLocation> getLocationArrayList(final LocationsCallback locationsCallback) {

    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(
            Request.Method.POST,
            json_url,
            null,
            new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                    // your code here...

                    // Add this at the end here
                    ArrayList<ModelLocation> locations = locationArrayList;
                    locationsCallback.onSuccess(locations);
                }
                //...

现在将您的 MapActivity 的位置分配给获取的数组:

locations = new ArrayList<>();
backgroundTask = new BackgroundTask(MapActivity.this);
backgroundTask.getLocationArrayList(new LocationsCallback() {
    @Override
    public void onSuccess(ArrayList<ModelLocation> fetchedLocations) {
        locations = fetchedLocations;

        // Your other code goes here...
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(MapActivity.this);
        mapView = mapFragment.getView();
        mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(MapActivity.this);
        Places.initialize(MapActivity.this, "KEY");
        placesClient = Places.createClient(MapActivity.this);
        //...
    }
});

标记现在将显示在地图上。请参见下面的屏幕截图。 :)