Lưu trữ

Author Archive

Map và Direction trong Android


Ngày bắt đầu làm quen với lập trình Android, mình có viết một ứng liên quan đến Map, hôm nay lôi ra nghịch thử thấy nó không hoạt động nữa chẳng hiểu tại sao. Tìm hiểu nguyên nhân mới biết rằng google không còn support KML direction data nữa. Đang rảnh rỗi nên ngồi thử viết lại và tìm hiểu thêm về cái Map trong Android.
Trong bài đọc này mình sẽ giới thiệu với các bạn cách hiển thị map trên thiết bị Android và tìm đường từ vị trí hiện tại tới một vị trí khác cần đến. Dưới đâylà screenshot của màn hình hiển thị map trong ứng dụng (marker màu xanh giả lập vị trí hiện tại, các markers màu đỏ giả lập các vị trí cần tới)
maptest1
Bạn chọn một vị trí trên bản đồ (marker màu đỏ) bằng cách chạm vào vị trí đó, ứng dụng sẽ tìm và vẽ đường từ vị trí hiện tại tới vị trí mà bạn vừa chọn (đường đi hiển thị bằng đường màu xanh lá cây) như hình sau:
maptest2
Sau đây là phần code cho ứng dụng vừa mô tả. Trước hết để ứng dụng hoạt động được, các bạn cần khai báo những permissions sau trong file AndroidManifest.xml:
android.permission.INTERNET”
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_NETWORK_STATE

1. Layout: my_map.xlm
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android"
android:id=”@+id/map_screen” android:orientation=”vertical”
android:layout_width=”fill_parent” android:layout_height=”fill_parent”>
<com.google.android.maps.MapView
android:id=”@+id/mapview”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:gravity=”center”
android:clickable=”true”
android:apiKey=”your_google_apimap_key” />
</LinearLayout>

Trong file layout trên, các bạn chú ý cần đăng ký với google để lấy mapapi key. Để có google mapapi key, ta cần phải tạo MD5 fingerprint, mình sẽ hướng dẫn các bạn tạo MD5 fingerprint với debugkeystore vì nó đã có sẵn nên ta bỏ qua bước tạo keystore cho đỡ loằng ngoằng mất thời gian. Debug keystore trên máy mình có đường dẫn như dưới đây, các bạn tự tìm trên máy của các bạn.
C:\Documents and Settings\Administrator\.android\debug.keystore
Bây giờ các bạn bật command line console lên và change directory tới thư mục chứa keystore và dùng jdk keytool để tạo MD5 fingerprint như hình dưới đây:

fingerprint
Sau khi đã có MD5 fingerprint các bạn vào đây để Generate API Key.

2. Các Activity và Classes trong ứng dụng:
MyMapActivity.java
public class MyMapActivity extends MapActivity {
MapView mapView;
MapController mapController;
LocationListener locListener;
Button btn_back, btn_search, btn_config;
LinearLayout mainScreen;
ProgressDialog pd;

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.my_map);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
mapController.setZoom(14);
doLoadMap();
}

@Override
protected boolean isRouteDisplayed() {
return false;
}

private void doLoadMap() {
Context context = getApplicationContext();
int latDegree = 0;
int longDegree = 0;
int numberOfPlaces = 10;
try {
Drawable redMarker = getResources().getDrawable(
R.drawable.redmarker);
MyItemOverlay mainMapOverlay = new MyItemOverlay(
redMarker, context);
//hardcode the curent latitude and longitude, in real world should get it from where we saved them
double currentLatitude = 21.0325;
double currentLongitude = 105.8492;
try {
latDegree = (int) (currentLatitude * 1E6);
longDegree = (int) (currentLongitude * 1E6);
} catch (NumberFormatException ex) {
Log.v(“EXCEPTION”, ex.toString());
}
// Provide some default locations and these to the map for testing purpose
for (int i = 1; i <= numberOfPlaces; i++) {
double itemLat = 0.0;
double itemLong = 0.0;
if(i%2 == 0){
itemLat = currentLatitude + (i * 0.001);
itemLong = currentLongitude – (i * 0.002);
}else{
itemLat = currentLatitude;
itemLong = currentLongitude – (i * 0.003);
}
GeoPoint geoPoint = new GeoPoint((int) (itemLat * 1E6),
(int) (itemLong * 1E6));
OverlayItem overlayItem = new OverlayItem(geoPoint, “”, “”);
mainMapOverlay.addOverlay(overlayItem);

}
Drawable greenMarker = getResources().getDrawable(
R.drawable.greenandroidmarker);
MyItemOverlay mainMapOverlayCurrent = new MyItemOverlay(
greenMarker, context);
GeoPoint myGeoPoint = new GeoPoint(latDegree, longDegree);
OverlayItem overlayItem = new OverlayItem(myGeoPoint, “Hello”,
“You are here”);
mainMapOverlayCurrent.addOverlay(overlayItem);
List mapOverlayList = mapView.getOverlays();
mapOverlayList.clear();
mapOverlayList.add(mainMapOverlay);
mapOverlayList.add(mainMapOverlayCurrent);
mapController.animateTo(myGeoPoint);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
Class Segment.java
public class Segment {
/** Points in this segment. **/
private GeoPoint start;
/** Turn instruction to reach next segment. **/
private String instruction;
/** Length of segment. **/
private int length;
/** Distance covered. **/
private double distance;

public Segment() {
}
public void setInstruction(final String turn) {
this.instruction = turn;
}
public String getInstruction() {
return instruction;
}
public void setPoint(final GeoPoint point) {
start = point;
}
public GeoPoint startPoint() {
return start;
}

/** Creates a segment which is a copy of this one.
* @return a Segment that is a copy of this one.
*/
public Segment copy() {
final Segment copy = new Segment();
copy.start = start;
copy.instruction = instruction;
copy.length = length;
copy.distance = distance;
return copy;
}
public void setLength(final int length) {
this.length = length;
}
public int getLength() {
return length;
}
public void setDistance(double distance) {
this.distance = distance;
}
public double getDistance() {
return distance;
}
}
Class Route.java
public class Route {
private String name;
private final List points;
private List segments;
private String copyright;
private String warning;
private String country;
private int length;
private String polyline;

public Route() {
points = new ArrayList();
segments = new ArrayList();
}
public void addPoint(final GeoPoint p) {
points.add(p);
}
public void addPoints(final List points) {
this.points.addAll(points);
}
public List getPoints() {
return points;
}
public void addSegment(final Segment s) {
segments.add(s);
}
public List getSegments() {
return segments;
}
public void setName(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setCopyright(String copyright) {
this.copyright = copyright;
}
public String getCopyright() {
return copyright;
}
public void setWarning(String warning) {
this.warning = warning;
}
public String getWarning() {
return warning;
}
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return country;
}
public void setLength(int length) {
this.length = length;
}
public int getLength() {
return length;
}
public void setPolyline(String polyline) {
this.polyline = polyline;
}
public String getPolyline() {
return polyline;
}
}
Class XMLParser.java
public class XMLParser {
protected static final String MARKERS = "markers";
protected static final String MARKER = "marker";
protected URL url;
protected XMLParser(final String url) {
try {
this.url = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
protected InputStream getInputStream() {
try {
return url.openConnection().getInputStream();
} catch (IOException e) {
return null;
}
}
}

Class GoogleParser.java
Google không support KML data nữa vì thế class dùng để parse direction JSON data và decode polyline mà google trả về
public class GoogleParser extends XMLParser{
/** Distance covered. **/
private int distance;

public GoogleParser(String url) {
super(url);
}

/**
* Parses a url pointing to a Google JSON object to a Route object.
*
* @return a Route object based on the JSON object.
*/

public Route parse() {
final String result = convertStreamToString(this.getInputStream());
// Create an empty route
final Route route = new Route();
// Create an empty segment
final Segment segment = new Segment();
try {
// Tranform the string into a json object
final JSONObject json = new JSONObject(result);
// Get the route object
final JSONObject jsonRoute = json.getJSONArray(“routes”)
.getJSONObject(0);
// Get the leg, only one leg as we don’t support waypoints
final JSONObject leg = jsonRoute.getJSONArray(“legs”)
.getJSONObject(0);
// Get the steps for this leg
final JSONArray steps = leg.getJSONArray(“steps”);
// Number of steps for use in for loop
final int numSteps = steps.length();
// Set the name of this route using the start & end addresses
route.setName(leg.getString(“start_address”) + ” to ”
+ leg.getString(“end_address”));
// Get google’s copyright notice (tos requirement)
route.setCopyright(jsonRoute.getString(“copyrights”));
// Get the total length of the route.
route.setLength(leg.getJSONObject(“distance”).getInt(“value”));
// Get any warnings provided (tos requirement)
if (!jsonRoute.getJSONArray(“warnings”).isNull(0)) {
route.setWarning(jsonRoute.getJSONArray(“warnings”)
.getString(0));
}
/*
* Loop through the steps, creating a segment for each one and
* decoding any polylines found as we go to add to the route
* object’s map array. Using an explicit for loop because it is
* faster!
*/
for (int i = 0; i < numSteps; i++) {
// Get the individual step
final JSONObject step = steps.getJSONObject(i);
// Get the start position for this step and set it on the
// segment
final JSONObject start = step.getJSONObject(“start_location”);
final GeoPoint position = new GeoPoint(
(int) (start.getDouble(“lat”) * 1E6),
(int) (start.getDouble(“lng”) * 1E6));
segment.setPoint(position);
// Set the length of this segment in metres
final int length = step.getJSONObject(“distance”).getInt(
“value”);
distance += length;
segment.setLength(length);
segment.setDistance(distance / 1000);
// Strip html from google directions and set as turn instruction
segment.setInstruction(step.getString(“html_instructions”)
.replaceAll(“<(.*?)*>”, “”));
// Retrieve & decode this segment’s polyline and add it to the
// route.
route.addPoints(decodePolyLine(step.getJSONObject(“polyline”)
.getString(“points”)));
// Push a copy of the segment to the route
route.addSegment(segment.copy());
}
} catch (JSONException e) {
Log.e(e.getMessage(), “Google JSON Parser – ” + url);
}
return route;
}

/**
* Convert an inputstream to a string.
*
* @param input
* inputstream to convert.
* @return a String of the inputstream.
*/

private static String convertStreamToString(final InputStream input) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(
input));
final StringBuilder sBuf = new StringBuilder();

String line = null;
try {
while ((line = reader.readLine()) != null) {
sBuf.append(line);
}
} catch (IOException e) {
Log.e(e.getMessage(), “Google parser, stream2string”);
} finally {
try {
input.close();
} catch (IOException e) {
Log.e(e.getMessage(), “Google parser, stream2string”);
}
}
return sBuf.toString();
}

/**
* Decode a polyline string into a list of GeoPoints.
*
* @param poly
* polyline encoded string to decode.
* @return the list of GeoPoints represented by this polystring.
*/

private List decodePolyLine(final String poly) {
int len = poly.length();
int index = 0;
List decoded = new ArrayList();
int lat = 0;
int lng = 0;

while (index < len) {
int b;
int shift = 0;
int result = 0;
do {
b = poly.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 = poly.charAt(index++) – 63;
result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;

decoded.add(new GeoPoint((int) (lat * 1E6 / 1E5),
(int) (lng * 1E6 / 1E5)));
}
return decoded;
}
}
Class MapRouteOverlay.java
public class MapRouteOverlay extends Overlay {
Route mRoute;
ArrayList mPoints;

public MapRouteOverlay(Route route, MapView mv) {
mRoute = route;
if (route.getPoints().size() > 0) {
mPoints = new ArrayList();
mPoints = (ArrayList)route.getPoints();
int moveToLat = (mPoints.get(0).getLatitudeE6() + (mPoints.get(
mPoints.size() – 1).getLatitudeE6() – mPoints.get(0)
.getLatitudeE6()) / 2);
int moveToLong = (mPoints.get(0).getLongitudeE6() + (mPoints.get(
mPoints.size() – 1).getLongitudeE6() – mPoints.get(0)
.getLongitudeE6()) / 2);
GeoPoint moveTo = new GeoPoint(moveToLat, moveToLong);
MapController mapController = mv.getController();
mapController.animateTo(moveTo);
//mapController.setZoom(14);
}
}

@Override
public boolean draw(Canvas canvas, MapView mv, boolean shadow, long when) {
super.draw(canvas, mv, shadow);
drawPath(mv, canvas);
return true;
}

public void drawPath(MapView mv, Canvas canvas) {
int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
for (int i = 0; i < mPoints.size(); i++) { Point point = new Point(); mv.getProjection().toPixels(mPoints.get(i), point); x2 = point.x; y2 = point.y; if (i > 0) {
canvas.drawLine(x1, y1, x2, y2, paint);
}
x1 = x2;
y1 = y2;
}
}
}
Class MyItemOverlay.java
public class MyItemOverlay extends ItemizedOverlay {
private ArrayList mOverlay = new ArrayList();
Context context;
private Drawable marker = null;
MapView mapView;
private Route mRoute;
double fromLat, fromLong;

public MyItemOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
this.marker = defaultMarker;
}

public MyItemOverlay(Drawable defaultMarker, Context context) {
super(boundCenterBottom(defaultMarker));
this.marker = defaultMarker;
this.context = context;
}

public void addOverlay(OverlayItem item) {
mOverlay.add(item);
populate();
}

@Override
protected OverlayItem createItem(int i) {
return mOverlay.get(i);
}

@Override
public int size() {
return mOverlay.size();
}

@Override
protected boolean onTap(int index) {
return true;
}

@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
final int action = event.getAction();
final int x = (int) event.getX();
final int y = (int) event.getY();
this.mapView = mapView;

if (action == MotionEvent.ACTION_DOWN) {
for (OverlayItem item : mOverlay) {
Point p = new Point(0, 0);
GeoPoint toPoint = mapView.getProjection().fromPixels(x, y);
mapView.getProjection().toPixels(item.getPoint(), p);
if (hitTest(item, marker, x – p.x, y – p.y)) {
//hardcode the curent latitude and longitude, in real world should get it from where we saved them
fromLat = 21.0325;
fromLong = 105.8492;
mRoute = getRoute(new GeoPoint((int)(fromLat*1E6),(int)(fromLong*1E6)), toPoint);
MapRouteOverlay mapOverlay = new MapRouteOverlay(mRoute,
mapView);
List listOfOverlays = mapView.getOverlays();
int size = listOfOverlays.size();
for (int i = 0; i < size; i++) {
if (listOfOverlays.get(i) instanceof MapRouteOverlay) {
listOfOverlays.remove(i);
}
}
listOfOverlays.add(mapOverlay);
mapView.invalidate();
}
}
}
return super.onTouchEvent(event, mapView);
}

private Route getRoute(final GeoPoint start, final GeoPoint dest) {
String jsonURL = “http://maps.googleapis.com/maps/api/directions/json?";
final StringBuffer sBuf = new StringBuffer(jsonURL);
sBuf.append(“origin=”);
sBuf.append(start.getLatitudeE6()/1E6);
sBuf.append(‘,’);
sBuf.append(start.getLongitudeE6()/1E6);
sBuf.append(“&destination=”);
sBuf.append(dest.getLatitudeE6()/1E6);
sBuf.append(‘,’);
sBuf.append(dest.getLongitudeE6()/1E6);
sBuf.append(“&sensor=true&mode=driving”);
GoogleParser parser = new GoogleParser(sBuf.toString());
Route r = parser.parse();
return r;
}
}
Mình sẽ không up source code lên vì hay nghịch bậy nên tài khoản và các file up lên thường bị xóa dẫn đến tình trạng link không down được :), do đó các bạn tự copy các đoạn code trên vào project và chạy thử nhé. Chúc các bạn vui vẻ!

Chuyên mục:Android

Serialize và Deserialize một object trong Android


Đôi lúc ta muốn serialize một object cùng trạng thái các thuộc tính của nó tại một thời điểm nhất định xuống một file và deserialize object đó để lấy được các thuộc tính của object tại thời điểm mà nó được lưu xuống file. Trong Android ta làm như sau:

public void writeObjectToFile(Context context, MyObject myObj){
File file = new File(context.getDir("data", Context.MODE_PRIVATE),"fileName");
ObjectOutputStream outputStream;
try {
outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(myObj);
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public MyObject readObjectFromFile(Context context, String fileName){
MyObject myObj = null;
File file = new File(context.getDir("data", Context.MODE_PRIVATE), fileName);
InputStream instream = null;
try {
instream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
ObjectInputStream ois = new ObjectInputStream(instream);
try {
myObj = (MyObject) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (StreamCorruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return myObj;
}

Chuyên mục:Android

Kiểm tra một thiết bị có phải là Android Tablet và lấy chiều rộng màn hình của một thiết bị Android


1. Code kiểm tra một thiết bị android có phải là Tablet không:

public boolean isTablet(Context context) {
boolean ret = false;
int screenLayout = context.getResources().getConfiguration().screenLayout;
ret = ((screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 4);
return ret;
}

2. Code lấy chiều rộng màn hình một thiết bị Android:

public int getScreenWidth(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int screenWidth = display.getWidth();
return screenWidth;
}

Chuyên mục:Android

Code kiểm tra trạng thái GPS trong Android


Dưới đây là đoạn code để kiểm tra trạng thái GPS trên một thiết bị Android:

LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
boolean statusOfGPS = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);

Lưu ý để kiểm tra được trạng thái GPS trên Android, ta cần khai báo android.permission.ACCESS_FINE_LOCATION trong file AndroidManifest.xml

Chuyên mục:Android

Kiểm tra tình trạng kết nối mạng trong Android


Đây là đoạn code dùng để kiểm tra tình trạng kết nối mạng trong Android:

public boolean isNetworkAvail(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().isAvailable()
&& cm.getActiveNetworkInfo().isConnected()) {
return true;
}
return false;
}

Để đoạn code trên hoạt động, ta cần khai báo các permissions sau trong file AndroidManifest.xml:
android.permission.INTERNET
android.permission.ACCESS_NETWORK_STATE

Chuyên mục:Android