百度地图,Android显示车辆轨迹动画
初次设计想就用百度自带的显示覆盖物的方式,计算两个坐标点的距离,添加短距离的坐标点,然后在密密麻麻的坐标点之间显示,隐藏覆盖物,形成移动的视觉效果。
后来发现会创建较大的内存,而且效果不佳。
最后选择添加View的方法,利用addView方法添加小车的view,利用Android动画,同步和异步结合显示小车运行。
主要几个注意点和方案是:
- 计算小车转弯的角度,判断顺时逆时针。
- 计算地图缩放比例,依据是一组经纬度的最大距离值
- 在显示动画的时候地图是不能缩放的,因为经纬度和屏幕坐标转化不是实时的,数据是在调用接口时屏幕当前的比例尺转化。
- 还有就是当动画前屏幕被缩放了,轨迹已经超出了屏幕显示,就计算下一个车辆到达的点在不在屏幕上,不在的话就移动地图,将该点设置为中心点,这个并不完善
代码片段
地图移动到某一点,并且设置缩放比例。
@Override
public void MoveToPoint(List<LatLng> list) {
/**
* 设置地图移动到指定位置
*/
// 定义地图状态
//数字越小,比例尺越大
Double lat=new Double(0),lng=new Double(0);
double latlng[]=new double[2];
int zooms=getZoomnum(latlng,list);
Log.i(TAG, "lat lng:" + latlng[0] + "__ " + latlng[1]+"zoom "+zooms);
MapStatus mMapStatus = new MapStatus.Builder().target(new LatLng(latlng[0],latlng[1])).zoom(zooms)
.build();
// 定义MapStatusUpdate对象,以便描述地图状态将要发生的变化
MapStatusUpdate mMapStatusUpdate = MapStatusUpdateFactory
.newMapStatus(mMapStatus);
// 改变地图状态
mBaiduMap.setMapStatus(mMapStatusUpdate);
}
//根据轨迹,一组坐标值计算缩放比例
public int getZoomnum(double[] lalg,final List<LatLng> pointlist){
int zoomSize[]={5,10,20,50,100,200,500,1000,2000,5000,10000,20000};//分别对应于zoom 21,20,19,18,17,16,15,14,13,12,11,10
int i,j;
double max=0,temp;
for(i=0;i<pointlist.size();i++)
{
for(j=i+1;j<pointlist.size();j++)
{
temp=DistanceUtil.getDistance(pointlist.get(i),pointlist.get(j));
if(temp>max){
max=temp;
lalg[0]=(pointlist.get(i).latitude+pointlist.get(j).latitude)/2;
lalg[1]=(pointlist.get(i).longitude+pointlist.get(j).longitude)/2;
Log.i(TAG,"lat lng:"+lalg[0]+"_dadsa_ "+lalg[1]);
}
}
}
Log.i(TAG,"max: "+max);
for(i=0;i<11;i++)
{
if(max>zoomSize[i]*12&&max<zoomSize[i+1]*12) {
Log.i(TAG,"ixiabio: "+i);
return 20 - i;
}
}
if(max<zoomSize[0]*12)return 21;
else return 10;
}
利用向量知识计算方向角,顺时针旋转为正,逆时针为负。
//计算两个向量的方向角,并根据顺时针返回正值,逆时针返回负值
public float getangle(Point p1,Point p2,Point p3){
double xp1p2=p2.x-p1.x;
double yp1p2=p2.y-p1.y;
double xp2p3=p3.x-p2.x;
double yp2p3=p3.y-p2.y;
double a1=Math.sqrt(xp1p2*xp1p2+yp1p2*yp1p2);
double b1=Math.sqrt(xp2p3*xp2p3+yp2p3*yp2p3);
double ab=xp1p2*xp2p3+yp1p2*yp2p3;
float angle;
if((a1*b1)!=0)
angle =(float)Math.acos(ab/(a1*b1));
else angle=0;
double a_b=xp1p2*yp2p3-xp2p3*yp1p2;
if(a_b<0)angle=-angle;
return angle*180/(float)Math.PI;
}
核心部分,动画绘制
//行车的时候,地图不能缩放,因为经纬度转化成坐标值是以当时地图的比例尺进行转化的
public void rotateyAnimRun(final View view,final List<Point> list)
{
//ObjectAnimator的父类是valueAnimator,爷爷类是Animator.
List<Animator> lt= new ArrayList<Animator>();
Log.i(TAG,"lt size: "+lt.size());
int size=list.size();
float t1=0f;
AnimatorSet set = new AnimatorSet();
int i=0;
carPathlist_point=list;
for (i=0;i<size-1;i++){
Animator animator,animator2;
int k=i;//计数车子不动相同的经纬度情况
while(i<size-1&&list.get(i).x==list.get(i+1).x&&list.get(i).y==list.get(i+1).y)i++;
if(i==size-1)break;
Log.i(TAG,"坐标: "+list.get(i).x+"____"+list.get(i).y);
//PropertyValuesHolder属性holder,分别设置X,Y方向的移动值,然后调用ofPropertyValuesHolder设置属性值,这个是异步叠加动画。
//参数指的是相对于view最初位置的偏移值
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", list.get(i).x-list.get(0).x,
list.get(i+1).x-list.get(0).x);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("translationY", list.get(i).y-list.get(0).y,
list.get(i+1).y-list.get(0).y);
PropertyValuesHolder pvhR;
//监听在小车运行的时候如果下一个坐标点不在地图内,则将地图的中心点设置为下一个坐标点。
class newAnListener implements Animator.AnimatorListener{
private int center;
public newAnListener(int center_){center=center_;}
@Override
public void onAnimationStart(Animator animation) {
WindowManager wm = (WindowManager) BaiduMapActivity.this
.getSystemService(Context.WINDOW_SERVICE);
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
Log.i(TAG, "width: " + width + "height: " + height + "mapCenter: " + center);
Log.i(TAG, "x: " + list.get(center + 1).x + "y: " + list.get(center + 1).y);
if (list.get(center + 1).x < 0 ||
list.get(center + 1).y < 0 ||
list.get(center + 1).x > width ||
list.get(center + 1).y > height) {
Log.i(TAG, "不是大了就是小了");
MapStatusUpdate mMapStatusUpdate = MapStatusUpdateFactory.newLatLng(carPathlist.get(center));
// 改变地图状态
mBaiduMap.setMapStatus(mMapStatusUpdate);
}
}
@Override
public void onAnimationEnd(Animator animation) {}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
};
if(k==0) {
float angle =getangle(new Point(list.get(i).x-1,list.get(i).y),list.get(i),list.get(i+1));
Log.i(TAG, "angle: "+angle);
t1=t1+angle;
pvhR = PropertyValuesHolder.ofFloat("rotation", t1);
animator2=ObjectAnimator.ofPropertyValuesHolder(view, pvhR).setDuration(500);
animator2.addListener(new newAnListener(i));
}
else{
Log.i(TAG,"k: "+k+"i: "+i);
float angle =getangle(list.get(k-1),list.get(i),list.get(i+1));
Log.i(TAG, "angle: "+angle);
t1=t1+angle;
pvhR = PropertyValuesHolder.ofFloat("rotation", t1);
animator2=ObjectAnimator.ofPropertyValuesHolder(view, pvhR).setDuration(500);
animator2.addListener(new newAnListener(i));
}
animator= ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY).setDuration(1000);
//先旋转再直行
lt.add(animator2);
lt.add(animator);
}
//顺序执行Animator list中的动画效果
set.playSequentially(lt);
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//所有手势不可用
mUiSettings.setAllGesturesEnabled(false);
mMapView.showZoomControls(false);
lay_showpath.setClickable(false);
Log.i(TAG, "set_____________start");
}
@Override
public void onAnimationEnd(Animator animation) {
mMapView.removeViewInLayout(view);
//所有手势可用
mUiSettings.setAllGesturesEnabled(true);
mMapView.showZoomControls(true);
lay_showpath.setClickable(true);
Log.i(TAG, "set_____________end");
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
//可以将会设置绘制动画,设置在监听百度地图已经加载完毕的事件里
set.start();
Log.i(TAG, "lt end size: " + lt.size());
}
添加该view到百度地图。
@Override
public void DrawDynaPath(final List<LatLng> pointlist) {
//获取小车的view
Bitmap bitmap= BitmapFactory.decodeResource(getResources(), R.drawable.ic_vehicle_location_current);
ImageView iv=new ImageView(this);
iv.setImageBitmap(bitmap);
Log.i(TAG, "size():" + pointlist.size());
Projection pj=mBaiduMap.getProjection();
//所有经纬度转化成坐标
List<Point> list=new ArrayList<Point>();
for(LatLng ll:pointlist){
list.add(pj.toScreenLocation(ll));
}
mMapView.removeViewInLayout(iv);
// layoutMode(MapViewLayoutParams.ELayoutMode mode 指定 MapViewLayoutParams 的方式:屏幕坐标或者地图经纬度坐标
//absoluteMode是不随地图变化而移动的,mapMode是随地图移动
MapViewLayoutParams mlp = new MapViewLayoutParams.Builder().
layoutMode(MapViewLayoutParams.ELayoutMode.mapMode).
position(pointlist.get(0)).
width(MapViewLayoutParams.WRAP_CONTENT).
height(MapViewLayoutParams.WRAP_CONTENT).build();
Log.i(TAG,"list.get(0):"+list.get(0).toString());
// mMapView.removeAllViews();
rotateyAnimRun(iv, list);
mMapView.addView(iv, mlp);
// layout_baidumap.removeAllViewsInLayout();
// mMapView.addView(parent);
mMapView.refreshDrawableState();
}