我家淘宝店,主要协助同学做毕业设计: 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. 说明
- 该类定义了3种操作地图物品的方式:
- 移动
- 旋转
- 缩放
- 该类的实现方法:
该类使用了两个线程分别刷新移动和旋转两个动作,分别为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);