Làm việc với GmapAPI. Ứng dụng tìm đường.
Bài đăng này đã không được cập nhật trong 3 năm
Mở đầu
Nghe đến Gmap chắc hẳn ai cũng quen thuộc và cũng đã từng sử dụng vài lần để tìm kiếm địa điểm và đường đi. Hôm nay mình muốn chia sẻ với các bạn cách sử dụng gmap api .
Ứng dụng
Hiện nay việc đưa map vào trong ứng dụng là vô cùng phổ biến , sử dụng map trong app giúp cho việc mô tả địa điểm một cách trực quan và rất dễ dàng , đồng thời xoay quanh map có thể có rất nhiều chức năng các bạn có thể phát triển làm tăng độ hiệu dụng của app : tìm đường, tìm kiếm xung quanh, .... Để ứng dụng được gmap vào app chúng ta cần xử lí một số bước sau.
Tạo 1 project map
- Bạn đảm bảo Android SDK của mình đã được cài đặt google playservice :
- Trong Android studio bạn creat new project và chọn google map Activity
Sau đó 1 project mới được tạo, bạn sẽ thấy xuất hiện file google_maps_api.xml trong value folder. Trong đó có đoạn
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string>
YOUR_KEY_HERE : chính là vị trí bạn điền key lấy được từ bước 2. Tại bước này khi run project bạn sẽ thấy như sau :
Lấy Google Maps API key:
Như hướng dẫn trong file google_maps_api.xml sau khi tạo cũng có ghi đầy đủ cách get 1 gmap api key. Trong khuôn khổ bài viết mình không viết chi tiết . Sau khi lấy được api key bạn thay thế vào "YOUR_KEY_HERE" trong bước 1 và kết quả bạn nhận được. Như vậy bạn đã hiển thị được map google trong app của mình để sử dụng. Có 1 số chú ý như sau.
- Trong file json chưa map : Các bạn chú ý sẽ có "android:name="com.google.android.gms.maps.SupportMapFragment" đây là đoạn code quan trọng để hiển thị map lên view bạn mong muốn.
<android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- Trong Activity, hoặc fragment chứa map chú ý cần implements OnMapReadyCallback
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
Hàm onMapReady được dùng để xử lí thông tin trên map khi map đã sẵn sàng. Sau đây là 1 số action mình hay sử dụng trên map.
Add Marker trên map.
Trong hàm onMapReady ta thêm như sau:
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Sydney and move the camera
LatLng hanoi = new LatLng(21.0169598, 105.8114176); // tạo location có tọa độ lat, long tương ứng
mMap.addMarker(new MarkerOptions().position(hanoi).title("Marker in HaNoi")); // set marker tại điểm được tao ở trên với nội dung hiển thị khi click vào địa điểm
mMap.moveCamera(CameraUpdateFactory.newLatLng(hanoi));// move camera tới điểm vừa tạo.
}
Ngoài ra bạn cũng có thể config trong code hoặc file json các thông số như :
map:cameraBearing="112.5"
map:cameraTargetLat="21.0169598"
map:cameraTargetLng="105.8114176"
map:cameraTilt="30"
map:cameraZoom="13"
map:mapType="normal"
map:uiCompass="false"
map:uiRotateGestures="true"
map:uiScrollGestures="true"
map:uiTiltGestures="true"
map:uiZoomControls="true"
map:uiZoomGestures="true"
Sau config trên bạn được map như sau, chú ý ở phần góc dưới bên phải đã xuất hiện thanh zoom map.
Vẽ đường đi giữa 2 điểm trên map.
Với file MapsActivity.java được khởi tạo ban đầu ta tạo thêm 2 file mới như sau : GMapV2Direction.java
public class GMapV2Direction {
public final static String MODE_DRIVING = "driving";
public final static String MODE_WALKING = "walking";
public GMapV2Direction() {
}
public Document getDocument(LatLng start, LatLng end, String mode) {
String url = "http://maps.googleapis.com/maps/api/directions/xml?"
+ "origin=" + start.latitude + "," + start.longitude
+ "&destination=" + end.latitude + "," + end.longitude
+ "&sensor=false&units=metric&mode=" + mode;
try {
HttpClient httpClient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost, localContext);
InputStream in = response.getEntity().getContent();
DocumentBuilder builder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
Document doc = builder.parse(in);
return doc;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String getDurationText(Document doc) {
NodeList nl1 = doc.getElementsByTagName("duration");
Node node1 = nl1.item(nl1.getLength() - 1);
NodeList nl2 = node1.getChildNodes();
Node node2 = nl2.item(getNodeIndex(nl2, "text"));
Log.i("DurationText", node2.getTextContent());
return node2.getTextContent();
}
public int getDurationValue(Document doc) {
NodeList nl1 = doc.getElementsByTagName("duration");
Node node1 = nl1.item(nl1.getLength() - 1);
NodeList nl2 = node1.getChildNodes();
Node node2 = nl2.item(getNodeIndex(nl2, "value"));
Log.i("DurationValue", node2.getTextContent());
return Integer.parseInt(node2.getTextContent());
}
public String getDistanceText(Document doc) {
NodeList nl1 = doc.getElementsByTagName("distance");
Node node1 = nl1.item(nl1.getLength() - 1);
NodeList nl2 = node1.getChildNodes();
Node node2 = nl2.item(getNodeIndex(nl2, "text"));
Log.i("DistanceText", node2.getTextContent());
return node2.getTextContent();
}
public int getDistanceValue(Document doc) {
NodeList nl1 = doc.getElementsByTagName("distance");
Node node1 = nl1.item(nl1.getLength() - 1);
NodeList nl2 = node1.getChildNodes();
Node node2 = nl2.item(getNodeIndex(nl2, "value"));
Log.i("DistanceValue", node2.getTextContent());
return Integer.parseInt(node2.getTextContent());
}
public String getStartAddress(Document doc) {
NodeList nl1 = doc.getElementsByTagName("start_address");
Node node1 = nl1.item(0);
Log.i("StartAddress", node1.getTextContent());
return node1.getTextContent();
}
public String getEndAddress(Document doc) {
NodeList nl1 = doc.getElementsByTagName("end_address");
Node node1 = nl1.item(0);
Log.i("StartAddress", node1.getTextContent());
return node1.getTextContent();
}
public String getCopyRights(Document doc) {
NodeList nl1 = doc.getElementsByTagName("copyrights");
Node node1 = nl1.item(0);
Log.i("CopyRights", node1.getTextContent());
return node1.getTextContent();
}
public ArrayList<LatLng> getDirection(Document doc) {
NodeList nl1, nl2, nl3;
ArrayList<LatLng> listGeopoints = new ArrayList<LatLng>();
nl1 = doc.getElementsByTagName("step");
if (nl1.getLength() > 0) {
for (int i = 0; i < nl1.getLength(); i++) {
Node node1 = nl1.item(i);
nl2 = node1.getChildNodes();
Node locationNode = nl2
.item(getNodeIndex(nl2, "start_location"));
nl3 = locationNode.getChildNodes();
Node latNode = nl3.item(getNodeIndex(nl3, "lat"));
double lat = Double.parseDouble(latNode.getTextContent());
Node lngNode = nl3.item(getNodeIndex(nl3, "lng"));
double lng = Double.parseDouble(lngNode.getTextContent());
listGeopoints.add(new LatLng(lat, lng));
locationNode = nl2.item(getNodeIndex(nl2, "polyline"));
nl3 = locationNode.getChildNodes();
latNode = nl3.item(getNodeIndex(nl3, "points"));
ArrayList<LatLng> arr = decodePoly(latNode.getTextContent());
for (int j = 0; j < arr.size(); j++) {
listGeopoints.add(new LatLng(arr.get(j).latitude, arr
.get(j).longitude));
}
locationNode = nl2.item(getNodeIndex(nl2, "end_location"));
nl3 = locationNode.getChildNodes();
latNode = nl3.item(getNodeIndex(nl3, "lat"));
lat = Double.parseDouble(latNode.getTextContent());
lngNode = nl3.item(getNodeIndex(nl3, "lng"));
lng = Double.parseDouble(lngNode.getTextContent());
listGeopoints.add(new LatLng(lat, lng));
}
}
return listGeopoints;
}
private int getNodeIndex(NodeList nl, String nodename) {
for (int i = 0; i < nl.getLength(); i++) {
if (nl.item(i).getNodeName().equals(nodename))
return i;
}
return -1;
}
private ArrayList<LatLng> decodePoly(String encoded) {
ArrayList<LatLng> poly = new ArrayList<LatLng>();
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 position = new LatLng((double) lat / 1E5, (double) lng / 1E5);
poly.add(position);
}
return poly;
}
}
GetDirectionsAsyncTask.java . Chú ý trong file chứa : "private WeakReference<MapsActivity> activity" nên để có thể sử dụng cho nhiều màn hình ta có thể tạo 1 base activity, base fragment để sử dụng.
public class GetDirectionsAsyncTask extends
AsyncTask<Map<String, String>, Object, ArrayList> {
public static final String USER_CURRENT_LAT = "user_current_lat";
public static final String USER_CURRENT_LONG = "user_current_long";
public static final String DESTINATION_LAT = "destination_lat";
public static final String DESTINATION_LONG = "destination_long";
public static final String DIRECTIONS_MODE = "directions_mode";
private WeakReference<MapsActivity> activity;
private Exception exception;
// private ProgressDialog progressDialog;
public GetDirectionsAsyncTask(MapsActivity activity) {
super();
this.activity = new WeakReference<MapsActivity>(activity);
}
public void onPreExecute() {
// progressDialog = new ProgressDialog(activity.get().getActivity());
// progressDialog.setMessage("Calculating directions");
// progressDialog.show();
}
@Override
public void onPostExecute(ArrayList result) {
// progressDialog.dismiss();
try {
if (exception == null && result != null && result.size() > 0
&& activity != null) {
activity.get().handleGetDirectionsResult(result);
} else {
processException();
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
@Override
protected ArrayList doInBackground(Map<String, String>... params) {
Map<String, String> paramMap = params[0];
try {
LatLng fromPosition = new LatLng(Double.valueOf(paramMap
.get(USER_CURRENT_LAT)), Double.valueOf(paramMap
.get(USER_CURRENT_LONG)));
LatLng toPosition = new LatLng(Double.valueOf(paramMap
.get(DESTINATION_LAT)), Double.valueOf(paramMap
.get(DESTINATION_LONG)));
GMapV2Direction md = new GMapV2Direction();
Document doc = md.getDocument(fromPosition, toPosition,
paramMap.get(DIRECTIONS_MODE));
ArrayList directionPoints = md.getDirection(doc);
return directionPoints;
} catch (Exception e) {
exception = e;
e.printStackTrace();
return null;
}
}
private void processException() {
// Toast.makeText(activity,
// activity.getString(R.string.error_when_retrieving_data), 3000)
// .show();
}
}
Trong MainActivity.java thêm đoạn code sau :
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Sydney and move the camera
AMSTERDAM = new LatLng(21.0169598, 105.8114176);
mMap.addMarker(new MarkerOptions().position(AMSTERDAM).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(AMSTERDAM));
PARIS = new LatLng(20.9889196,105.8635858);
final GMapV2Direction md = new GMapV2Direction();
// OnGetDataCompleteListener completeListener = null;
findDirections(AMSTERDAM.latitude, AMSTERDAM.longitude,
PARIS.latitude, PARIS.longitude,
GMapV2Direction.MODE_DRIVING);
}
private static LatLng AMSTERDAM;
private static LatLng PARIS;
private Polyline newPolyline;
private LatLngBounds latlngBounds;
public void handleGetDirectionsResult(ArrayList<LatLng> directionPoints) {
PolylineOptions rectLine = new PolylineOptions().width(5).color(
Color.RED);
for (int i = 0; i < directionPoints.size(); i++) {
rectLine.add(directionPoints.get(i));
}
if (newPolyline != null) {
newPolyline.remove();
}
newPolyline = mMap.addPolyline(rectLine);
latlngBounds = createLatLngBoundsObject(AMSTERDAM, PARIS);
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
latlngBounds, 400, 400, 150));
}
private LatLngBounds createLatLngBoundsObject(LatLng firstLocation,
LatLng secondLocation) {
if (firstLocation != null && secondLocation != null) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(firstLocation).include(secondLocation);
return builder.build();
}
return null;
}
public void findDirections(double fromPositionDoubleLat,
double fromPositionDoubleLong, double toPositionDoubleLat,
double toPositionDoubleLong, String mode) {
Map<String, String> map = new HashMap<String, String>();
map.put(GetDirectionsAsyncTask.USER_CURRENT_LAT,
String.valueOf(fromPositionDoubleLat));
map.put(GetDirectionsAsyncTask.USER_CURRENT_LONG,
String.valueOf(fromPositionDoubleLong));
map.put(GetDirectionsAsyncTask.DESTINATION_LAT,
String.valueOf(toPositionDoubleLat));
map.put(GetDirectionsAsyncTask.DESTINATION_LONG,
String.valueOf(toPositionDoubleLong));
map.put(GetDirectionsAsyncTask.DIRECTIONS_MODE, mode);
GetDirectionsAsyncTask asyncTask = new GetDirectionsAsyncTask(this);
asyncTask.execute(map);
// asyncTask.cancel(true);
}
Run app và nhận được kết quả
Khá đơn giản và tiện dụng phải không các bạn. Còn rất nhiều những tính năng có thể phát triển dựa trên 2 tính năng cơ bản này , hi vọng chia sẻ của mình có thể giúp ích cho các bạn.
Thanks you!
All rights reserved