mAppWidget - 4. 物品在地图上旋转和移动

我家淘宝店,主要协助同学做毕业设计: https://shop104550034.taobao.com/?spm=2013.1.1000126.d21.pPCzDZ

上一篇文章讲了如何在地图上放置物品,这篇讲述如何让物品在地图上移动起来

效果如下:

1. 建立动画地图物品类:AnimationMapObject

1. AnimationMapObject代码

package com.qinxiaoyu.mAppwidget;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.animation.LinearInterpolator;

/**
 * @author    秦晓宇
 * @date      2016年4月1日 下午5:38:55
 *
 */
public class AnimationMapObject{

    private static final boolean isDebug = true;

    /**前大灯是否开启*/
    protected boolean isHeadlampsOpen;

    /**转向枚举
     * @author    秦晓宇
     * @date      2016年4月5日 上午9:47:46
     */
    public enum MOTOR_TURNING{
        NONE,
        TURN_RIGHT,
        TURN_LEFT
    };

    protected MOTOR_TURNING turning;
    protected float speed;
    protected String type;
    protected int id;
    protected Bitmap bitmap;

    protected float rotationAngle;
    protected RotationAnimationThread rotationanimationThread;
    protected MoveAnimationThread moveAnimationThread;

    enum ROTATION_TYPE{
        ROTATION_ANTICLOCKWISE, //逆时针旋转
        ROTATION_CLOCKWISE,     //顺时针旋转
    };
    protected ROTATION_TYPE rotationType;
    protected boolean isRotation;
    protected boolean isDraw;
    protected float rotationEndAngle;

    protected boolean isMove;
    protected Point targetPosition;
    protected float move_xoffset;
    protected float move_yoffset;
    protected float move_ystep;
    protected float move_xstep;
    protected float xoffset;
    protected float yoffset;
    private float addxOffset;
    private float addYoffset;

    /**图形缩放*/
    protected float scale = 1f;

//  Matrix matrix;
    Point pointTmp;
    /**
     * 初始化一辆车,绘制将车辆的图片载入到程序中
     * @param context
     * @param drawable
     *          - 车辆的图片资源
     * @date 2016年4月7日下午5:09:50
     */
    public AnimationMapObject(Context context,int id,Drawable drawable,Point initPoint)
    {
//      matrix = new Matrix();
        this.id = id;
        debug("getDrawable = "+ drawable);
        bitmap = ((BitmapDrawable) drawable).getBitmap();
        targetPosition = new Point();
        pointTmp = new Point();
//      animationTimer = new Timer();
        rotationanimationThread = new RotationAnimationThread();
        rotationanimationThread.start();
        moveAnimationThread = new MoveAnimationThread();
        moveAnimationThread.start();
        move_xstep = initPoint.x;
        move_ystep = initPoint.y;

    }

    /**
     * 调试方法
     * @author    秦晓宇
     * @date      2016年4月7日 上午10:28:33
     * @param str
     */
    private void debug(String str)
    {
        if(isDebug == true)
            Log.d("AnimationMapObject",str);
    }

    /**
     * 绘制小车
     * @author    秦晓宇
     * @date      2016年4月7日 下午5:13:54
     * @param canvas
     * @param point
     */
    public void draw(Canvas canvas){
        Matrix matrix = new Matrix();
        Paint paint = new Paint();

        //设置抗锯齿,防止过多的失真
        matrix.postScale(scale, scale);
//      if(isRotation == true);
        matrix.postRotate(rotationAngle, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
//      if(isMove == true)
        matrix.postTranslate(move_xstep-bitmap.getWidth() / 2,move_ystep-bitmap.getHeight() / 2);       

        paint.setAntiAlias(true);  

        canvas.drawBitmap(bitmap, matrix, paint);
    }

    /**
     * 使车辆在一定时间内旋转一定的角度
     * @author    秦晓宇
     * @date      2016年4月7日 下午5:17:22
     * @param start_rotation
     *          - 开始角度
     * @param end_rotation
     *          - 结束角度
     * @param duration
     *          - 旋转时间 毫秒为单位
     */
    public void turnTo(float start_rotation,float end_rotation,long duration){
        LinearInterpolator ll = new LinearInterpolator();
        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "rotation",start_rotation, end_rotation);
        animator.setInterpolator(ll);
        animator.setDuration(duration);
        animator.start();
    } 

    public void trueLeft()
    {

    }

    /**
     * 动画效果旋转某个物体
     * @author    秦晓宇
     * @date      2016年4月11日 上午11:22:01
     * @param angle
     *          - 旋转角度,正值顺时针旋转,负值逆时针旋转
     * @param duration
     *          - 旋转时间
     */
    public void setRotation(float angle,int duration)
    {
        if(angle == 0) return;

        /*
         * 设定要旋转的角度和持续时间,当前角度和angle
         * */

        //设置开始旋转状态标志位
        isRotation = true;
        //判断正传还是反转
        if(angle > 0)
            rotationType = ROTATION_TYPE.ROTATION_CLOCKWISE;
        else
            rotationType = ROTATION_TYPE.ROTATION_ANTICLOCKWISE;
        //计算旋转到达的角度
        rotationEndAngle = rotationAngle+angle;
        //计算每一度的旋转时间
        int rotationScale = (int) (duration/Math.abs(angle));
        debug("setRotation 单次刷新时间 = "+rotationScale);
        rotationanimationThread.setDuration(rotationScale);
    }

    /**
     * 设置移动动画
     * @author    秦晓宇
     * @date      2016年4月11日 下午2:57:39
     * @param point
     *          - 移动的目标地址
     * @param duration
     *          - 移动的时间
     */
    public void setMove(Point point,int duration)
    {
        if(point == null) return;
        if(point.x == move_xstep && point.y == move_ystep) return;
        this.targetPosition = point;
        isMove = true;

        targetPosition = point;

        //当前位置
        //pointTmp = bitmap.get//getPosition();

        pointTmp.x = (int) move_xstep;
        pointTmp.y = (int) move_ystep;
        //计算xy轴的偏移量
        xoffset = point.x-pointTmp.x;
        yoffset = point.y-pointTmp.y;

        move_xoffset = xoffset/duration*20;
        move_yoffset = yoffset/duration*20;             

        move_xstep = pointTmp.x+move_xoffset;
        move_ystep = pointTmp.y+move_yoffset;

        //moveAnimationThread.setDuration(duration);
        debug("当前点x坐标 = "+pointTmp.x);
        debug("当前点y坐标 = "+pointTmp.y);
        debug("物体按x轴移动的总距离 = " + xoffset);
        debug("物体按y轴移动的总距离 = " + yoffset);
        debug("物体每次重绘x轴位置 = " + move_xoffset);
        debug("物体每次重绘y轴位置 = " + move_yoffset);
    }

    /**
     * 每次旋转1度,时间由duration来确定。若旋转60度,duration=60,则定时1ms一次
     * 若旋转60度,duration=600ms则定时10ms重绘一次
     *
     * 一次重绘时间=duration/angle
     *
     * **/
    private class RotationAnimationThread extends Thread
    {
        private boolean runFlag = true;
        private int duration;

        public void setDuration(int duration) {
            this.duration = duration;
        }
        public void run()
        {
            while(runFlag)
            {
                drawRotation();

                try
                {
                    Thread.sleep(duration);
                }
                catch (InterruptedException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        private void drawRotation()
        {
            if(isRotation == false) return;
            debug("rotationAngle = "+rotationAngle+"   rotationEndAngle = "+rotationEndAngle);
            if(rotationType == ROTATION_TYPE.ROTATION_CLOCKWISE)
            {
                debug("正转");
                if(rotationAngle >= rotationEndAngle)
                {
                    isRotation = false;
                    return;
                }
                rotationAngle += 1;
            }

            if(rotationType == ROTATION_TYPE.ROTATION_ANTICLOCKWISE)
            {
                debug("反转");
                if(rotationAngle <= rotationEndAngle)
                {
                    isRotation = false;
                    return;
                }
                rotationAngle -= 1;
            }
        }

    }

    private class MoveAnimationThread extends Thread
    {
        private boolean runFlag = true;
        private final int duration = 20;
        public void run()
        {
            while(runFlag)
            {
                drawMove();
                try
                {
                    Thread.sleep(duration);
                }
                catch (InterruptedException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        private void drawMove()
        {
            if(isMove == false) return;
            //若移动距离已经够了,标示已经移动到位
            if((Math.abs(addxOffset)>=Math.abs(xoffset))&&(Math.abs(addYoffset)>=Math.abs(yoffset)))
            {
                isMove = false;
                addxOffset = 0;
                addYoffset = 0;
                return;
            }
            move_ystep += move_yoffset;
            move_xstep += move_xoffset;
            //debug("drawMove move_yoffset = "+move_yoffset);
            addxOffset+=move_xoffset;
            addYoffset+=move_yoffset;
        }

    }

    public boolean isRotation() {
        return isRotation;
    }

    /******************** getting & setting **************************/
    /**
     * 获得大灯是否开启的状体
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:05:28
     * @return
     */
    public boolean  getIsHeadlampsOpen(){return this.isHeadlampsOpen;}
    /**
     * 设置大灯开启状态
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:05:48
     * @param isHeadlampsOpen
     */
    public void setIsHeadlampsOpen(boolean isHeadlampsOpen){this.isHeadlampsOpen = isHeadlampsOpen;}
    /**
     * 获得转向状态
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:06:06
     * @return
     */
    public MOTOR_TURNING getTurning(){return this.turning;}
    /**
     * 设置转向状态
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:06:21
     * @param turning
     */
    public void setTurning(MOTOR_TURNING turning){this.turning = turning;}
    /**
     * 获得车辆速度
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:06:31
     * @return
     */
    public float getSpeed(){return this.speed;}
    /**
     * 设置车辆速度
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:06:47
     * @param speed
     */
    public void setSpeed(float speed){this.speed = speed;}
    /**
     * 获得车辆类型
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:07:02
     * @return
     */
    public String getType(){return this.type;}
    /**
     * 设置车辆类型
     * @author    秦晓宇
     * @date      2016年4月5日 上午10:07:10
     * @param type
     */
    public void setType(String type){this.type = type;}
    /**
     * 获得车辆id
     * @author    秦晓宇
     * @date      2016年4月8日 上午8:18:18
     * @return    车辆id
     */
    public int getId() {return id;}
    /**
     * 设置车辆id
     * @author    秦晓宇
     * @date      2016年4月8日 上午8:18:43
     * @param id
     */
    public void setId(int id) {this.id = id;}

    /**
     * 设置当前图片在地图的位置
     * @author    秦晓宇
     * @date      2016年4月13日 上午9:52:18
     * @param point
     *          - 图片所处位置
     */
    public void setPosition(Point point)
    {
        move_xstep = point.x;
        move_ystep = point.y;
    }

    /**
     * 获得当前图片相对于地图的位置
     * @author    秦晓宇
     * @date      2016年4月13日 上午9:55:06
     * @return
     *          - 当前图片所处的位置
     */
    public Point getPosition()
    {
        Point point = new Point();
        point.x = (int) move_xstep;
        point.y = (int) move_ystep;
        return point;
    }
    public float getScale() {
        return scale;
    }

    public void setScale(float scale) {
        this.scale = scale;
    }

}

2. 说明

  1. 该类定义了3种操作地图物品的方式:

    1. 移动
    2. 旋转
    3. 缩放
  2. 该类的实现方法:

    该类使用了两个线程分别刷新移动和旋转两个动作,分别为MoveAnimationThread 和RotationAnimationThread。当设置了移动或者旋转的参数时,线程会自动匀速调整参数,调整过参数的图标被地图控件不断刷新,以达到不断运动的效果。

2. 在地图控件上不断刷新物品

在上一篇文章中我们创建了RoadWayMapWidget,这次我们对这个类进行了修改

1. RoadWayMapWidget:

**
 * @author    秦晓宇
 * @date    2016年4月1日 上午9:04:27
 * @describe  井下交通地图地图控件
 */
public class RoadWayMapWidget extends MapWidget{
    private ArrayList<AnimationMapObject> cars;
    Handler handler;
    /**
     *
     * @param context
     *          -context
     * @param rootMapFolder
     *          -地图资源文件名
     * @param initialZoomLevel
     *          -初始化缩放等级
     * @date  2016年4月1日上午9:11:39
     */
    public RoadWayMapWidget(Context context, String rootMapFolder,int initialZoomLevel) {
        super(context, rootMapFolder, initialZoomLevel);
        // TODO Auto-generated constructor stub

        cars = new ArrayList<AnimationMapObject>();

        handler = new Handler()
        {
            public void handleMessage(Message msg)
            {
                if(msg.what == 0x1)
                {
                    invalidate();
                }
            }
        };

        new Timer().schedule(new TimerTask(){

            @Override
            public void run() {
                // TODO Auto-generated method stub
//              debug("TimerTask run");
                handler.sendEmptyMessage(0x1);

            }
        },0,50);

    }
    /**
     * 绘制小车
     * @author    秦晓宇
     * @date      2016年4月7日 下午5:35:51
     * @param canvas
     */
    private void drawCars(Canvas canvas)
    {
        int size = cars.size();
        for(int i=0;i<size;i++)
        {
            cars.get(i).draw(canvas);
        }
    }
    /**
     * 重写onDraw方法
     *
     * @param canvas
     *
     * @date 2016年4月1日上午9:38:50
     *
     * @see com.ls.widgets.map.MapWidget#onDraw(android.graphics.Canvas)
     */
    @Override
    protected void onDraw(Canvas canvas)
    {
        this.getDrawingRect(drawingRect);

        if (config != null)
        {
            if (prevGrid != null)
            {
                prevGrid.draw(canvas, paint, drawingRect);
            }

            if (grid != null)
            {
                grid.draw(canvas, paint, drawingRect);
            }

            drawLayers(canvas, drawingRect);
            drawCars(canvas);
        }
        else
        {
            debug("scrollTo(0, 0);");
            scrollTo(0, 0);
            drawMissingDataErrorMessage(canvas);
        }
    }
    /******************** getting & setting **************************/
    public void addCar(AnimationMapObject car)
    {
        cars.add(car);
    }
}

2. 说明:

这个类通过定时器,每隔50毫秒刷新自身,同时调用drawCars方法将添加到其上的物品重绘

3. 使用

1. MainActivity

public class MainActivity extends Activity {

    Handler handler;

    private void debug(String str){
        Log.d("MainActivity",str);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE); //设置无标题
        setContentView(R.layout.activity_main); 

        final RoadWayMapWidget map = new RoadWayMapWidget(this, "map",15);
        LinearLayout layout = (LinearLayout) findViewById(R.id.mainLayout);
        map.getConfig().setZoomBtnsVisible(false);
        layout.addView(map);

        final AnimationMapObject myCar = new AnimationMapObject(getApplicationContext(),1,getResources().getDrawable(R.drawable.car_arror),new Point(100,100));

        map.addCar(myCar);
        myCar.setMove(new Point(1500, 500), 10000);
        myCar.setRotation(-360, 3000);
    }
}

2. 说明

添加移动物品
map.addCar(myCar);
设置物品移动动画
myCar.setMove(new Point(1500, 500), 10000);
设置物品旋转动画
myCar.setRotation(-360, 3000);
时间: 2024-10-24 18:12:41

mAppWidget - 4. 物品在地图上旋转和移动的相关文章

mAppWidget - 3. 在地图上放置物品

我家淘宝店,主要协助同学做毕业设计:https://shop104550034.taobao.com/?spm=2013.1.1000126.d21.pPCzDZ 1. 如何在地图上放置物品 现在我们要将这个箭头放置在地图上 1. 将地图图片文件放置到资源文件夹中 地图放置在/res/drawable-mdpi中,如下图所示 2. 在地图上放置 在MainActivity中代码如下: public class MainActivity extends Activity { private voi

通过地图上两个点的经纬度测算两点的距离

根据两点经纬度计算距离 这些经纬线是怎样定出来的呢?地球是在不停地绕地轴旋转(地轴是一根通过地球南北两极和地球中心的 假想线),在地球中腰画一个与地轴垂直的大圆圈,使圈上的每一点都和南北两极的距离相等,这个圆圈 就叫作"赤道".在赤道的南北两边,画出许多和赤道平行的圆圈,就是"纬圈":构成这些圆圈的线段, 叫做纬线.我们把赤道定为纬度零度,向南向北各为90度,在赤道以南的叫南纬,在赤道以北的叫北纬. 北极就是北纬90度,南极就是南纬90度.纬度的高低也标志着气候的冷

Android基于mAppWidget实现手绘地图(二)--概要

尊重原创,参考自:http://www.67tgb.com/ http://lemberg.github.io/mappwidget/user_guide.html 最近在看一些导游类应用,发现一些景区的导览图使用的完全是自定义地图,也就是手绘地图.这种小范围使用的,专业的地图给人以亲切的感觉.但是他们是如何实现的呢,网上找了找实现思路,都没有太满意的. 在一家英国的网站上,发现了一个类库,可以实现类似的功能.试了一下它提供的demo,发现功能强大的超过了我的预期.在这里分享给大家,希望给大家提

Android基于mAppWidget实现手绘地图(一)--简介

尊重原创,参考自:http://www.67tgb.com/ http://lemberg.github.io/mappwidget/user_guide.html 最近在看一些导游类应用,发现一些景区的导览图使用的完全是自定义地图,也就是手绘地图.这种小范围使用的,专业的地图给人以亲切的感觉.但是他们是如何实现的呢,网上找了找实现思路,都没有太满意的. 在一家英国的网站上,发现了一个类库,可以实现类似的功能.试了一下它提供的demo,发现功能强大的超过了我的预期.在这里分享给大家,希望给大家提

Ios 高德地图 地图上添加多个大头针 怎么在复用队列中知道我单击的是哪一个大头针

//创建大头针对象     MAPointAnnotation *pointAnnotation = [[MAPointAnnotation alloc] init];     //插入大头针的位置     pointAnnotation.coordinate = CLLocationCoordinate2DMake(39.989631, 116.481018);     //大头针的标题     pointAnnotation.title = @"方恒国际";     //大头针的子

iOS:实现MKAnnotation协议,在地图上设置大头针,点击显示具体的位置信息

如何添加大头针(地标): 通过MapView的addAnnotation方法可以添加一个大头针到地图上 通过MapView的addAnnotations方法可以添加多个大头针到地图上 –(void)addAnnotation:(id <MKAnnotation>)annotation; 说明:需要传入一个遵守了MKAnnotation协议的对象 基本步骤为: <1>新建一个遵守MKAnnotation协议的类: @interface MyAnnotation : NSObject 

ArcGIS for Android地图上实际距离与对应的屏幕像素值计算

/** * 将实际地理距离转换为屏幕像素值 * * @param distance * 实际距离,单位为米 * @param currScale * 当前地图尺寸 * @param context * @return */ public static double metreToScreenPixel(double distance, double currScale, Context context) { float dpi = context.getResources().getDispla

ArcGIS API for Silverlight 实现修改地图上的工程点位置

原文:ArcGIS API for Silverlight 实现修改地图上的工程点位置 #region 处理工程点点击编辑相关事件 public Graphic editgraphics = null; //待编辑的Graphics图层 public Graphic oldgraphics = null; //原先Graphics图层 public Symbol symbolold = null; /// <summary> /// 在地图上点击编辑点处理事件 /// </summary

在js版搜索地图上添加标记

由于我们做的是有关于旅游方面的项目,所以涉及到了地图功能.我接到的其中一个任务就是,在地图上显示指定的几个景点,并在地图上加上标记. 我们项目用的是搜狗地图,使用的是js版本.大家有兴趣的话,可以参考搜索地图api以及示例代码. 在地图上添加标记是地图的一个基本功能.这个标记叫做Marker.可以从这里看官网上对于Marker类的介绍. 实现的基本步骤,首先在页面上创建一个地图,然后地图上添加一个marker.你可以对这个marker指定位置.显示内容,在地图上的显隐等.具体请看一下代码: <h