如何在Android中的Google地图V2中绘制自由手形多边形?
我想Free Hand Polygon on the Map in Google Map V2
画一个Free Hand Polygon on the Map in Google Map V2
。
Overlay
地图V1可以完成这个任务,但Google Map已经从V2中删除了这个类。 (根据这个谷歌地图V2有删除覆盖类 )。
Google Map V1绘制自由式多边形的好例子。
在地图V2中,我们可以在Google官方文档的帮助下以编程方式绘制多边形,但是用户应该做什么? 我发现地图V2的不明确的答案
我开始使用简单的Google Map&绘制多边形以编程方式执行此操作,它正常工作,但现在我正在寻找用户如何绘制? 我不想根据多边形上的标记进行绘制。
// Instantiates a new Polygon object and adds points to define a rectangle PolygonOptions rectOptions = new PolygonOptions() .add(new LatLng(37.35, -122.0), new LatLng(37.45, -122.0), new LatLng(37.45, -122.2), new LatLng(37.35, -122.2), new LatLng(37.35, -122.0)); // Get back the mutable Polygon Polygon polygon = myMap.addPolygon(rectOptions);
我在这个主题上做了大量的研究和开发工作,但是没有在Map V2中实现这样一个完美的方法。
一些问题
- 如何在Map V2中绘制自由式多边形(就像我们可以用Map V1做的那样)?
- 是否有任何技巧或替代scheme来实现这一目标? 如果是的话如何?
- 我们能否在地图上绘制触摸事件并绘制多边形?
- 在Map V2中可行吗?
- 触发事件是否可能返回lat-long数组?
- 如何根据
setOnDragListener
上的屏幕坐标获得Lat-long?
每个新版本都有一些额外的东西,所以我期待着我可以在Map v2中实现同样的function。
我不是要求给我一些示例代码或发布您的代码,只是一些正确的方向和文件。
我提供了我在研发过程中发现的所有文件和证据。
在Rnd花了一整天的时间,并testing了一些替代品后,我find了一个解决scheme。 其实我已经find了两个同样的问题的替代scheme,但是我想build议使用scheme2,因为与scheme1相比,这非常容易。
其实我已经在TheLittleNaruto , AndroidHacker和一些其他开发人员和Alternative 2的帮助下,在Khan的帮助下find了Alternative 1 。
替代scheme1
如何在Map V2中绘制自由式多边形(就像我们可以用Map V1做的那样)? 在Map V2中可行吗?
是的,这是可行的,但你不能直接在地图上的OnTouch()
和OnDraw()
。 所以我们必须想办法来达到这个目的。
有没有什么诀窍或替代方法来实现这件事情,如果是的话,怎么样?
是的,Google地图V2不支持使用class="com.google.android.gms.maps.SupportMapFragment"
的地图上的OnTouch()
或OnDraw()
,所以我们必须规划一个定制的片段。
是否有可能返回触摸事件经纬度数组?
是的,如果我们创build任何自定义地图片段并使用它,我们可以在地图上获取触摸或拖动事件。
如何在setOnDragListener的屏幕坐标上获得Lat-long?
setOnDragListener
将返回屏幕坐标(x,y) 。 现在,有一些技术将(x,y)转换为LatLng,并包括Projection和Point & LatLng 。
customMapFragment.setOnDragListener(new MapWrapperLayout.OnDragListener() {@Override public void onDrag(MotionEvent motionEvent) { Log.i("ON_DRAG", "X:" + String.valueOf(motionEvent.getX())); Log.i("ON_DRAG", "Y:" + String.valueOf(motionEvent.getY())); float x = motionEvent.getX(); // get screen x position or coordinate float y = motionEvent.getY(); // get screen y position or coordinate int x_co = Integer.parseInt(String.valueOf(Math.round(x))); // casting float to int int y_co = Integer.parseInt(String.valueOf(Math.round(y))); // casting float to int projection = mMap.getProjection(); // Will convert your x,y to LatLng Point x_y_points = new Point(x_co, y_co);// accept int x,y value LatLng latLng = mMap.getProjection().fromScreenLocation(x_y_points); // convert x,y to LatLng latitude = latLng.latitude; // your latitude longitude = latLng.longitude; // your longitude Log.i("ON_DRAG", "lat:" + latitude); Log.i("ON_DRAG", "long:" + longitude); // Handle motion event: } });
它是如何工作的 ?
正如我之前已经提到的,我们必须创build一个自定义的根视图,并使用它,我们可以在地图上获得触摸或拖动事件。
第1步:我们创buildMySupportMapFragment extends SupportMapFragment
,我们将使用它作为我们的.xml文件
<fragment android:id="@+id/map" android:layout_width="fill_parent" android:layout_height="fill_parent" class="pkg_name.MySupportMapFragment" />
第2步:创build一个MapWrapperLayout extends FrameLayout
以便我们可以设置一个Touch或Drag侦听器 ,并用地图视图embedded其视图。 所以,我们需要一个我们将在Root_Map.java中使用的接口
MySupportMapFragment.Java
public class MySupportMapFragment extends SupportMapFragment { public View mOriginalContentView; public MapWrapperLayout mMapWrapperLayout; @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState); mMapWrapperLayout = new MapWrapperLayout(getActivity()); mMapWrapperLayout.addView(mOriginalContentView); return mMapWrapperLayout; } @Override public View getView() { return mOriginalContentView; } public void setOnDragListener(MapWrapperLayout.OnDragListener onDragListener) { mMapWrapperLayout.setOnDragListener(onDragListener); } }
MapWrapperLayout.java
public class MapWrapperLayout extends FrameLayout { private OnDragListener mOnDragListener; public MapWrapperLayout(Context context) { super(context); } public interface OnDragListener { public void onDrag(MotionEvent motionEvent); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mOnDragListener != null) { mOnDragListener.onDrag(ev); } return super.dispatchTouchEvent(ev); } public void setOnDragListener(OnDragListener mOnDragListener) { this.mOnDragListener = mOnDragListener; } }
Root_Map.Java
public class Root_Map extends FragmentActivity { private GoogleMap mMap; public static boolean mMapIsTouched = false; MySupportMapFragment customMapFragment; Projection projection; public double latitude; public double longitude; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.root_map); MySupportMapFragment customMapFragment = ((MySupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)); mMap = customMapFragment.getMap(); customMapFragment.setOnDragListener(new MapWrapperLayout.OnDragListener() { @Override public void onDrag(MotionEvent motionEvent) { Log.i("ON_DRAG", "X:" + String.valueOf(motionEvent.getX())); Log.i("ON_DRAG", "Y:" + String.valueOf(motionEvent.getY())); float x = motionEvent.getX(); float y = motionEvent.getY(); int x_co = Integer.parseInt(String.valueOf(Math.round(x))); int y_co = Integer.parseInt(String.valueOf(Math.round(y))); projection = mMap.getProjection(); Point x_y_points = new Point(x_co, y_co); LatLng latLng = mMap.getProjection().fromScreenLocation(x_y_points); latitude = latLng.latitude; longitude = latLng.longitude; Log.i("ON_DRAG", "lat:" + latitude); Log.i("ON_DRAG", "long:" + longitude); // Handle motion event: } }); }}
参考链接1,链接2
到这里,我可以基于X,Y屏幕坐标获得LatLong 。 现在我只需要将其存储在Array中 。 该数组将用于在地图上绘图,最后它将看起来像一个自由形状的多边形。
我希望这一定会帮助你。
更新:
替代2
我们知道, 框架布局是一个透明的布局,所以我已经实现了使用框架布局。 在这种情况下,不需要创build自定义片段。 我刚刚使用了框架布局作为根布局。 所以基本上我会在根布局中获得Touch Events ,并且会返回屏幕坐标,就像我们之前在自定义片段中获得的一样。
现在,我在“Free Draw”中创build了一个Button。 所以当你点击的时候,你可以在地图上移动你的手指并画出一个自由的多边形,这将禁止你的地图在屏幕上移动。 当你重新点击相同的button时,屏幕进入理想模式。
root_map.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <fragment android:id="@+id/map" android:layout_width="fill_parent" android:layout_height="fill_parent" class="com.google.android.gms.maps.SupportMapFragment" /> <FrameLayout android:id="@+id/fram_map" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/btn_draw_State" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Free Draw" /> </FrameLayout> </FrameLayout>
Root_Map.java
FrameLayout fram_map = (FrameLayout) findViewById(R.id.fram_map); Button btn_draw_State = (Button) findViewById(R.id.btn_draw_State); Boolean Is_MAP_Moveable = false; // to detect map is movable
//button会改变地图的可移动状态
btn_draw_State.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if (Is_MAP_Moveable != true) { Is_MAP_Moveable = true; } else { Is_MAP_Moveable = false; } } });
触摸框架布局的点击和做一些任务的帮助
fram_map.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { float x = event.getX(); float y = event.getY(); int x_co = Math.round(x); int y_co = Math.round(y); projection = mMap.getProjection(); Point x_y_points = new Point(x_co, y_co); LatLng latLng = mMap.getProjection().fromScreenLocation(x_y_points); latitude = latLng.latitude; longitude = latLng.longitude; int eventaction = event.getAction(); switch (eventaction) { case MotionEvent.ACTION_DOWN: // finger touches the screen val.add(new LatLng(latitude, longitude)); case MotionEvent.ACTION_MOVE: // finger moves on the screen val.add(new LatLng(latitude, longitude)); case MotionEvent.ACTION_UP: // finger leaves the screen Draw_Map(); break; } if (Is_MAP_Moveable == true) { return true; } else { return false; } } });
//绘制你的地图
public void Draw_Map() { rectOptions = new PolygonOptions(); rectOptions.addAll(val); rectOptions.strokeColor(Color.BLUE); rectOptions.strokeWidth(7); rectOptions.fillColor(Color.CYAN); polygon = mMap.addPolygon(rectOptions); }
然而,现在你必须在画画的时候保留你的列表,所以你必须清除以前的列表数据。
看看这个..我相信你能够显示谷歌地图V2
在AsyncTask中查看“decodePoly”和“drawPath”方法
主要关注“drawPath”
PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true); for (int z = 0; z < list.size(); z++) { LatLng point = list.get(z); options.add(point); } line = myMap.addPolyline(options);
完整的课程供您参考
package com.example.androidhackergooglemap; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONObject; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; 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.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.location.Location; import android.location.LocationManager; import android.os.AsyncTask; import android.os.Bundle; import android.provider.Settings; import android.support.v4.app.FragmentActivity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Toast; public class MainActivity extends FragmentActivity implements OnClickListener { private GoogleMap myMap; Polyline line; Context context; Location location; boolean check_provider_enabled = false; // Static LatLng LatLng startLatLng = new LatLng(30.707104, 76.690749); LatLng endLatLng = new LatLng(30.721419, 76.730017); public void onCreate(Bundle bd) { super.onCreate(bd); setContentView(R.layout.activity_main); context = MainActivity.this; // GoogleMap myMap myMap = ((SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map)).getMap(); myMap.setMyLocationEnabled(true); myMap.moveCamera(CameraUpdateFactory.newLatLng(startLatLng)); myMap.animateCamera(CameraUpdateFactory.zoomTo(12)); LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE); boolean enabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER); location = service.getLastKnownLocation(LocationManager.GPS_PROVIDER); // check if enabled and if not send user to the GSP settings // Better solution would be to display a dialog and suggesting to // go to the settings if (!enabled) { /*Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(intent);*/ Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(intent); Toast.makeText(getApplicationContext(), "Enable GPS servcies to use this app.", Toast.LENGTH_LONG).show(); } else{ try{ String urlTopass = makeURL(startLatLng.latitude, startLatLng.longitude, endLatLng.latitude, endLatLng.longitude); new connectAsyncTask(urlTopass).execute(); }catch(Exception e){ e.printStackTrace(); } } // Now auto clicking the button // btntemp.performClick(); } private class connectAsyncTask extends AsyncTask<Void, Void, String> { private ProgressDialog progressDialog; String url; connectAsyncTask(String urlPass) { url = urlPass; } @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); progressDialog = new ProgressDialog(context); progressDialog.setMessage("Fetching route, Please wait..."); progressDialog.setIndeterminate(true); progressDialog.show(); } @Override protected String doInBackground(Void... params) { JSONParser jParser = new JSONParser(); String json = jParser.getJSONFromUrl(url); return json; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); progressDialog.hide(); if (result != null) { drawPath(result); } } } public String makeURL(double sourcelat, double sourcelog, double destlat, double destlog) { StringBuilder urlString = new StringBuilder(); urlString.append("http://maps.googleapis.com/maps/api/directions/json"); urlString.append("?origin=");// from urlString.append(Double.toString(sourcelat)); urlString.append(","); urlString.append(Double.toString(sourcelog)); urlString.append("&destination=");// to urlString.append(Double.toString(destlat)); urlString.append(","); urlString.append(Double.toString(destlog)); urlString.append("&sensor=false&mode=driving&alternatives=true"); return urlString.toString(); } public class JSONParser { InputStream is = null; JSONObject jObj = null; String json = ""; // constructor public JSONParser() { } public String getJSONFromUrl(String url) { // Making HTTP request try { // defaultHttpClient DefaultHttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); HttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); is = httpEntity.getContent(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { BufferedReader reader = new BufferedReader( new InputStreamReader(is, "iso-8859-1"), 8); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } json = sb.toString(); is.close(); } catch (Exception e) { Log.e("Buffer Error", "Error converting result " + e.toString()); } return json; } } public void drawPath(String result) { if (line != null) { myMap.clear(); } myMap.addMarker(new MarkerOptions().position(endLatLng).icon( BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher))); myMap.addMarker(new MarkerOptions().position(startLatLng).icon( BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher))); try { // Tranform the string into a json object final JSONObject json = new JSONObject(result); JSONArray routeArray = json.getJSONArray("routes"); JSONObject routes = routeArray.getJSONObject(0); JSONObject overviewPolylines = routes .getJSONObject("overview_polyline"); String encodedString = overviewPolylines.getString("points"); List<LatLng> list = decodePoly(encodedString); PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true); for (int z = 0; z < list.size(); z++) { LatLng point = list.get(z); options.add(point); } line = myMap.addPolyline(options); /*for (int z = 0; z < list.size() - 1; z++) { LatLng src = list.get(z); LatLng dest = list.get(z + 1); line = myMap.addPolyline(new PolylineOptions() .add(new LatLng(src.latitude, src.longitude), new LatLng(dest.latitude, dest.longitude)) .width(5).color(Color.BLUE).geodesic(true)); }*/ } catch (Exception e) { e.printStackTrace(); } } private List<LatLng> decodePoly(String encoded) { List<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 p = new LatLng((((double) lat / 1E5)), (((double) lng / 1E5))); poly.add(p); } return poly; } @Override public void onClick(View arg0) { // TODO Auto-generated method stub } }
希望能帮助到你。 干杯!
UPDATE
看看这个.. 创buildOnDragListener的谷歌地图V2碎片
也检查这一个.. 如何通过使用谷歌地图V2触摸它在地图片段上绘制一个形状
一些更多的参考.. 如何从谷歌地图V2 Android的标记获取屏幕坐标
所以我们在地图v2上有一些自由手绘的解决scheme。 在您的地图活动中实施GoogleMap.OnMarkerDragListener
。 它会覆盖onMarkerDrag函数。
@Override public void onMarkerDrag(Marker marker) { //add the marker's latlng in a arraylist of LatLng and pass it to the loop for (int i = 0; i < arraylistoflatlng.size(); i++) { myMap.addPolyline(new PolylineOptions() .addAll(arraylistoflatlng) .width(5) .color(Color.RED)); } }
你可以通过一些类似的黑客手段,只要用户将触摸地图,你必须检测该坐标,并将其传递给onMarkerDrag。 因为您必须使用该区域的信息进行进一步处理。 对于触摸事件,您可以实现GoogleMap.OnMapClickListener
并从其参数中获取坐标。 希望这会帮助:)
这是谷歌地图API V2教程:
public class MapPane extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.map_activity); GoogleMap map = ((MapFragment) getFragmentManager() .findFragmentById(R.id.map)).getMap(); map.moveCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(-18.142, 178.431), 2)); // Polylines are useful for marking paths and routes on the map. map.addPolyline(new PolylineOptions().geodesic(true) .add(new LatLng(-33.866, 151.195)) // Sydney .add(new LatLng(-18.142, 178.431)) // Fiji .add(new LatLng(21.291, -157.821)) // Hawaii .add(new LatLng(37.423, -122.091)) // Mountain View ); } }
链接: https : //developers.google.com/maps/documentation/android/