您的位置:首页 > 移动开发 > Android开发

Android地图开发:自定义地图弹出框内容

2012-04-10 18:08 495 查看
Android地图开发有个很常见的需求,比如地图上绘制一系列图标,点击每个图标,弹出一个气泡,里面是详细内容,有图片、文字等等。

这篇文章讲的是如何自定义一个弹出框,里面包含图片、文字等等,思路是这样的:

我们写一个弹出框的xml布局文件,需要弹出的时候,将这个view转换成bitmap,然后转换成drawable,然后就可以控制这个drawable的位置,比如让它显示在图标的上方,当然可以随意设定弹出的位置。

我们先看看效果图:



地图使用的是图吧的地图,实际上Google Maps也是一样的,API调用方法都一样的,只是图吧地图xml布局里面不需要key。

上图中弹出的气泡时间上是一个LinearLayout,只是将其转换成了drawable,以便使用canvas绘制。

这里我们分成两个部分讲,分成两个类,MapPopupActivity(地图页面)和MapPopupView(弹出框)。

1.首先,我们准备好地图,在此地图上我们将绘制一系列图标,点击图标时弹出气泡。内容有点多,我会进行注释。

MapPopupActivity.java

import android.content.res.Resources;

import android.graphics.Canvas;

import android.graphics.drawable.Drawable;

import android.os.Bundle;

import com.mapbar.android.maps.*;

import com.tuan800.credit.R;

import com.tuan800.credit.views.MapPopupView;

import java.util.ArrayList;

import java.util.List;

/**

* Created by IntelliJ IDEA.

* User: chenjishi

* Date: 12-4-10

* Time: 下午4:29

* To change this template use File | Settings | File Templates.

*/

public class MapPopupActivity extends MapActivity implements MapPopupView.PopupViewTapListener {

private List<Spot> mSpotList;

private MapView mMapView;

private MapController mMapController;

private List<Overlay> mOverlayList;

private OverItemT mOverItemT;

//弹出窗口类

private MapPopupView mMapPopupView;

//设定一个地图中心位置

private double mLat = 39.973860D;

private double mLng = 116.382240D;

private Resources mRes;

@Override

protected void onCreate(Bundle bundle) {

super.onCreate(bundle);

setContentView(R.layout.map_view);

mMapView = (MapView) this.findViewById(R.id.map_view);

mMapView.setBuiltInZoomControls(true);

mMapController = mMapView.getController();

mMapController.setZoom(12);

mRes = getResources();

//默认图标

Drawable defaultIcon = mRes.getDrawable(R.drawable.my_location);

defaultIcon.setBounds(0, 0, defaultIcon.getIntrinsicWidth(), defaultIcon.getIntrinsicHeight());

//绘制图标的内部类

mOverItemT = new OverItemT(defaultIcon);

//初始化弹出窗

mMapPopupView = new MapPopupView(this, mRes.getDrawable(R.drawable.category_1));

//处理弹出窗的点击事件

mMapPopupView.setPopupViewListener(this);

mOverlayList = mMapView.getOverlays();

//下面这两句很重要,不然图标无法显示

mOverlayList.add(mOverItemT);

mOverlayList.add(mMapPopupView);

//设置地图中心点

setMyLocation();

//初始化一系列图标

setData();

//往地图上绘制图标

setMarkers();

}

private void setMyLocation() {

GeoPoint p = new GeoPoint((int) (mLat * 1E6), (int) (mLng * 1E6));

mOverItemT.addOverlay(new OverlayItem(p, "我的位置", ""), 0);

mMapController.setCenter(p);

}

private void setMarkers() {

if (mSpotList.size() == 0) {

return;

}

for (Spot spot : mSpotList) {

GeoPoint point = new GeoPoint((int) (spot.lat * 1E6), (int) (spot.lng * 1E6));

OverlayItem item = new OverlayItem(point, spot.title, spot.detail);

mOverItemT.addOverlay(item, spot.categoryId);

}

}

@Override

public void onPopupViewTap() {

//点击弹窗时让其消失

mMapPopupView.dismiss();

}

//内部类,负责图标的绘制

class OverItemT extends ItemizedOverlay<OverlayItem> {

//每一个OverlayItem就是一个图标

ArrayList<OverlayItem> itemList = new ArrayList<OverlayItem>();

public OverItemT(Drawable drawable) {

super(boundCenterBottom(drawable));

}

public void addOverlay(OverlayItem item, int idx) {

if (item == null) return;

//这里我们根据spot的id分配不同的图标

Drawable icon;

int iconIdx = R.drawable.my_location;

if (idx >= 1 && idx <= 4) {

iconIdx = mRes.getIdentifier("drawable/category_" + idx, null, getPackageName());

}

icon = mRes.getDrawable(iconIdx);

item.setMarker(icon);

boundCenterBottom(icon);

itemList.add(item);

populate();

}

@Override

protected OverlayItem createItem(int i) {

return itemList.get(i);

}

@Override

public int size() {

return itemList.size();

}

//这里负责图标点击事件,如果点击的是当前位置,不弹窗,当前位置i=0,每一次点击时,我们将其他的弹窗关掉,本类中只存在一个弹窗类对象。

@Override

protected boolean onTap(int i) {

mMapPopupView.dismiss();

if (i == 0) return true;

OverlayItem item = itemList.get(i);

setFocus(item);

//int数组2,3是弹窗图标索引,这里我们将在弹窗里面绘制两个图标

mMapPopupView.addOverlay(item, new int[]{2, 3});

mMapController.animateTo(item.getPoint());

return true;

}

//这里是点击图标之外的区域,让已经显示的弹窗消失。

@Override

public boolean onTap(GeoPoint geoPoint, MapView mapView) {

if (!super.onTap(geoPoint, mapView)) {

mMapPopupView.dismiss();

}

return true;

}

//super.draw(canvas, mapView, false);false代表不绘制阴影

@Override

public void draw(Canvas canvas, MapView mapView, boolean b) {

super.draw(canvas, mapView, false);

}

}

private void setData() {

mSpotList = new ArrayList<Spot>();

String[] names = {

"宏状元粥店(健德门店)",

"非尝羊蝎子(马甸店)",

"非尝羊蝎子婆婆鱼",

"天丽姿美容(双安店",

"牛魔王拉面(健德门店)",

"福美炫彩电玩台球俱乐部",

"福美炫彩电玩城",

"华乐文化艺术培训中心(马甸校区)",

"清香阁(健德门桥店)",

"芭斯罗缤(北辰世纪店)",

"北京雅谛思艺术培训",

"食尚芭萨西饼屋",

"清香阁(健德门桥店)",

"非尝羊蝎子婆婆鱼"

};

String[] lats = {

"39.973831",

"39.97335",

"39.97625",

"39.975761",

"39.976719",

"39.975848",

"39.975761",

"39.976719",

"39.927289",

"39.976719",

"39.97656",

"39.976586",

"39.972751",

"39.977435"

};

String[] lngs = {

"116.381906",

"116.383853",

"116.38121",

"116.379047",

"116.379857",

"116.380286",

"116.379047",

"116.379857",

"116.291042",

"116.379857",

"116.37946",

"116.385382",

"116.386314",

"116.386266"

};

for (int i = 0; i < lngs.length; i++) {

Spot spot = new Spot();

spot.title = names[i];

spot.lat = Double.parseDouble(lats[i]);

spot.lng = Double.parseDouble(lngs[i]);

spot.categoryId = (i % 4) + 1;

spot.detail = "仅售9元,新辣道梭边鱼随你吃!新辣道+拉手网!本单所售全部款项将全部捐给芒果V基金,用于四川凉山贫困儿童救助事业,全国47家店通用,亲,只有抢到了才有机会喔!须为新辣道会员,每桌1名会员即可,如无会员卡,可在门店办理,4月2、3、4、7、8、14、15日拉手券不可使用!锅底需到店另付!";

mSpotList.add(spot);

}

}

private class Spot {

public String title;

public String detail;

public double lat;

public double lng;

public int categoryId;

}

}

2.弹窗类,首先inflat出弹窗内容的view,根据overlayitem的title,detail,设置文字,根据传进来的int数组动态生成图片。

public class MapPopupView extends ItemizedOverlay<OverlayItem> {

OverlayItem mOverlayItem;

Paint mPaint;

//图标的高度,这样我们就可以将气泡偏移这个高度,让气泡放置在图标之上而不遮盖图标

int mIconHeight;

//气泡的xml布局,稍后我们会放在后面贴上

LinearLayout mPopupView;

TextView mPopTitle;

TextView mPopDetail;

LinearLayout mIcons;

Context mContext;

PopupViewTapListener listener;

public MapPopupView(Context context, Drawable drawable) {

super(boundCenterBottom(drawable));

mContext = context;

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

mPopupView = (LinearLayout) inflater.inflate(R.layout.map_popup_view, null);

mPopTitle = (TextView) mPopupView.findViewById(R.id.tv_popup_title);

mPopDetail = (TextView) mPopupView.findViewById(R.id.tv_popup_detail);

mIcons = (LinearLayout) mPopupView.findViewById(R.id.ll_popup_icon);

mPaint = new Paint();

mPaint.setAntiAlias(true);

mIconHeight = drawable.getIntrinsicHeight();

populate();

}

public void setPopupViewListener(PopupViewTapListener listener) {

this.listener = listener;

}

@Override

protected OverlayItem createItem(int i) {

return mOverlayItem;

}

@Override

public int size() {

return mOverlayItem == null ? 0 : 1;

}

public void addOverlay(OverlayItem item, int[] bankIds) {

if (item == null) return;

//设置标题

mPopTitle.setText(item.getTitle());

//生成银行icon

if (bankIds != null && bankIds.length > 0) {

mIcons.setVisibility(View.VISIBLE);

mIcons.removeAllViews();

for (int i : bankIds) {

int iconIdx = mContext.getResources().getIdentifier("drawable/bank_" + i, null, mContext.getPackageName());

ImageView iv = new ImageView(mContext);

iv.setImageResource(iconIdx);

mIcons.addView(iv, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

}

}

//设置内容,为了防止内容过长,我们让文字这行,显示15个字,两行就够了

String detail = item.getSnippet();

if (!TextUtils.isEmpty(detail)) {

mPopDetail.setVisibility(View.VISIBLE);

StringBuilder sb = new StringBuilder();

if (detail.length() >= 15) {

sb.append(detail.substring(0, 14)).append("\n");

if (detail.substring(15).length() >= 15) {

sb.append(detail.substring(15, 29));

} else {

sb.append(detail.substring(15));

}

} else {

sb.append(detail);

}

mPopDetail.setText(sb.toString());

}

//准备开始将view转成bitmap

mPopupView.setDrawingCacheEnabled(true);

mPopupView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),

View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

mPopupView.layout(0, 0, mPopupView.getMeasuredWidth(), mPopupView.getMeasuredHeight());

mPopupView.buildDrawingCache();

//转成了bitmap

Bitmap mBitmap = mPopupView.getDrawingCache();

//不过我们还得将bitmap转成drawable,drawAt方法参数不能直接用bitmap

Drawable d = new BitmapDrawable(mBitmap);

//这里很重要哦,我们在这里设定气泡显示的位置,不然的话气泡显示不出来,方法是将气泡左偏移一半,上偏移图标的高度

d.setBounds(-mBitmap.getWidth() / 2, -mBitmap.getHeight() - mIconHeight, mBitmap.getWidth() / 2, -mIconHeight);

mOverlayItem = new OverlayItem(item.getPoint(), "", "");

mOverlayItem.setMarker(d);

//调用populate()方法气泡就开始绘制了,可以认为调用了draw

populate();

}

@Override

public void draw(Canvas canvas, MapView mapView, boolean b) {

if (mOverlayItem == null) return;

//将经纬度转换成地图上的坐标

Projection projection = mapView.getProjection();

Point point = projection.toPixels(mOverlayItem.getPoint(), null);

drawAt(canvas, mOverlayItem.getMarker(0), point.x, point.y, false);

}

//每次弹出一个新的气泡时,将前面的消失掉

public void dismiss() {

mOverlayItem = null;

//这句代码很重要,不然的话每个气泡的内容会一样的,因为先前我们将mPopupView进行了cache,现在释放掉才行。

mPopupView.setDrawingCacheEnabled(false);

setLastFocusedIndex(-1);

populate();

}

@Override

protected boolean onTap(int i) {

listener.onPopupViewTap();

return true;

}

public interface PopupViewTapListener {

public void onPopupViewTap();

}

}

3.附上xml布局

map_view.xml

<?xml version="1.0" encoding="utf-8"?>

<com.mapbar.android.maps.MapView

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/map_view"

android:layout_width="fill_parent"

android:layout_height="match_parent"

android:clickable="true"/>

map_popup_view.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:orientation="vertical"

android:background="@drawable/map_overlay_item_bg"

android:paddingLeft="8dp"

android:paddingRight="8dp"

>

<TextView

android:id="@+id/tv_popup_title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:paddingTop="8dp"

android:textSize="16sp"

android:textColor="@color/text_color_regular"

/>

<LinearLayout

android:id="@+id/ll_popup_icon"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:visibility="gone"

>

</LinearLayout>

<TextView

android:id="@+id/tv_popup_detail"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:visibility="gone"

android:paddingBottom="8dp"

android:textSize="14sp"

android:textColor="@color/text_color_light"

/>

</LinearLayout>

图片就不贴了,map_overlay_item_bg就是气泡背景图,.9.png格式的。

有问题大家可以交流交流,不知道谁有更好点的方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐