Итак, продолжаем нашу серию спонтанных уроков, вызванных необходимостью переводить своё приложение на новый API
Сегодня мы разберемся, как получить маршрут и отобразить его на карте. Для этого обратимся к API Google Directions.
Получать маршрут мы будем грамотно, используя AsyncTask. Мы должны обратиться к Google Service Directions, передать координаты начальной и конечной точек и распарсить JSON ответ. Например вот такой: http://maps.google.com/maps/api/directions/json?origin=55.772851,37.586806&destination=55.415003,37.899904&sensor=false
При этом мы в ответе мы получаем шифрованную полилайн для каждого шага, я написал метод decodePolilyne. Можно конечно ограничиться начальными и конечными координатами шага, но полученный результат вас не устроит, проверено на себе.
Итак, пишем интерфейс для AsyncTask, сохраняем его в файл OnRouteCalcCompleted.java:
И саму AsyncTask RouteHandler.java:
package com.example.mapexample;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.android.gms.maps.model.LatLng;
import android.os.AsyncTask;
import android.util.Log;
/**
* Класс возвращает полилинию дороги, если указан mapView, отрисовывается на нём
* @author awacs
*
*/
public class RouteHandler extends AsyncTask {
private final static String TAG = "RouteHandler";
/**
* Для точного интерполирования марштура
*/
public static final int FINE_ROUTE = 1;
/**
* Для грубого интерполирования маршрута
*/
public static final int COARSE_ROUTE = 2;
private final HttpClient client = new DefaultHttpClient();
private String content;
private boolean error = false;
private String error_msg = "";
private ArrayList polyline;
private int accuracyRoute = 1;
private long distance;
private OnRouteCalcCompleted listener;
public RouteHandler( OnRouteCalcCompleted l ){
this.listener = l;
}
public double getDistance(){
Log.d(TAG, "distance = " + distance + "m");
return distance/1000;
}
public void calculateRoute( double latStart, double lonStart, double latEnd, double lonEnd, int accuracy ){
this.accuracyRoute = accuracy;
StringBuilder origin = new StringBuilder();
origin.append( Double.toString(latStart));
origin.append(",");
origin.append( Double.toString(lonStart));
StringBuilder destination = new StringBuilder();
destination.append( Double.toString(latEnd));
destination.append(",");
destination.append( Double.toString(lonEnd));
List nameValuePairs = new ArrayList();
nameValuePairs.add(new BasicNameValuePair("origin", origin.toString()));
nameValuePairs.add(new BasicNameValuePair("destination", destination.toString() ));
nameValuePairs.add(new BasicNameValuePair("sensor", "false"));
String paramString = URLEncodedUtils.format(nameValuePairs, "utf-8");
execute( "http://maps.google.com/maps/api/directions/json" + "?" + paramString );
}
public boolean isError(){
return error;
}
public String getErrorMsg(){
return error_msg;
}
@Override
protected String doInBackground(String... urls) {
Log.d(TAG, "RouteHandler::doInBackground");
try {
Log.v(TAG, urls[0]);
HttpPost httppost = new HttpPost(urls[0]);
ResponseHandler responseHandler = new BasicResponseHandler();
content = client.execute( httppost, responseHandler );
} catch (ClientProtocolException e) {
Log.d(TAG, "GetRouteHandler::ClientProtocolException");
e.printStackTrace();
error = true;
cancel(true);
} catch (IOException e) {
Log.d(TAG, "GetRouteHandler::IOException");
e.printStackTrace();
error = true;
cancel(true);
}
return content;
}
protected void onPostExecute(String content) {
if (error) {
error_msg = "Offline";
} else {
try {
JSONObject response = new JSONObject(content);
String status = response.getString("status");
Log.v(TAG, content);
if( status.equalsIgnoreCase("OK") ){
polyline = new ArrayList();
JSONArray routesArray = response.getJSONArray("routes");
JSONObject route = routesArray.getJSONObject(0);
// массив с информацией об отрезке маршрута
JSONArray legs = route.getJSONArray("legs");
JSONObject leg = legs.getJSONObject(0);
JSONObject distanceObj = leg.getJSONObject("distance");
distance = distanceObj.getLong("value");
JSONObject durationObj = leg.getJSONObject("duration");
// содержит куб выделения информационного окна для маршрута.
JSONObject bounds = route.getJSONObject("bounds");
JSONObject bounds_southwest = bounds.getJSONObject("southwest");
JSONObject bounds_northeast = bounds.getJSONObject("northeast");
double maxLat = bounds_northeast.getDouble("lat");
double maxLon = bounds_northeast.getDouble("lng");
double minLat = bounds_southwest.getDouble("lat");
double minLon = bounds_southwest.getDouble("lng");
JSONArray steps = leg.getJSONArray("steps");
for( int i=0; i
JSONObject step = steps.getJSONObject(i);
JSONObject start_location = step.getJSONObject("start_location");
JSONObject end_location = step.getJSONObject("end_location");
double latitudeStart = start_location.getDouble("lat");
double longitudeStart = start_location.getDouble("lng");
double latitudeEnd = end_location.getDouble("lat");
double longitudeEnd = end_location.getDouble("lng");
LatLng startGeoPoint = new LatLng(latitudeStart,longitudeStart);
LatLng endGeoPoint = new LatLng(latitudeEnd,longitudeEnd);
JSONObject polylineObject = step.getJSONObject("polyline");
if( accuracyRoute == FINE_ROUTE ){
List points = decodePoly(polylineObject.getString("points"));
Log.d(TAG, " " + points.size());
polyline.addAll(points);
} else {
polyline.add(startGeoPoint);
polyline.add(endGeoPoint);
}
}
} else if( status.equalsIgnoreCase("NOT_FOUND")){
// по крайней мере для одной заданной точки (исходной точки, пункта назначения или путевой точки) геокодирование невозможно.
} else if( status.equalsIgnoreCase("ZERO_RESULTS")){
// между исходной точкой и пунктом назначения не найдено ни одного маршрута.
} else if( status.equalsIgnoreCase("MAX_WAYPOINTS_EXCEEDED")){
// в запросе задано слишком много waypoints. Максимальное количество waypoints равно 8 плюс исходная точка и пункт назначения. ( (Пользователи Google Maps Premier могут выполнять запросы с количеством путевых точек до 23.)
} else if( status.equalsIgnoreCase("INVALID_REQUEST")){
// запрос недопустим
}else if( status.equalsIgnoreCase("OVER_QUERY_LIMIT")){
// служба получила слишком много запросов от вашего приложения в разрешенный период времени.
}else if( status.equalsIgnoreCase("REQUEST_DENIED")){
// служба Directions отклонила запрос вашего приложения.
}else if( status.equalsIgnoreCase("UNKNOWN_ERROR")){
// обработка запроса маршрута невозможна из-за ошибки сервера. При повторной попытке запрос может быть успешно выполнен
}
} catch (JSONException e) {
e.printStackTrace();
}
}
listener.onRouteCompleted( polyline );
} // end postExecute
/**
* Декодирует полилинию из переданной гуглом строки
* @param encoded
* @return
*/
private List decodePoly(String encoded) {
List poly = new ArrayList();
int index = 0, len = encoded.length();
int lat = 0, lng = 0;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
LatLng p = new LatLng( lat/1E5, lng/1E5);
poly.add(p);
}
return poly;
} // end decodePoly
} // end class
Для использования этого таска в нашей активити, нужно имплементить OnRouteCalcCompleted, соответвенно в Activity добавятся методы:
а отобразить готовую полилинию можно в добавленном onRouteCompleted:
}
Да, теперь добавляем например в инициализацию mMap из урока 3 код для вызова AsyncTask и компилируем готовое приложение.
private RouteHandler routeHandler;
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
}
routeHandler = new RouteHandler( this );
routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, RouteHandler.FINE_ROUTE);
}
Код полной активити:
package com.example.mapexample;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import java.sql.Time;
import java.util.ArrayList;
import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
public class MainActivity extends FragmentActivity implements LocationSource, LocationListener, OnRouteCalcCompleted{
private final static String TAG = "MainActivity";
private OnLocationChangedListener mListener;
private LocationManager lManager;
private GoogleMap mMap;
private static double mLatitude;
private static double mLongitude;
private RouteHandler routeHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
//mMap.setTrafficEnabled(true);
mMap.setMyLocationEnabled(true);
mMap.setOnMapLongClickListener(this);
lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
setUpMapIfNeeded();
}
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
if( lManager != null ){
lManager.requestLocationUpdates(lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
}
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
}
routeHandler = new RouteHandler( this );
routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, AppSettings.FINE_ROUTE);
}
protected void onPause() {
if( lManager != null ){
lManager.removeUpdates(this);
}
super.onPause();
}
@Override
public void onLocationChanged(Location location) {
if( mListener != null ){
mListener.onLocationChanged( location );
}
}
@Override
public void activate(OnLocationChangedListener listener) {
mListener = listener;
}
@Override
public void deactivate() {
mListener = null;
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
@Override
public void onRouteCalcBegin() {
// TODO Auto-generated method stub
}
@Override
public void onRouteCompleted(ArrayList route) {
mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
}
}
Сегодня мы разберемся, как получить маршрут и отобразить его на карте. Для этого обратимся к API Google Directions.
Получать маршрут мы будем грамотно, используя AsyncTask. Мы должны обратиться к Google Service Directions, передать координаты начальной и конечной точек и распарсить JSON ответ. Например вот такой: http://maps.google.com/maps/api/directions/json?origin=55.772851,37.586806&destination=55.415003,37.899904&sensor=false
При этом мы в ответе мы получаем шифрованную полилайн для каждого шага, я написал метод decodePolilyne. Можно конечно ограничиться начальными и конечными координатами шага, но полученный результат вас не устроит, проверено на себе.
Итак, пишем интерфейс для AsyncTask, сохраняем его в файл OnRouteCalcCompleted.java:
package com.example.mapexample;
import java.util.ArrayList;
import com.google.android.gms.maps.model.LatLng;
public interface OnRouteCalcCompleted{
void onRouteCalcBegin();
void onRouteCompleted( ArrayListroute );
}
И саму AsyncTask RouteHandler.java:
package com.example.mapexample;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.android.gms.maps.model.LatLng;
import android.os.AsyncTask;
import android.util.Log;
/**
* Класс возвращает полилинию дороги, если указан mapView, отрисовывается на нём
* @author awacs
*
*/
public class RouteHandler extends AsyncTask
private final static String TAG = "RouteHandler";
/**
* Для точного интерполирования марштура
*/
public static final int FINE_ROUTE = 1;
/**
* Для грубого интерполирования маршрута
*/
public static final int COARSE_ROUTE = 2;
private final HttpClient client = new DefaultHttpClient();
private String content;
private boolean error = false;
private String error_msg = "";
private ArrayList
private int accuracyRoute = 1;
private long distance;
private OnRouteCalcCompleted listener;
public RouteHandler( OnRouteCalcCompleted l ){
this.listener = l;
}
public double getDistance(){
Log.d(TAG, "distance = " + distance + "m");
return distance/1000;
}
public void calculateRoute( double latStart, double lonStart, double latEnd, double lonEnd, int accuracy ){
this.accuracyRoute = accuracy;
StringBuilder origin = new StringBuilder();
origin.append( Double.toString(latStart));
origin.append(",");
origin.append( Double.toString(lonStart));
StringBuilder destination = new StringBuilder();
destination.append( Double.toString(latEnd));
destination.append(",");
destination.append( Double.toString(lonEnd));
List
nameValuePairs.add(new BasicNameValuePair("origin", origin.toString()));
nameValuePairs.add(new BasicNameValuePair("destination", destination.toString() ));
nameValuePairs.add(new BasicNameValuePair("sensor", "false"));
String paramString = URLEncodedUtils.format(nameValuePairs, "utf-8");
execute( "http://maps.google.com/maps/api/directions/json" + "?" + paramString );
}
public boolean isError(){
return error;
}
public String getErrorMsg(){
return error_msg;
}
@Override
protected String doInBackground(String... urls) {
Log.d(TAG, "RouteHandler::doInBackground");
try {
Log.v(TAG, urls[0]);
HttpPost httppost = new HttpPost(urls[0]);
ResponseHandler
content = client.execute( httppost, responseHandler );
} catch (ClientProtocolException e) {
Log.d(TAG, "GetRouteHandler::ClientProtocolException");
e.printStackTrace();
error = true;
cancel(true);
} catch (IOException e) {
Log.d(TAG, "GetRouteHandler::IOException");
e.printStackTrace();
error = true;
cancel(true);
}
return content;
}
protected void onPostExecute(String content) {
if (error) {
error_msg = "Offline";
} else {
try {
JSONObject response = new JSONObject(content);
String status = response.getString("status");
Log.v(TAG, content);
if( status.equalsIgnoreCase("OK") ){
polyline = new ArrayList
JSONArray routesArray = response.getJSONArray("routes");
JSONObject route = routesArray.getJSONObject(0);
// массив с информацией об отрезке маршрута
JSONArray legs = route.getJSONArray("legs");
JSONObject leg = legs.getJSONObject(0);
JSONObject distanceObj = leg.getJSONObject("distance");
distance = distanceObj.getLong("value");
JSONObject durationObj = leg.getJSONObject("duration");
// содержит куб выделения информационного окна для маршрута.
JSONObject bounds = route.getJSONObject("bounds");
JSONObject bounds_southwest = bounds.getJSONObject("southwest");
JSONObject bounds_northeast = bounds.getJSONObject("northeast");
double maxLat = bounds_northeast.getDouble("lat");
double maxLon = bounds_northeast.getDouble("lng");
double minLat = bounds_southwest.getDouble("lat");
double minLon = bounds_southwest.getDouble("lng");
JSONArray steps = leg.getJSONArray("steps");
for( int i=0; i
JSONObject step = steps.getJSONObject(i);
JSONObject start_location = step.getJSONObject("start_location");
JSONObject end_location = step.getJSONObject("end_location");
double latitudeStart = start_location.getDouble("lat");
double longitudeStart = start_location.getDouble("lng");
double latitudeEnd = end_location.getDouble("lat");
double longitudeEnd = end_location.getDouble("lng");
LatLng startGeoPoint = new LatLng(latitudeStart,longitudeStart);
LatLng endGeoPoint = new LatLng(latitudeEnd,longitudeEnd);
JSONObject polylineObject = step.getJSONObject("polyline");
if( accuracyRoute == FINE_ROUTE ){
List
Log.d(TAG, " " + points.size());
polyline.addAll(points);
} else {
polyline.add(startGeoPoint);
polyline.add(endGeoPoint);
}
}
} else if( status.equalsIgnoreCase("NOT_FOUND")){
// по крайней мере для одной заданной точки (исходной точки, пункта назначения или путевой точки) геокодирование невозможно.
} else if( status.equalsIgnoreCase("ZERO_RESULTS")){
// между исходной точкой и пунктом назначения не найдено ни одного маршрута.
} else if( status.equalsIgnoreCase("MAX_WAYPOINTS_EXCEEDED")){
// в запросе задано слишком много waypoints. Максимальное количество waypoints равно 8 плюс исходная точка и пункт назначения. ( (Пользователи Google Maps Premier могут выполнять запросы с количеством путевых точек до 23.)
} else if( status.equalsIgnoreCase("INVALID_REQUEST")){
// запрос недопустим
}else if( status.equalsIgnoreCase("OVER_QUERY_LIMIT")){
// служба получила слишком много запросов от вашего приложения в разрешенный период времени.
}else if( status.equalsIgnoreCase("REQUEST_DENIED")){
// служба Directions отклонила запрос вашего приложения.
}else if( status.equalsIgnoreCase("UNKNOWN_ERROR")){
// обработка запроса маршрута невозможна из-за ошибки сервера. При повторной попытке запрос может быть успешно выполнен
}
} catch (JSONException e) {
e.printStackTrace();
}
}
listener.onRouteCompleted( polyline );
} // end postExecute
/**
* Декодирует полилинию из переданной гуглом строки
* @param encoded
* @return
*/
private List
List
int index = 0, len = encoded.length();
int lat = 0, lng = 0;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
LatLng p = new LatLng( lat/1E5, lng/1E5);
poly.add(p);
}
return poly;
} // end decodePoly
} // end class
Для использования этого таска в нашей активити, нужно имплементить OnRouteCalcCompleted, соответвенно в Activity добавятся методы:
@Override
public void onRouteCalcBegin() {
// Тут можно добавить например вызов прогрессбара, "Ждите, строим маршрут...."
}
public void onRouteCalcBegin() {
// Тут можно добавить например вызов прогрессбара, "Ждите, строим маршрут...."
}
а отобразить готовую полилинию можно в добавленном onRouteCompleted:
@Override
public void onRouteCompleted(ArrayList route) {
public void onRouteCompleted(ArrayList
mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
// а заодно и удалить прогрессбар :)}
Да, теперь добавляем например в инициализацию mMap из урока 3 код для вызова AsyncTask и компилируем готовое приложение.
private RouteHandler routeHandler;
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
}
routeHandler = new RouteHandler( this );
routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, RouteHandler.FINE_ROUTE);
}
Код полной активити:
package com.example.mapexample;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import java.sql.Time;
import java.util.ArrayList;
import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
public class MainActivity extends FragmentActivity implements LocationSource, LocationListener, OnRouteCalcCompleted{
private final static String TAG = "MainActivity";
private OnLocationChangedListener mListener;
private LocationManager lManager;
private GoogleMap mMap;
private static double mLatitude;
private static double mLongitude;
private RouteHandler routeHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
//mMap.setTrafficEnabled(true);
mMap.setMyLocationEnabled(true);
mMap.setOnMapLongClickListener(this);
lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
setUpMapIfNeeded();
}
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
if( lManager != null ){
lManager.requestLocationUpdates(lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
}
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
}
routeHandler = new RouteHandler( this );
routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, AppSettings.FINE_ROUTE);
}
protected void onPause() {
if( lManager != null ){
lManager.removeUpdates(this);
}
super.onPause();
}
@Override
public void onLocationChanged(Location location) {
if( mListener != null ){
mListener.onLocationChanged( location );
}
}
@Override
public void activate(OnLocationChangedListener listener) {
mListener = listener;
}
@Override
public void deactivate() {
mListener = null;
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
@Override
public void onRouteCalcBegin() {
// TODO Auto-generated method stub
}
@Override
public void onRouteCompleted(ArrayList
mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
}
}
3 комментария:
JSONArray steps = leg.getJSONArray("steps");
for( int i=0; i
строчка с for обрывается, пожалуйста исправьте
JSONArray steps = leg.getJSONArray("steps");
for( int i=0; i < steps.length(); i++){
Да, с кодом были проблемы. Но все равно спасибо, он помог сделать немного свой алгоритм. Если нужен полностью рабочий код пишите на почту.
Отправить комментарий