Android高德SDK 地图篇四:Marker动画效果和逆地理编码

1,674 阅读7分钟

在上面的gif图像中,

1. 大家可以看到       (图不太好截取) 有个白色圆圈不断扩大,后又恢复原样的动画效果。

Marker动画效果

  • Marker的帧动画

实现marker白色圆圈不断扩大后又恢复原样的效果 

我们要先了解Marker的一些方法

请注意Marker还有一个 setIcon方法,与setIcons是有区别的。

MarkerOptions当然也有这些方法

其实实现这个效果有点像Android的帧动画。

但是这个没有停止动画的方法,如果需要停止marker的帧动画

那么我们就调用setIcon给Marker设置一张单个图片就可以了。

所以这个动画效果的开始与停止的代码如下:

/**
 * 开启帧动画
 * 设置多少帧刷新一次图片资源,Marker动画的间隔时间,值越小动画越快。默认为20,最小为1
 */
private void startFrameAnimation(@IntRange(from = 1, to = 20) int time) {
    if (null != getMarker()) {
        getMarker().setIcons(getListBitmapDescriptor());
        // 设置多少帧刷新一次图片资源,Marker动画的间隔时间,值越小动画越快。默认为20,最小为1。
        getMarker().setPeriod(time);
    }

}

private void stopFrameAnimation() {
    if (null != getMarker()) {
        //设置单张图片效果  表示停止Marker帧动画
        getMarker().setIcon(getSingleBitmapDescriptor());
    }
}

在本篇的代码中 setIcons(List<> picList) picList 是根据一张图片生成的。主要用了
setXfermode这个特性来重新生成一张图片 如果你觉得麻烦其实也是可以用多张图片,这里我就不详细讲解了 看代码吧: 如果你觉得麻烦 就叫UI 给你几张图就可以了。下面是生成新图片的方法

public static Bitmap combineCenterBitmap(Bitmap background, float percent) {
    if (background == null) {
        return null;
    }

    int bgWidth = background.getWidth();
    int bgHeight = background.getHeight();

    int min = 5;
    int max = bgWidth / 2;
    int difference = max - min;
    float radius = min + difference * percent + 1;

    Bitmap newmap = Bitmap
            .createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(newmap);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setColor(Color.WHITE);
    canvas.drawBitmap(background, 0, 0, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
    canvas.drawCircle(bgWidth / 2, bgWidth / 2 + 1, radius, paint);
    canvas.save(Canvas.ALL_SAVE_FLAG);

    canvas.restore();
    return newmap;
}

然后在代码中添加启动 停止动画的方法

public void hideCenterMarkerInfoWindow() {
    centerMarker.hideInfoWindow();
    stopFrameAnimation();

}

public void showLoadingInfoWindow() {
    startFrameAnimation(5);
    type = InfoWindowUIType.LOADING;
    centerMarker.showInfoWindow();

}

public void showSuccessInfoWindow() {
    stopFrameAnimation();
    type = InfoWindowUIType.SUCCESS;
    centerMarker.showInfoWindow();

}

public void showErrorInfoWindow() {
    stopFrameAnimation();
    type = InfoWindowUIType.FAILED;
    centerMarker.showInfoWindow();

}

这样我们的marker就有帧动画的效果了

  • Marker属性动画 (因为这个跟Android的属性动画很像,我们就取这个名字吧)

我们也可以完成其他动画 例如透明 旋转  移动 等。

其实这个跟Android本身动画差不多 

不一样的是  Marker设置的动画类都是是高德自己实现的并不是Android自带的那些动画类。

现在我们来看下高德属性动画的实现方式

首先我们看marker的下面两个方法

高德自己实现的动画类有如下(跟Android的属性动画名称都一样 不要搞混了)

透明动画(AlphaAnimation)

旋转动画(RotateAnimation)

缩放动画(ScaleAnimation),

移动动画(TranslateAnimation)

动画集合(AnimationSet)

我们就以缩放动画为例来写代码:

//缩放动画
Animation scalAnimation = new ScaleAnimation(1F, 1F, 0.8F, 1F);
//时间设置短点
scalAnimation.setDuration(300);
centerMarker.setAnimation(scalAnimation);
centerMarker.startAnimation();

如何停止动画?

高德没提供停止的方法,不好意思我现在还没发现主动停止动画的方式。😶

动画还可以设置差值器等等其他,动画实现很多效果

添加代码如下:

/**
 * 开启帧动画
 * 设置多少帧刷新一次图片资源,Marker动画的间隔时间,值越小动画越快。默认为20,最小为1
 */
private void startFrameAnimation(@IntRange(from = 1, to = 20) int time) {
    if (null != centerMarker) {
        centerMarker.setIcons(getListBitmapDescriptor());
        // 设置多少帧刷新一次图片资源,Marker动画的间隔时间,值越小动画越快。默认为20,最小为1。
        centerMarker.setPeriod(time);
        //缩放动画
        Animation scalAnimation = new ScaleAnimation(1F, 1F, 0.8F, 1F);
        //时间设置短点
        scalAnimation.setDuration(300);
        centerMarker.setAnimation(scalAnimation);
        centerMarker.startAnimation();
    }

}

那么完成如下效果:

逆地理编码

什么是逆地理编码: 这个名词 我第一次见觉得很深奥很难理解,后来觉得挺简单的,  其实这只是个名称,当我们知道他可以用来干什么,就非常好理解了。

逆地理编码  就是通过已知经纬度得到地址信息  例如:

104.053375,30.673637  这个是哪里呢  你总不能跟用户说经纬度是多少吧 所以我们可以使用逆地理编码得到 地址信息

步骤如下:

1. 逆地理编码(104.053375,30.673637)  

2.得到结果 天府广场  

3.那么你就可以跟用户说这里是天府广场

有逆地理编码 当然也有地理编码了。

简单来说
逆地理编码   是 《经纬度》   转换  《地址信息》
地理编码      是《地址信息》 转换    《经纬度》 

官方解释:

地理编码又称地址匹配,指的是从已知的地址描述到对应的经纬 度坐标的转换
逆地理编码即地址解析服务,具体是指从已知的经纬度坐标到对 应的地址描述(如省市、街区、楼层、房间等)的转换。

GeocodeSearch 地理编码与逆地理编码类。

这个类既可以逆地理编码也可以地理编码。

看下这个类方法如下:

RegeocodeQuery 

此类定义了逆地理编码查询的地理坐标点、查询范围、坐标类型。

我们看他的构造方法如下:

GeocodeSearch.OnGeocodeSearchListener

它回调方法如下:

逆地理编码实现代码:

private void startfindPointNameQuery(LatLonPoint point) {
    //创建一个GeocodeSearch  逆地理编码和地理编码类
    search = new GeocodeSearch(getContext());
    //设置监听器
    search.setOnGeocodeSearchListener(new GeocodeSearch.OnGeocodeSearchListener() {
        @Override
        public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
            //逆地理编码异步返回
            //由于我们的版本是大于3.2.1 所以成功是1000
            if (i == 1000) {
                //RegeocodeAddress是逆地理编码搜索的地理结果。
                RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress();
                //逆地理编码返回的格式化地址。
                String formatAddress = regeocodeAddress.getFormatAddress();

            } else {

            }

        }

        @Override
        public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {
            //地理编码异步返回

        }
    });
    //point - 要进行逆地理编码的地理坐标点。
    //radius - 查找范围。默认值为1000,取值范围1-3000,单位米。
    //latLonType - 输入参数坐标类型。包含GPS坐标和高德坐标。 GeocodeSearch.AMAP 、GeocodeSearch.GPS
    float radius = 1000F;
    //逆定力RegeocodeQuery构造函数
    RegeocodeQuery regeocodeQuery = new RegeocodeQuery(point, radius, GeocodeSearch.AMAP);
    //发起逆地理编码异步查询
    search.getFromLocationAsyn(regeocodeQuery);

}

好了这样我们就可以获取到地址信息了

大家可以看到返回的onRegeocodeSearched方法的变量RegeocodeResult regeocodeResult

这个类最重要的方法 regeocodeResult.getRegeocodeAddress();

RegeocodeAddress

得到RegeocodeAddress 法包含了我们想要的信息formatAddress(地址信息) ,RegeocodeAddress类也有很多其他属性这里就不一一介绍了 请大家到高德的文档中查看吧:

a.amap.com/lbs/static/…

好了 我们现在来 实现 获取中心marker坐标的 名称

我们添加UI效果,效果如下:

然后我们在获取地名成功后 设置TextView的文字就可以了

public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
    //逆地理编码异步返回
    //由于我们的版本是大于3.2.1 所以成功是1000
    if (i == 1000) {
        //RegeocodeAddress是逆地理编码搜索的地理结果。
        RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress();
        //逆地理编码返回的格式化地址。
        String formatAddress = regeocodeAddress.getFormatAddress();
        centerMarkerView.showSuccessInfoWindow();
        tv_start_place.setText(formatAddress);

    } else {
        centerMarkerView.showErrorInfoWindow();
        tv_start_place.setText("获取位置失败");
    }

}

然后我们实现附近车辆的效果。

附近车辆其实 多个marker 我们来实现这个效果 

首先我们来看marker的设置角度的方法

MarkerOptions类:

Marker类:

然后我们设置marker的角度,

代码如下:

private void removeNearCarMarker() {
    if (null != nearByCarMarkerList) {
        for (Marker marker : nearByCarMarkerList) {
            marker.remove();
        }
    }

}

private void addNearCarMarker(List<CarPositionData> list) {
    nearByCarMarkerList = new ArrayList<>();
    for (CarPositionData data : list) {
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.icon(BitmapDescriptorFactory.fromResource(R.mipmap.car));
        markerOptions.rotateAngle(data.getBearing());
        LatLng latLng = new LatLng(data.getLatitude(), data.getLongitude());
        markerOptions.position(latLng);
        Marker marker = getAMap().addMarker(markerOptions);
        nearByCarMarkerList.add(marker);
    }
}

好了 我们通过方法产生附近的车辆数据(不用关心这个方法  在实际情况下应该是服务器返回给我们的)。

/**
 * 获取模拟的附近的车辆的位置
 */
private void getNearCarFakeHttp(LatLonPoint point, final RegeocodeResult regeocodeResult) {
    delayTest = new DelayTest();
    delayTest.getDelayPost(point, new DelayTest.OnCallBack() {
        @Override
        public void onSuccess(List<CarPositionData> list) {
            //RegeocodeAddress是逆地理编码搜索的地理结果。
            RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress();
            //逆地理编码返回的格式化地址。
            String formatAddress = regeocodeAddress.getFormatAddress();
            //显示中心点marker成功的infoWindow
            centerMarkerView.showSuccessInfoWindow();
            //给定位的名称赋值到textView上
            tv_start_place.setText(formatAddress);

            //移除附近的车辆的marker
            removeNearCarMarker();
            //添加附近的车辆的marker
            addNearCarMarker(list);

        }

        @Override
        public void onFailed() {
            //显示中心点失败的infoWindow
            centerMarkerView.showErrorInfoWindow();
            tv_start_place.setText("获取位置失败");
        }
    });

}

好了这样我们就完成了

(由于这个附近车辆的地理坐标是模拟产生的 所以车辆有可能不在道路,这个不是bug,如果是由真实车辆上传的,就不会有这个问题)

效果如下:

代码下载地址:gitee.com/justforgame…