Android高德SDK 地图篇三:Marker实现选择起点

2,416 阅读6分钟

在很多应用中都Marker在地图控件中间位置不变化:如下图

ps:有人说是屏幕这个其实形容不准确 其实是MapView的中心点,只不过地图一般都是铺满屏幕的,所以才会有屏幕的说法

请注意:(在这篇文章中 屏幕指MapView)

在上一期中 我们知道 Marker类有   设置Marker像素位置的方法如下

那么我们如何取的地图控件中心点的像素坐标呢。

这里我们需要了解两个新的类

  • CameraPosition

   相机位置,这个类包含了所有的可视区域的位置参数。

  这个类是我们在地图移动视角后,存储一些参数如下:

在这个类中 target属性是地图控件中心点的坐标,这个是属性是我们等会要使用

  • Projection 

这个类负责将屏幕位置和地理坐标(经纬度)进行转换。屏幕位置是相对地图的左上角的位置,所以并不一定是从整个屏幕开始计算的。

这个类重要的是他的方法。请看如下图

看完上面我们就应该知道 如果取地图控件中心点的像素位置了。

我们要通过CarmeraPositon的target属性要拿到地图中心点的坐标

通过Projection的toScreenLocation方法来获取地图控件中心点的像素位置

代码如下:

//拿到地图中心点的坐标。
LatLng latLng = aMap.getCameraPosition().target;
//把中心点的坐标转换成屏幕像素位置
Point screenPosition = aMap.getProjection().toScreenLocation(latLng);
//给marker设置像素位置。
marker.setPositionByPixels(screenPosition.x, screenPosition.y);

调用这段代码需要注意的问题:

如果我们直接在Activty中的onCreate或者Frgment的onViewCreate方法调用,由于MapView这个时候的长度和宽度都还是0,这个时候算出的screenPosition.x screenPosition.y

都为0.计算的结果是不正确的,你会看不到Marker的,

所以我们要在MapView的长度和宽度已经算出来的情况下在调用这个方法,

高德SDK也提供了这个方法代码如下:

getAMap().setOnMapLoadedListener(new AMap.OnMapLoadedListener() {
    @Override
    public void onMapLoaded() {
       //在这里调用这个方法,才能显示出来
        addCenterMarker();
    }
});

移动地图后获取中间Marker的坐标点(实现选择起点)

好了 我们来实现下面的这个效果

首先我们分析下这个功能,

1. 移动地图的时候 infoWindow隐藏,

2. 停止移动的时候 infoWindow显示loading效果。

3. 根据坐标的获取地理名称后 infoWindow显示成功。 当然也有失败的情况。

根据坐标点的获取地理名称 在高德上 叫逆地理编码, 是一个耗时操作(相当于一个网络请求)

ps:  大家看到那个三分钟和附近车辆的坐标位置, 其实应该是滴滴网络请求接口返回的,但是我并没有这个接口 就直接写了 3分钟,附近车辆坐标是根据坐标点随机生成。

当然在实际情况下这样肯定不行 正常情况应该是:

当我们停止移动地图的时候,应该有两个耗时操作,一个是获取坐标点地理名称(逆地理编码),一个是获取预估时间附近车辆位置(http请求接口),且这两个耗时操作都要成功 InfoWindow 才能显示成功。 

这个时候我就非常推荐使用RXJAVA来实现这个功能了。

但是是在这里我们主要是介绍高德SDK 尽量不引入其他框架,来增加大家的学习成本。

在代码中我会做一个测试数据来模拟获取预估时间附近车辆位置(http请求接口) 不使用rxjava

首先我们了解下 一个方法

OnCameraChangeListener:

这个方法会在地图移动的时候回调。

onCameraChange 会在移动地图的时候不断调用

onCameraChangeFinish 会在地图停止移动的时候的调用

我们应该

在onCameraChange 隐藏infoWindow

在onCameraChangeFinish 进行逆地理编码和获取接驾时间的耗时操作,infoWindow同时显示loading效果,成功后infoWindow显示成功 失败后InfoWindow显示失败

我们把代码都写在Fragment中不好 所以我们封装一下 

建一个CenterMarkerView类

代码如下:

/**
 * @author jikun
 *         Created by jikun on 2018/3/14.
 */

public class CenterMarkerView {

    private Marker centerMarker;

    public void addCenterMarker(AMap aMap) {
        MarkerOptions options = new MarkerOptions();
        //对应Marker.setIcon方法  设置Marker的图片
        options.icon(BitmapDescriptorFactory.fromResource(R.mipmap.icon_me_location));
        options.anchor(0.5F, 1);
        //拿到地图中心点的坐标。
        LatLng latLng = aMap.getCameraPosition().target;
        //把中心点的坐标转换成屏幕像素位置
        Point screenPosition = aMap.getProjection().toScreenLocation(latLng);
        //在地图上添加Marker并获取到Marker.
        centerMarker = aMap.addMarker(options);
        //给marker设置像素位置。
        centerMarker.setPositionByPixels(screenPosition.x, screenPosition.y);
        centerMarker.setAnchor(0.5F, 1);

    }

    public void initInfoWindowsView(final Context context, AMap aMap) {
        aMap.setInfoWindowAdapter(new AMap.InfoWindowAdapter() {
            @Override
            public View getInfoWindow(Marker marker) {
                View infoWindow = LayoutInflater.from(context).inflate(
                        R.layout.map_fast_car_info_window, null);

                infoWindow.findViewById(R.id.ll_have_net).setVisibility(View.VISIBLE);
                infoWindow.findViewById(R.id.ll_no_net).setVisibility(View.GONE);
                infoWindow.findViewById(R.id.ll_left).setVisibility(View.VISIBLE);
                infoWindow.findViewById(R.id.iv_loading).setVisibility(View.GONE);
                return infoWindow;
            }

            @Override
            public View getInfoContents(Marker marker) {
                return null;
            }
        });
    }


    public void hideCenterMarkerInfoWindow() {

    }

    public void showLoadingInfoWindow() {

    }

    public void showSuccessInfoWindow() {

    }

    public void showErrorInfoWindow() {

    }

    public void destorty() {
        if (null != centerMarker) {
            centerMarker.destroy();
        }


    }
}

当我们调用Marker.showInfoWindow 方法的时候 InfoWindowAdapter的getInfoWindow()和getInfoContents()会回调

根据这个 我们先写一个枚举类 来区分当前InfoWindow是 加载中 还是成功 或者失败的UI效果

注解代码如下:

//添加支持注解的依赖到你的项目中,需要在build.gradle文件中的依赖块中添加:
//dependencies { compile 'com.android.support:support-annotations:24.2.0' }
@IntDef({InfoWindowUIType.LOADING, InfoWindowUIType.SUCCESS, InfoWindowUIType.FAILED})
@Retention(RetentionPolicy.SOURCE)
public @interface InfoWindowUIType {

    public static final int LOADING = 0;
    public static final int SUCCESS = 1;
    public static final int FAILED = 2;


}

好了我们来写UI控制效果 代码如下:

public void initInfoWindowsView(final Context context, AMap aMap) {
    aMap.setInfoWindowAdapter(new AMap.InfoWindowAdapter() {
        @Override
        public View getInfoWindow(Marker marker) {
            View infoWindow = LayoutInflater.from(context).inflate(
                    R.layout.map_fast_car_info_window, null);
            switch (type) {
                case InfoWindowUIType.LOADING:
                    //Loading InfoWindow显示效果
                    infoWindow.findViewById(R.id.ll_have_net).setVisibility(View.VISIBLE);
                    infoWindow.findViewById(R.id.ll_no_net).setVisibility(View.GONE);
                    infoWindow.findViewById(R.id.ll_left).setVisibility(View.GONE);
                    infoWindow.findViewById(R.id.iv_loading).setVisibility(View.VISIBLE);

                    ImageView imageView = infoWindow.findViewById(R.id.iv_loading);
                    AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
                    if (!animationDrawable.isRunning()) {
                        animationDrawable.start();
                    }

                    break;
                case InfoWindowUIType.SUCCESS:
                    infoWindow.findViewById(R.id.ll_have_net).setVisibility(View.VISIBLE);
                    infoWindow.findViewById(R.id.ll_no_net).setVisibility(View.GONE);
                    infoWindow.findViewById(R.id.ll_left).setVisibility(View.VISIBLE);
                    infoWindow.findViewById(R.id.iv_loading).setVisibility(View.GONE);
                    break;
                case InfoWindowUIType.FAILED:
                    infoWindow.findViewById(R.id.ll_have_net).setVisibility(View.GONE);
                    infoWindow.findViewById(R.id.ll_no_net).setVisibility(View.VISIBLE);
                    break;
                default:
                    break;
            }

            return infoWindow;
        }

        @Override
        public View getInfoContents(Marker marker) {
            return null;
        }
    });
}

我们添加一个模拟请求的延迟类

代码如下

/**
 * @author jikun
 *         Created by jikun on 2018/3/14.
 */

public class DelayTest {
    public OnCallBack callBack;

    public Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (null != callBack) {
                callBack.onSuccess();
            }
        }
    };

    public void getDelayPost(OnCallBack callBack) {
        this.callBack = callBack;
        long sec = (long) (Math.random() * 1300);
        Log.e("测试代码", "测试代码时间=" + sec);
        handler.sendEmptyMessageDelayed(0, sec);
    }

    public void stop() {
        this.callBack = null;
    }

    public interface OnCallBack {
        void onSuccess();

        void onFailed();
    }

    public void destory() {
        handler.removeCallbacksAndMessages(null);
    }
}

然后在CenterMarkerMapFragment 的代码如下:

/**
 * @author jikun
 *         Created by jikun on 2018/3/13.
 */

public class CenterMarkerMapFragment extends BaseMapFragment {

    private CenterMarkerView centerMarkerView;
    private DelayTest delayTest;

    public static CenterMarkerMapFragment newInstance() {

        Bundle args = new Bundle();

        CenterMarkerMapFragment fragment = new CenterMarkerMapFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_marker_map, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        TextureMapView textureMapView = view.findViewById(R.id.textureMapView);
        delayTest = new DelayTest();
        initMapView(textureMapView, savedInstanceState);

        centerMarkerView = new CenterMarkerView();
        centerMarkerView.initInfoWindowsView(getContext(), getAMap());

        getAMap().setOnMapLoadedListener(new AMap.OnMapLoadedListener() {
            @Override
            public void onMapLoaded() {
                centerMarkerView.addCenterMarker(getAMap());
                moveCameraOnMap();
            }
        });
        getAMap().setOnCameraChangeListener(new AMap.OnCameraChangeListener() {
            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                delayTest.stop();
                centerMarkerView.hideCenterMarkerInfoWindow();


            }

            @Override
            public void onCameraChangeFinish(CameraPosition cameraPosition) {

                centerMarkerView.showLoadingInfoWindow();

                delayTest.getDelayPost(new DelayTest.OnCallBack() {
                    @Override
                    public void onSuccess() {
                        centerMarkerView.showSuccessInfoWindow();
                    }

                    @Override
                    public void onFailed() {
                        centerMarkerView.showErrorInfoWindow();

                    }
                });


            }
        });


    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        centerMarkerView.destorty();
        delayTest.destory();
    }


    /**
     * 移动地图到Marker标记点位置的方法。
     */
    private void moveCameraOnMap() {
        LatLng latLng = new LatLng(30.657505, 104.065692);
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLng(latLng);
        getAMap().moveCamera(cameraUpdate);
    }

最后我们就实现如下效果

由于篇幅问题,

逆地理编码和 显示周围车辆我就放在下一篇中讲解了。

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