如何在我的自定义地图图像上绘制小标记图像并保持其位置刷新?- Android

12

我正在开发一个跟踪系统,用于绘制用户在房间或其他地方的移动轨迹。

现在,我已经成功将我的地图导入应用程序中,并且可以自由缩放和移动地图。下一步是在地图上放置标记来指示用户位置,并在地图上移动标记(就像漂浮在地图上一样)。

我在某个地方读到过,认为这可以使用MyLocationOverlay来实现,但是大多数在线示例都是应用于google map。然而,在我的情况下,我的地图不是google map,它是我自己的地图。(地图可以是我的房间的地图,甚至是我自己绘制的地图!)

我已经完成了跟踪算法,即我知道在哪里放置我的标记图像

因此,唯一的问题是如何在地图图像的顶部呈现标记图像。

具体来说,我有一个自定义视图MapView,如下所示:

package com.example.drsystem;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

public class MapView extends View {

    private static final int INVALID_POINTER_ID = -1;

    private Drawable mImage;
    private float mPosX;
    private float mPosY;

    private float mLastTouchX;
    private float mLastTouchY;
    private int mActivePointerId = INVALID_POINTER_ID;

    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;

    public MapView(Context context) {
        this(context, null, 0);

        mImage = getResources().getDrawable(R.drawable.lv12n);
        mImage.setBounds(0, 0, mImage.getIntrinsicWidth(), mImage.getIntrinsicHeight());
    }

    // called when XML tries to inflate this View
    public MapView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);

        mImage = getResources().getDrawable(R.drawable.lv12n);
        mImage.setBounds(0, 0, mImage.getIntrinsicWidth(), mImage.getIntrinsicHeight());
    }

    public MapView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            final float x = ev.getX();
            final float y = ev.getY();

            mLastTouchX = x;
            mLastTouchY = y;
            mActivePointerId = ev.getPointerId(0);
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
            final float x = ev.getX(pointerIndex);
            final float y = ev.getY(pointerIndex);

            // Only move if the ScaleGestureDetector isn't processing a gesture.
            if (!mScaleDetector.isInProgress()) {
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;

                mPosX += dx;
                mPosY += dy;

                invalidate();
            }

            mLastTouchX = x;
            mLastTouchY = y;

            break;
        }

        case MotionEvent.ACTION_UP: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_POINTER_UP: {
            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = ev.getX(newPointerIndex);
                mLastTouchY = ev.getY(newPointerIndex);
                mActivePointerId = ev.getPointerId(newPointerIndex);
            }
            break;
        }
        }

        return true;
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.save();
        Log.d("MapView", "X: " + mPosX + " Y: " + mPosY);
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor);
        mImage.draw(canvas);
        canvas.restore();
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();

            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));

            invalidate();
            return true;
        }
    }

}

到目前为止,地图已成功输入到应用程序中,并且可以移动和缩放。

然后,我将此自定义视图插入到布局XML文件中,如下所示:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MapView" >

    <com.example.drsystem.MapView
        android:id="@+id/mapView1"
        android:layout_width="fill_parent"
        android:layout_height="400dp"
        android:layout_above="@+id/button2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />

 ...

</RelativeLayout>

基于所有这些信息,我该如何在我的地图上绘制一个点并不断更新其位置?
更新:
问题就是关于将另一幅小图片放置在自定义视图之上并移动它。
我还附上了此处的屏幕截图供您参考:
上面按钮和文本视图的区域是我的MapView。

1
检查Redpin的源代码。它确实做到了这一点。 - Reno
2个回答

2

首先,使用LocationClient API(而不是LocationManager),因为它们与混合的gps提供程序wifi和网络非常有效地配合使用。 它们还使用传感器识别移动,以便可以节省电池电量。

我使用以下LocationOverlay代码在地图上放置我的图标。根据您的需求进行自定义。它有点粗糙,但有效。

private class LocationMarker extends ItemizedOverlay<OverlayItem> {
    private Activity appContext;
    private List<OverlayItem> items = new ArrayList<OverlayItem>();
    private Drawable marker = null;
    private OverlayItem inDrag = null;
    private ImageView dragImage = null;
    private int xDragImageOffset = 0;
    private int yDragImageOffset = 0;
    private int xDragTouchOffset = 0;
    private int yDragTouchOffset = 0;
    private MapView location;
    private boolean pickupOrDrop;

    public LocationMarker(Activity newAppContext, MapView newLocation, Drawable newMarker,
            ImageView newPoint, boolean newWhichPoint) {
        super(newMarker);
        this.appContext = newAppContext;
        this.location = newLocation;
        this.marker = newMarker;
        this.dragImage = newPoint;
        this.pickupOrDrop = newWhichPoint;

        xDragImageOffset = dragImage.getDrawable().getIntrinsicWidth() / 2;
        yDragImageOffset = dragImage.getDrawable().getIntrinsicHeight();
        populate();
    }

    public void placeMarker(GeoPoint myPoint) {
        OverlayItem toDrop = new OverlayItem(myPoint, "", "");
        items.add(toDrop);
        populate();
    }

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

    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
        super.draw(canvas, mapView, shadow);
        boundCenterBottom(marker);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event, MapView mapView) {
        if (pickupOrDrop != whichPoint)
            return false;
        final int action = event.getAction();
        final int x = (int) event.getX();
        final int y = (int) event.getY();
        boolean result = false;

        // Draw temp image
        if (action == MotionEvent.ACTION_DOWN) {
            Log.d("iTaxeeta:Overlay", "Action Down" + marker.toString());
            GeoPoint myPoint = null;
            if (items.isEmpty()) {
                if (whichPoint == PICKUP) {
                    myPoint = source = location.getProjection().fromPixels(
                            x - xDragTouchOffset, y - yDragTouchOffset);
                } else if (whichPoint == DROP) {
                    myPoint = destination = location.getProjection().fromPixels(
                            x - xDragTouchOffset, y - yDragTouchOffset);
                }
                OverlayItem toDrop = new OverlayItem(myPoint, "", "");
                items.add(toDrop);
                populate();
            }
            for (OverlayItem item : items) {
                Point p = new Point(0, 0);
                Log.d("iTaxeeta:Overlay", item.getTitle());
                location.getProjection().toPixels(item.getPoint(), p);
                if (hitTest(item, marker, x - p.x, y - p.y)) {
                    result = true;
                    inDrag = item;
                    items.remove(inDrag);
                    populate();

                    xDragTouchOffset = 0;
                    yDragTouchOffset = 0;
                    setDragImagePosition(p.x, p.y);

                    dragImage.setVisibility(View.VISIBLE);
                    xDragTouchOffset = x - p.x;
                    yDragTouchOffset = y - p.y;

                    break;
                } 
                else {
                    items.clear();
                    populate();
                    if (whichPoint == PICKUP)
                        myPoint = source = location.getProjection().fromPixels(
                                x - xDragTouchOffset, y - yDragTouchOffset);
                    else if (whichPoint == DROP)
                        myPoint = destination = location.getProjection().fromPixels(
                                x - xDragTouchOffset, y - yDragTouchOffset);
                    new GetGeoAddress((IActionBar) appContext, myPoint).execute();
                    OverlayItem toDrop = new OverlayItem(myPoint, "", "");
                    items.add(toDrop);
                    populate();
                    if (source != null && destination != null) {
                        searchCabs.setEnabled(true);
                        // tip.startAnimation(rollUpAnimation);
                        searchCabs.setTextColor(Color.parseColor("#eeeee4"));
                        if (whichPoint == PICKUP)
                            searchCabs.setBackgroundColor(Color.parseColor("#fe000a"));
                        else if (whichPoint == DROP)
                            searchCabs.setBackgroundColor(Color.parseColor("#17ee27"));
                    }
                    break;
                }
            }
        }
        // Draw temp image while moving finger across the screen
        else if (action == MotionEvent.ACTION_MOVE && inDrag != null) {
            Log.d("iTaxeeta:Overlay", "Action Move" + marker.toString());
            setDragImagePosition(x, y);
            result = true;
        }
        // Position the selected point now
        else if (action == MotionEvent.ACTION_UP && inDrag != null) {
            GeoPoint myPoint = null;
            Log.d("iTaxeeta:Overlay", "Action Up" + marker.toString());
            dragImage.setVisibility(View.VISIBLE);
            if (whichPoint == PICKUP)
                myPoint = source = location.getProjection().fromPixels(x - xDragTouchOffset,
                        y - yDragTouchOffset);
            else if (whichPoint == DROP)
                myPoint = destination = location.getProjection().fromPixels(
                        x - xDragTouchOffset, y - yDragTouchOffset);
            OverlayItem toDrop = new OverlayItem(myPoint, inDrag.getTitle(),
                    inDrag.getSnippet());
            items.add(toDrop);
            populate();
            inDrag = null;
            new GetGeoAddress((IActionBar) appContext, myPoint).execute();
            if (source != null && destination != null) {
                searchCabs.setEnabled(true);
                // tip.startAnimation(rollUpAnimation);
                searchCabs.setTextColor(Color.parseColor("#eeeee4"));
                if (whichPoint == PICKUP)
                    searchCabs.setBackgroundColor(Color.parseColor("#fe000a"));
                else if (whichPoint == DROP)
                    searchCabs.setBackgroundColor(Color.parseColor("#17ee27"));
            }
            result = true;

        }

        return (result || super.onTouchEvent(event, mapView));
    }

谢谢您的回答,但我实际上不会使用手机提供的位置服务。因为我没有使用Wifi或GPS。我有自己设计的算法来查找位置。所以问题就是如何在我的自定义视图上叠加标记并根据需要移动它。 - Sibbs Gambling

1
这是我代码的一部分,它可以实现您要求的拖动手势(但缩放有点复杂)。我需要知道您基于哪个点进行缩放,例如两个手指的中心或屏幕上的(0.0)点。
首先初始化点但不可见。
ImageButton dot = new ImageButton(this);// since I add onClick for the dot     
//setImageBitmap
dot.setVisibility(View.INVISIBLE);
RelativeLayout.LayoutParams dotparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
dotparams.setMargins(0,0,0,0); 
dot.setLayoutParams(dotparams);
final RelativeLayout myLayout = (RelativeLayout)findViewById(R.id.maplayout);//set an Id for your RelativeLayout 
myLayout.addView(dot,dotparams);

为地图添加与相关位置相对应的点。
dotImageWidth/Height可以从Bitmap.getWidth()/getHeight()获取。
private void displayPositon(int dotx, int doty) {
    dot.setVisibility(View.VISIBLE);
    RelativeLayout.LayoutParams dotparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
    dotparams.setMargins((int)(((android.widget.RelativeLayout.LayoutParams) mImage.getLayoutParams()).leftMargin + dotx - dotImageWidth/2 ),(int)(((android.widget.RelativeLayout.LayoutParams) mImage.getLayoutParams()).topMargin + doty - dotImageHeight),0,0);
    dot.setLayoutParams(params);

在 onTouchEvent 中 switch 语句之前。
RelativeLayout.LayoutParams params[];
params = new RelativeLayout.LayoutParams[2];//one is the top-left of your mapView, one is the dot
params[0]= (android.widget.RelativeLayout.LayoutParams) dot.getLayoutParams();
params[1]= (android.widget.RelativeLayout.LayoutParams) mImage.getLayoutParams();

按下操作

dotleftMargin = ((android.widget.RelativeLayout.LayoutParams) dot.getLayoutParams()).leftMargin;
dottopMargin = ((android.widget.RelativeLayout.LayoutParams) dot.getLayoutParams()).topMargin;
mapleftMargin = ((android.widget.RelativeLayout.LayoutParams) mImage.getLayoutParams()).leftMargin;
maptopMargin = ((android.widget.RelativeLayout.LayoutParams) mImage.getLayoutParams()).topMargin;

动作为ACTION_MOVE

params[0].setMargins((int)(x - mLastTouchX  + dotleftMargin), (int)(y- mLastTouchY + dottopMargin), 0, 0);
params[1].setMargins((int)(x - mLastTouchX  + mapleftMargin), (int)(y- mLastTouchY + maptopMargin), 0, 0);

我觉得你有点误解我的问题了。我的问题实际上并不是关于地图的。简单来说:我该如何将一张图片放在另一张图片的上面?你能否相应地修改你的答案呢?谢谢。 - Sibbs Gambling
我来回答你的问题,只需要将ImageButton更改为ImageView,并在你的RelativeLayout中添加id并在布局上添加图片即可。我无法绑定地图和点,所以我写了当你移动地图时如何将点与其一起移动。 - Gina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接