如何使相机居中,使标记位于屏幕的底部? (谷歌地图api V2 Android)

时间:2013-05-26 21:49:33

标签: android google-maps android-maps

单击标记时,摄像机的默认行为是将其置于屏幕中心,但由于我通常在信息窗口中有长文本说明,因此实际更改摄像机位置以便标记打开更方便屏幕底部(使信息窗口位于屏幕中央)。我想我应该能够通过覆盖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;



    }

感谢您帮助^ ^

11 个答案:

答案 0 :(得分:46)

我可能稍后会编辑这个答案以提供一些代码,但我认为可行的是:

  1. 获取所点击标记的LatLng LatLng M )。
  2. 使用Projection.toScreenLocation(LatLng)方法将 LatLng M 转换为Point Point M )。这将为您提供设备显示屏上标记的位置(以像素为单位)。
  3. 计算地图高度的一半 Point M 上方的点(新点)的位置。
  4. 新点转换回LatLng并将地图置于其中心。
  5. 查看here我的答案,了解如何获取地图的高度。

        // 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);
    

答案 1 :(得分:18)

是的,使用Projection类。更具体地说:

获取地图的预测:

 Projection projection = map.getProjection();

获取标记的位置:

 LatLng markerLocation = marker.getPosition();

将位置传递给Projection.toScreenLocation()方法:

 Point screenPosition = projection.toScreenLocation(markerLocation);

像这样你可以相对于中心或屏幕周围移动你的标记

Point mappoint = googleMap.getProjection().toScreenLocation(new LatLng(latitude, longitude));
        mappoint.set(mappoint.x, mappoint.y-30);
        googleMap.animateCamera(CameraUpdateFactory.newLatLng(googleMap.getProjection().fromScreenLocation(mappoint)));

答案 2 :(得分:6)

我更喜欢Larry McKenzie的答案,它不依赖于屏幕投影(即mProjection.toScreenLocation()),我的猜测是当地图缩放级别低时投影分辨率会变差,这让我有时候不能获得准确的位置。因此,基于谷歌地图规范的计算肯定会解决问题。

以下是将标记从底部移动到屏幕尺寸的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);

答案 3 :(得分: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;
            }
        });

答案 4 :(得分:3)

如果您不关心地图放大并且只想将标记放在底部,请参阅下文,我认为这是一个更简单的解决方案

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);

答案 5 :(得分:1)

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

答案 6 :(得分:1)

我也遇到了这个问题,并以骇人的方式解决了它。让我们首先声明一个双字段。您需要根据需要调整其值,但我建议您将其保留在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));

答案 7 :(得分: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;
}

答案 8 :(得分:0)

仍然希望根据位置坐标使相机居中的人

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

Credits

答案 9 :(得分: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。下一步 - 计算从当前标记位置(中心)到目标的距离。最后,我们将地图中心从标记直接移动到计算距离。

答案 10 :(得分:0)

我需要类似的东西,但方程式还需要缩放,倾斜和支撑。

我的问题比较复杂,但是解决方案只是一种概括,因此也可以应用于问题中的问题。

就我而言,我以编程方式更新了标记的位置;摄像机可以旋转,缩放和倾斜,但是我希望标记始终在从底部开始的特定视图高度的百分比处可见。 (类似于“地图”导航中的汽车标记位置)

解决方案:

我首先选择屏幕中心的地图位置,以及从底部(以底部视图)的一定百分比可见的点的位置(使用地图投影);我得到了两点之间的距离(以米为单位),然后从标记位置开始计算了一个位置,并朝着轴承方向移动了计算出的距离;这个新职位是我的新Camera目标。

代码(科特琳):

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

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

,其中的movePoint方法是从此处复制的: https://stackoverflow.com/a/43225262/2478422

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类)

我希望这个主意很明确,无需进一步解释。

一个明显的局限性:它使用当前的缩放和倾斜来应用逻辑,因此如果新的摄像头位置也需要不同的zoom_level和倾斜,则该逻辑将不起作用。