最近项目中需要让用户在 地图上选择 商户的位置,需要有一个图标一直在屏幕的正中央,然后用户拖动地图的时候,动态解析中心点的地址信息,效果图如下:
废话不多说了,直接上代码吧
界面布局文件 select_shoplocation.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <RelativeLayout android:id="@+id/rl_topbar" android:layout_width="match_parent" android:layout_height="@dimen/list_topbar_height" android:background="@drawable/list_bar" > <Button android:id="@+id/bt_back" android:layout_width="35dp" android:layout_height="35dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:layout_marginLeft="@dimen/list_topbar_margin" android:layout_marginRight="@dimen/favorite_op_margin" android:background="@drawable/list_icon_back" /> <TextView android:id="@+id/tv_map_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_centerVertical="true" android:text="@string/know_shop" android:textColor="@color/white" android:textSize="22sp" /> <Button android:id="@+id/bt_save" android:layout_width="35dp" android:layout_height="35dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginLeft="@dimen/list_topbar_margin" android:layout_marginRight="@dimen/favorite_op_margin" android:background="@drawable/icon_ok" /> </RelativeLayout> <RelativeLayout android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" > <com.yulore.yellowpage.view.MyMapView android:id="@+id/mapView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:apiKey="@string/maps_api_key" android:clickable="true" /> <com.yulore.yellowpage.view.MyCenterPoint android:id="@+id/bt_mapCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true"/> </RelativeLayout> <RelativeLayout android:layout_width="fill_parent" android:layout_height="50dp" android:background="#3D3F49" > <TextView android:id="@+id/tv_address" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:textColor="@color/white" android:textSize="13sp" /> </RelativeLayout> </LinearLayout>
这里我自定义了两个View:MyMapView、MyCenterPoint,MyMapView继承自 MapView,因为我们需要拦截触摸事件。
MyMapView.java
import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import com.mapabc.mapapi.core.GeoPoint; import com.mapabc.mapapi.map.MapView; public class MyMapView extends MapView { private OnMapViewChangedListener mListener; public void setOnMapViewChangedListener(OnMapViewChangedListener mListener) { this.mListener = mListener; } public MyMapView(Context arg0, AttributeSet arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } public MyMapView(Context arg0, AttributeSet arg1, int arg2) { super(arg0, arg1, arg2); // TODO Auto-generated constructor stub } public MyMapView(Context arg0, String arg1, String arg2) { super(arg0, arg1, arg2); // TODO Auto-generated constructor stub } public MyMapView(Context arg0, String arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } @Override public boolean onTouchEvent(MotionEvent arg0) { GeoPoint g = this.getProjection().fromPixels(MyCenterPoint.w, MyCenterPoint.h); if(mListener!=null) mListener.onMapViewChanged(g); return super.onTouchEvent(arg0); } /** * 回调接口 * @author FX_SKY * */ public interface OnMapViewChangedListener{ public void onMapViewChanged(GeoPoint point); } }
MyCenterPoint.java
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import com.yulore.yellowpage.R; public class MyCenterPoint extends View { public static int w; public static int h; public static Bitmap mBitmap; public MyCenterPoint(Context context) { super(context); } public MyCenterPoint(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyCenterPoint(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_map); w = this.getWidth() / 2 - mBitmap.getWidth() / 2; h = this.getHeight() / 2 - mBitmap.getHeight() / 2; canvas.drawBitmap(mBitmap, w, h, null); } }
地图界面 SelectLocationActivity
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.location.Address; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.mapabc.mapapi.core.GeoPoint; import com.mapabc.mapapi.core.MapAbcException; import com.mapabc.mapapi.core.OverlayItem; import com.mapabc.mapapi.geocoder.Geocoder; import com.mapabc.mapapi.map.ItemizedOverlay; import com.mapabc.mapapi.map.MapActivity; import com.mapabc.mapapi.map.MapController; import com.mapabc.mapapi.map.MapView; import com.umeng.analytics.MobclickAgent; import com.yulore.yellowpage.http.BaseTask; import com.yulore.yellowpage.http.NetUtil; import com.yulore.yellowpage.http.RequestVo; import com.yulore.yellowpage.parser.ResultParser; import com.yulore.yellowpage.util.CommonUtil; import com.yulore.yellowpage.util.Constant; import com.yulore.yellowpage.util.LogUtil; import com.yulore.yellowpage.util.ThreadPoolManager; import com.yulore.yellowpage.view.MyCenterPoint; import com.yulore.yellowpage.view.MyMapView; import com.yulore.yellowpage.view.MyMapView.OnMapViewChangedListener; /** * 显示店铺位置 界面 * * @author feng bingbing * */ public class SelectLocationActivity extends MapActivity implements OnClickListener { private static final String TAG = SelectLocationActivity.class .getSimpleName(); private MyMapView mMapView; private MapController mMapController; private ProgressDialog mProgressDialog = null; double lat = 0.0f, lng = 0.0f; double oldLat = 0.0f, oldLng = 0.0f; private Geocoder coder;// 逆地理编码 private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case Constant.QUERY_ADDRESS_SUCCESS: address = (String) msg.obj; if (address != null) { tv_address.setText(address.concat("( ") .concat(String.format("%.6f", lat)).concat(",") .concat(String.format("%.6f", lng)).concat(" )")); } break; case Constant.QUERY_ADDRESS_FAILED: Toast.makeText(getApplicationContext(), R.string.map_query_address_failed, 0).show(); break; case Constant.REQUEST_SERVER_SUCCESS: mProgressDialog.dismiss(); String result = msg.obj.toString(); if (result.equals("0")) { Toast.makeText(getApplicationContext(), "您的更改已经提交成功", Toast.LENGTH_SHORT).show(); finish(); overridePendingTransition(R.anim.back_left_in, R.anim.back_right_out); } else { Toast.makeText(getApplicationContext(), "提交失败,请稍后重新提交", Toast.LENGTH_SHORT).show(); } break; default: if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } break; } }; }; private TextView tv_address; private int type; private TextView tv_map_title; private Button bt_save; private Button bt_back; private RelativeLayout rl_topbar; private String address; private String shopId; private MyCenterPoint bt_mapCenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.select_shoplocation); LogUtil.i(TAG, "onCreate"); findView(); Intent intent = getIntent(); if (intent != null) { String location = intent.getStringExtra("location"); if (location != null && location.length() > 0) { String[] arr = location.split(","); lat = Double.parseDouble(arr[0]); lng = Double.parseDouble(arr[1]); oldLat = lat; oldLng = lng; } type = intent.getIntExtra("type", 1); address = intent.getStringExtra("address"); if (address != null) { tv_address.setText(address.concat("( ") .concat(String.valueOf(lat)).concat(",") .concat(String.valueOf(lng)).concat(" )")); } shopId = intent.getStringExtra("shopId"); } LogUtil.i(TAG, "onCreate:" + lat + ">>" + lng); mMapView.setVectorMap(true); mMapView.setBuiltInZoomControls(true); mMapController = mMapView.getController(); coder = new Geocoder(this, getResources().getString( R.string.maps_api_key)); // 根据给定的API Key 构造Geocoder 对象 mMapController.setZoom(15); // 设为当前位置显示 GeoPoint p1 = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); switch (type) { case 1: // 详情 rl_topbar.setBackgroundResource(R.drawable.bar); bt_back.setBackgroundResource(R.drawable.icon_back); tv_map_title.setText("商户位置"); tv_map_title.setTextColor(getResources().getColorStateList( R.color.black)); bt_save.setVisibility(View.GONE); bt_mapCenter.setVisibility(View.GONE); if(lat>0 && lng>0){ newOverlay(lat, lng); }else{ Toast.makeText(getApplicationContext(), "经纬度坐标信息有误", Toast.LENGTH_LONG).show(); } break; case 2: // 纠错 tv_map_title.setText("商户位置错误"); bt_save.setVisibility(View.VISIBLE); mMapController.setCenter(p1); // 设置地图中心点 mMapController.setCenter(p1); // 设置地图中心点 mMapView.setOnMapViewChangedListener(new MyOnMapViewChangedListener()); if(lat>0 && lng>0){ reverseAddress(lat, lng); }else{ Toast.makeText(getApplicationContext(), "经纬度坐标信息有误", Toast.LENGTH_LONG).show(); } break; case 3: // 我知道商户 tv_map_title.setText("请选择商户位置"); bt_save.setVisibility(View.VISIBLE); mMapController.setCenter(p1); // 设置地图中心点 mMapView.setOnMapViewChangedListener(new MyOnMapViewChangedListener()); //初始化 地图中心点 if(lat>0 && lng>0){ reverseAddress(lat, lng); }else{ Toast.makeText(getApplicationContext(), "经纬度坐标信息有误", Toast.LENGTH_LONG).show(); } break; default: break; } } private class MyOnMapViewChangedListener implements OnMapViewChangedListener { @Override public void onMapViewChanged(GeoPoint point) { if (point != null) { lat = (float) (point.getLatitudeE6() / 1E6); lng = (float) (point.getLongitudeE6() / 1E6); // LogUtil.i(TAG, "lat:" + lat + ",lng" + lng); reverseAddress(lat,lng); } } } /** * 根据经纬度 解析地址信息 * @param lat * @param lng */ private void reverseAddress(final double lat, final double lng) { Runnable task = new Runnable() { @Override public void run() { try { List<Address> list = coder.getFromLocation(lat, lng, 3); StringBuffer sb = new StringBuffer(); if (list != null && list.size() > 0) { Address address = list.get(0); String adminArea = address.getAdminArea(); String locality = address.getLocality(); String subLocality = address.getSubLocality(); String feature = address.getFeatureName(); sb.append(adminArea).append(locality) .append(subLocality).append(feature); // LogUtil.e(TAG, "adminArea:" + adminArea // + ",locality:" + locality + ",subLocality:" // + subLocality + ",feature:" + feature); } Message msg = Message.obtain(); msg.what = Constant.QUERY_ADDRESS_SUCCESS; msg.obj = sb.toString(); handler.sendMessage(msg); } catch (MapAbcException e) { e.printStackTrace(); handler.sendEmptyMessage(Constant.QUERY_ADDRESS_FAILED); } } }; ThreadPoolManager.getInstance().addTask(task); } public void onResume() { super.onResume(); MobclickAgent.onResume(this); } public void onPause() { super.onPause(); MobclickAgent.onPause(this); } public void newOverlay(double lat, double lng) { mMapView.getOverlays().clear(); Drawable marker = null; if (type == 1) { marker = getResources().getDrawable(R.drawable.icon_map_local); // 得到需要标在地图上的资源 } else { marker = getResources().getDrawable(R.drawable.icon_map); // 得到需要标在地图上的资源 } marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight()); // 为maker定义位置和边界 mMapView.getOverlays().add(new OverItemT(marker, this)); // 添加ItemizedOverlay实例到mMapView // 设为当前位置显示 GeoPoint p1 = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); // mMapController.animateTo(p1); mMapController.setCenter(p1); // 设置地图中心点 } private void findView() { mMapView = (MyMapView) findViewById(R.id.mapView); bt_back = (Button) findViewById(R.id.bt_back); bt_save = (Button) findViewById(R.id.bt_save); rl_topbar = (RelativeLayout) findViewById(R.id.rl_topbar); bt_mapCenter = (MyCenterPoint) findViewById(R.id.bt_mapCenter); tv_address = (TextView) findViewById(R.id.tv_address); tv_map_title = (TextView) findViewById(R.id.tv_map_title); bt_back.setOnClickListener(this); bt_save.setOnClickListener(this); } /** * 分条目覆盖物。当某个类型的覆盖物,包含多个类型相同、显示方式相同、处理方式相同的项时,使用此类。 */ class OverItemT extends ItemizedOverlay<OverlayItem> { private List<OverlayItem> GeoList = new ArrayList<OverlayItem>(); private Drawable marker; private Context mContext; public OverItemT(Drawable marker, Context context) { super(boundCenterBottom(marker)); this.marker = marker; this.mContext = context; // 用给定的经纬度构造GeoPoint,单位是微度(度 * 1E6) GeoPoint p1 = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); // 构造OverlayItem的三个参数依次为:item的位置,标题文本,文字片段 GeoList.add(new OverlayItem(p1, "", "")); populate(); // createItem(int)方法构造item。一旦有了数据,在调用其它方法前,首先调用这个方法 } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { super.draw(canvas, mapView, false); // 调整一个drawable边界,使得(0,0)是这个drawable底部最后一行中心的一个像素 boundCenterBottom(marker); } @Override protected OverlayItem createItem(int i) { // TODO Auto-generated method stub return GeoList.get(i); } @Override public int size() { // TODO Auto-generated method stub return GeoList.size(); } @Override public boolean onTap(GeoPoint point, MapView mapView) { return super.onTap(point, mapView); } } private HashMap<String, String> toJsonStr() { HashMap<String, String> map = new HashMap<String, String>(); map.put("errortype", "mAddress"); map.put("shopid", shopId); map.put("system", "2"); map.put("version", CommonUtil.getAppVersionCode(getApplicationContext())); map.put("uid", CommonUtil.getDeviceID(getApplicationContext())); map.put("lat", String.valueOf(lat)); map.put("lng", String.valueOf(lng)); return map; } private void postData() { mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage("正在提交..."); mProgressDialog.setIndeterminate(false); mProgressDialog.setMax(100); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); mProgressDialog.setCancelable(false); mProgressDialog.setCanceledOnTouchOutside(false); mProgressDialog.show(); RequestVo requestVo = new RequestVo(); requestVo.context = getApplicationContext(); requestVo.requestUrl = Constant.APP_HOST .concat(Constant.CORRECTION_INFO); requestVo.jsonParser = new ResultParser(); requestVo.requestDataMap = toJsonStr(); requestVo.type = 1; BaseTask task = new BaseTask(getApplicationContext(), requestVo, handler); ThreadPoolManager.getInstance().addTask(task); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bt_back: this.finish(); overridePendingTransition(R.anim.back_left_in, R.anim.back_right_out); break; case R.id.bt_save: if (type == 2) { if (oldLat != lat || oldLng != lng) { if (NetUtil.hasNetwork(getApplicationContext())) { postData(); } else { Toast.makeText(getApplicationContext(), "网络环境不可用", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(getApplicationContext(), "您的更改已经提交成功", Toast.LENGTH_SHORT).show(); finish(); overridePendingTransition(R.anim.back_left_in, R.anim.back_right_out); } } else { Intent data = new Intent(); data.putExtra("address", address); data.putExtra("lat", lat); data.putExtra("lng", lng); setResult(Constant.REQUEST_MAP_ADDRESS, data); this.finish(); overridePendingTransition(R.anim.back_left_in, R.anim.back_right_out); } break; default: break; } } // 处理后退键的情况 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { this.finish(); // finish当前activity // 设置切换动画,从上边进入,下边退出 overridePendingTransition(R.anim.back_left_in, R.anim.back_right_out); return true; } return super.onKeyDown(keyCode, event); } }
这里我使用的是Mapabc地图,其他的像 Baidu、Google 实现方式都是类似的,这里不贴了,有兴趣的同学 自己可以实现。