如何将相机居中,使标记位于屏幕底部?(Google地图API V2 Android)

54

当点击标记时,相机的默认行为是将其居中显示在屏幕上,但由于我通常在信息窗口中有很长的文本描述,所以更方便的做法是实际上改变相机的位置,使标记位于屏幕底部(使信息窗口位于屏幕中央)。我认为我应该能够通过覆盖onMarkerClick函数来实现这一点,如下所示(当此函数返回true时取消默认行为)。

@Override

public boolean onMarkerClick(final Marker marker) {


    // Google sample code comment : We return false to indicate that we have not

    // consumed the event and that we wish
    // for the default behavior to occur (which is for the camera to move
    // such that the
    // marker is centered and for the marker's info window to open, if it
    // has one).

    marker.showInfoWindow();

            CameraUpdate center=
                CameraUpdateFactory.newLatLng(new LatLng(XXXX,
                                                         XXXX));
            mMap.moveCamera(center);//my question is how to get this center

            // return false;
    return true;
}

编辑:

根据最佳答案的步骤解决了问题,以下是代码:

@Override

    public boolean onMarkerClick(final Marker marker) {

                //get the map container height
        LinearLayout mapContainer = (LinearLayout) findViewById(R.id.map_container);
        container_height = mapContainer.getHeight();

        Projection projection = mMap.getProjection();

        LatLng markerLatLng = new LatLng(marker.getPosition().latitude,
                marker.getPosition().longitude);
        Point markerScreenPosition = projection.toScreenLocation(markerLatLng);
        Point pointHalfScreenAbove = new Point(markerScreenPosition.x,
                markerScreenPosition.y - (container_height / 2));

        LatLng aboveMarkerLatLng = projection
                .fromScreenLocation(pointHalfScreenAbove);

        marker.showInfoWindow();
        CameraUpdate center = CameraUpdateFactory.newLatLng(aboveMarkerLatLng);
        mMap.moveCamera(center);
        return true;



    }

谢谢您的帮助 ^ ^


1
挑战在于你需要知道从屏幕中心到底部的角度,以确定缩放级别和屏幕尺寸。 - Larry McKenzie
也许你可以将“moveCamera”更改为“animateCamera”,以实现平滑移动 ;) - William Da Silva
对于任何寻找此功能的人,您可以在地图视图顶部添加所需的填充,然后将地图居中于标记上。然后将填充设置为零。这种方法有些取巧,但是有效。 - Adil Soomro
10个回答

59

我可能会在以后编辑这个答案提供一些代码,但我认为可以这样做:

  1. 获取点击标记的 LatLngLatLng M)。
  2. 使用 Projection.toScreenLocation(LatLng) 方法将 LatLng M 转换为一个点(Point M)。这给你了标记在设备显示器上的位置(以像素为单位)。
  3. 计算一个点(New Point),该点位于 Point M 上方的一半地图高度处。
  4. New Point 转换回 LatLng 并将地图居中于它。

查看这里获取如何获取地图高度的答案。

    // googleMap is a GoogleMap object
    // view is a View object containing the inflated map
    // marker is a Marker object
    Projection projection = googleMap.getProjection();
    LatLng markerPosition = marker.getPosition();
    Point markerPoint = projection.toScreenLocation(markerPosition);
    Point targetPoint = new Point(markerPoint.x, markerPoint.y - view.getHeight() / 2);
    LatLng targetPosition = projection.fromScreenLocation(targetPoint);
    googleMap.animateCamera(CameraUpdateFactory.newLatLng(targetPosition), 1000, null);

不,地图不必覆盖整个屏幕。 - zbr
除非您指的是地图视图,否则您是正确的。 - Larry McKenzie
明白了,我在考虑视图中地图的高度。 - Larry McKenzie
2
可以确认当相机倾斜和旋转时,它无法正常工作。 - Nima GT
非常好用,谢谢 :) - Ashutosh Chamoli
显示剩余4条评论

9
我更喜欢Larry McKenzie的答案,它不依赖于屏幕投影(即mProjection.toScreenLocation()),我猜测当地图缩放级别较低时,投影分辨率会变差,这有时会导致我无法获得准确的位置。因此,基于Google地图规范的计算肯定能解决这个问题。
以下是将标记移动到屏幕底部30%的示例代码。
zoom_lvl = mMap.getCameraPosition().zoom;
double dpPerdegree = 256.0*Math.pow(2, zoom_lvl)/170.0;
double screen_height = (double) mapContainer.getHeight();
double screen_height_30p = 30.0*screen_height/100.0;
double degree_30p = screen_height_30p/dpPerdegree;      
LatLng centerlatlng = new LatLng( latlng.latitude + degree_30p, latlng.longitude );         
mMap.animateCamera( CameraUpdateFactory.newLatLngZoom( centerlatlng, 15 ), 1000, null);

我认为这是比被接受的解决方案更好的解决方案,特别是当涉及到倾斜的相机时。 - mdelolmo
1
该方法无法在所有分辨率设备上使用。 - Anurag Srivastava

6

如果您不关心地图缩放,只希望标记在底部,请参见以下内容,我认为这是更简单的解决方案。

double center = mMap.getCameraPosition().target.latitude;
double southMap = mMap.getProjection().getVisibleRegion().latLngBounds.southwest.latitude;

double diff = (center - southMap);

double newLat = marker.getPosition().latitude + diff;

CameraUpdate centerCam = CameraUpdateFactory.newLatLng(new LatLng(newLat, marker.getPosition().longitude));

mMap.animateCamera(centerCam);

3

我遇到了同样的问题,我尝试了下面这个完美运作的解决方案

mMap.setOnMarkerClickListener(new OnMarkerClickListener() 
        {
            @Override
            public boolean onMarkerClick(Marker marker)
            {
                int yMatrix = 200, xMatrix =40;

                DisplayMetrics metrics1 = new DisplayMetrics();
                getWindowManager().getDefaultDisplay().getMetrics(metrics1);
                switch(metrics1.densityDpi)
                {
                case DisplayMetrics.DENSITY_LOW:
                    yMatrix = 80;
                    xMatrix = 20;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    yMatrix = 100;
                    xMatrix = 25;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    yMatrix = 150;
                    xMatrix = 30;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    yMatrix = 200;
                    xMatrix = 40;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    yMatrix = 200;
                    xMatrix = 50;
                    break;
                }

                Projection projection = mMap.getProjection();
                LatLng latLng = marker.getPosition();
                Point point = projection.toScreenLocation(latLng);
                Point point2 = new Point(point.x+xMatrix,point.y-yMatrix);

                LatLng point3 = projection.fromScreenLocation(point2);
                CameraUpdate zoom1 = CameraUpdateFactory.newLatLng(point3);
                mMap.animateCamera(zoom1);
                marker.showInfoWindow();
                return true;
            }
        });

3

我也遇到了这个问题,并以一种hacky的方式解决了它。首先声明一个double字段。您需要根据自己的要求调整它的值,但我建议您将其保持在0.001〜0.009之间,否则您可能会在缩放动画后错过标记。

  double offset = 0.009 
  /*You can change it based on your requirement.
 For left-right alignment please kindly keep it between 0.001~0.005 */

底部居中:

    LatLng camera = new LatLng(marker.getPosition().latitude+offset , marker.getPosition().longitude);
//Here "marker" is your target market on which you want to focus

对于顶部居中:

LatLng camera = new LatLng(marker.getPosition().latitude-offset , marker.getPosition().longitude);

对于左对齐:

LatLng camera = new LatLng(marker.getPosition().latitude, marker.getPosition().longitude+offset);

对于右对齐:

LatLng camera = new LatLng(marker.getPosition().latitude-offset , marker.getPosition().longitude-offset);

然后最后调用 -
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(camera, yourZoom));

1

我做了一些研究,根据文档显示地图是正方形的,在零缩放级别下,宽度和高度为256dp,北/南纬度为+/- 85度。随着缩放级别的增加,地图宽度也会增加,因此宽度和高度= 256 * 2N dp,其中N是缩放级别。因此,理论上您可以通过获取地图高度并将其除以170度来获得每度的dp,从而确定新位置。然后,将屏幕高度(或mapview高度)以dp为单位除以二,并将半视图大小转换为纬度度数。然后将您的新相机点设置在南纬那么多度。如果需要,我可以添加代码,但我现在正在使用手机。


0

任何仍在寻找根据位置坐标居中相机的人

CameraPosition cameraPosition = new CameraPosition.Builder().target(new LatLng(Lat, Lon))
        .zoom(15) 
        .bearing(0)
        .tilt(45)
        .build();
    map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

信用


0

经过一些经验,我已经实现了适合我的解决方案。

DisplayMetrics metrics = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(metrics);

Point targetPoint = new Point(metrics.widthPixels / 2, metrics.heightPixels - metrics.heightPixels / 9);
LatLng targetLatlng = map.getProjection().fromScreenLocation(targetPoint);
double fromCenterToTarget = SphericalUtil.computeDistanceBetween(map.getCameraPosition().target, targetLatlng);

LatLng center = SphericalUtil.computeOffset(new LatLng(location.latitude, location.longitude), fromCenterToTarget/1.2, location.bearing);
CameraUpdate camera = CameraUpdateFactory.newLatLng(center);
map.animateCamera(camera, 1000, null);

首先,我们选择屏幕上的物理点来移动标记。然后将其转换为LatLng。下一步 - 计算从当前标记位置(在中心)到目标的距离。最后,我们将地图的中心直接从标记移动到计算出的距离。


0

我已经尝试了这里提出的所有解决方案,并将它们进行了综合实现。考虑到地图投影、倾斜、缩放和信息窗口高度。

它并没有真正将标记放在“相机视图”的底部,但我认为在大多数情况下,它很好地适应了信息窗口和标记中心。

@Override
public boolean onMarkerClick(Marker marker) {
    mIsMarkerClick = true;
    mHandler.removeCallbacks(mUpdateTimeTask);
    mLoadTask.cancel(true);
    getActivity().setProgressBarIndeterminateVisibility(false);
    marker.showInfoWindow();
    Projection projection = getMap().getProjection();
    Point marketCenter = projection.toScreenLocation(marker.getPosition());
    float tiltFactor = (90 - getMap().getCameraPosition().tilt) / 90;
    marketCenter.y -= mInfoWindowAdapter.getInfoWindowHeight() / 2 * tiltFactor;
    LatLng fixLatLng = projection.fromScreenLocation(marketCenter);

    mMap.animateCamera(CameraUpdateFactory.newLatLng(fixLatLng), null);

    return true;
}

然后,您的自定义适配器将必须保留信息窗口膨胀视图的实例,以便能够获取其高度。

public int getInfoWindowHeight(){
    if (mLastInfoWindoView != null){
        return mLastInfoWindoView.getMeasuredHeight();
    }
    return 0;
}

自定义适配器必须保留信息窗口填充视图的实例,以便能够获取其高度。--> 如何实现?您能给出一些代码或逻辑吗?谢谢 - Dika

0

我需要类似的东西,但也要考虑缩放、倾斜和方位。

我的问题更加复杂,但解决方案是一种通用化的方法,因此它也可以应用于问题中的解决方案。

在我的情况下,我通过编程方式更新标记的位置;相机可以旋转、缩放和倾斜,但我希望标记始终以特定百分比的视图高度从底部可见。(类似于地图导航中的汽车标记位置)

解决方案:

首先,我选择屏幕中心的地图位置和一个点的位置,该点将在视图底部的某个百分比处可见(使用地图投影);我得到这两个点之间的距离(以米为单位),然后计算一个位置,从标记位置开始,沿着方向移动计算出的距离;这个新位置就是我的新相机目标。

代码(Kotlin):

val movePointBearing =
      if (PERCENTAGE_FROM_BOTTOM > 50) {
          (newBearing + 180) % 360
      } else newBearing

val newCameraTarget = movePoint(
       markerPosition,
       distanceFromMapCenter(PERCENTAGE_FROM_BOTTOM),
       markerBearing)

使用从这里复制的movePoint方法: https://dev59.com/n2kw5IYBdhLWcg3wHm9-#43225262

以及定义的distanceFromMapCenter方法:

fun distanceFromMapCenter(screenPercentage: Int): Float {
    val screenHeight = mapFragment.requireView().height
    val screenWith = mapFragment.requireView().width
    val projection = mMap.projection
    val center = mMap.cameraPosition.target
    val offsetPointY = screenHeight - (screenHeight * screenPercentage / 100)
    val offsetPointLocation = projection.fromScreenLocation(Point(screenWith / 2, offsetPointY))
    return distanceInMeters(center, offsetPointLocation)
}

那么只需定义一个distanceInMeters方法(例如使用Android Location类)

我希望这个想法清晰明了,无需进一步解释。

显而易见的限制是:它将使用当前的缩放和倾斜角度来应用逻辑,因此如果新的相机位置也需要不同的缩放级别和倾斜角度,则无法正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接