简体   繁体   English

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

[英]How to center the camera so that marker is at the bottom of screen? (Google map api V2 Android)

When a marker is clicked, the default behavior for the camera is to center it on screen, but because I usually have long text description in the info window, it's more convenient to actually change the camera position so that the marker is on the bottom of screen(making the info window in the center of screen).单击标记时,相机的默认行为是将其在屏幕上居中,但是因为我通常在信息窗口中有很长的文本描述,所以实际更改相机位置以便标记位于屏幕底部更方便屏幕(使信息窗口位于屏幕中央)。 I think I should be able to do that by overriding onMarkerClick function like below (the default behavior is cancelled when this function return true)我想我应该能够通过像下面这样覆盖 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;
}

Edit:编辑:

Problem solved using accepted answer's steps, codes below:使用已接受答案的步骤解决了问题,代码如下:

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



    }

Thanks for helping ^ ^谢谢你的帮助^ ^

I might edit this answer later to provide some code, but what I think could work is this:稍后我可能会编辑此答案以提供一些代码,但我认为可行的是:

  1. Get LatLng ( LatLng M ) of the clicked marker.获取点击标记的LatLng ( LatLng M )。
  2. Convert LatLng M to a Point ( Point M ) using the Projection.toScreenLocation(LatLng) method.使用Projection.toScreenLocation(LatLng)方法将LatLng M转换为Point ( Point M )。 This gives you the location of the marker on the device's display (in pixels).这为您提供了设备显示屏上标记的位置(以像素为单位)。
  3. Compute the location of a point ( New Point ) that's above Point M by half of the map's height.计算点 ( New Point ) 的位置,该点位于M上方地图高度的一半。
  4. Convert the New Point back to LatLng and center the map on it.新点转换回LatLng并将地图置于其中心。

Look here for my answer on how to get the map's height.在这里查看我关于如何获取地图高度的答案。

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

Yes, use Projection class. 是的,使用Projection类。 More specifically: 进一步来说:

Get Projection of the map: 获取地图的投影:

 Projection projection = map.getProjection();

Get location of your marker: 获取标记的位置:

 LatLng markerLocation = marker.getPosition();

Pass location to the Projection.toScreenLocation() method: 将位置传递给Projection.toScreenLocation()方法:

 Point screenPosition = projection.toScreenLocation(markerLocation);

Like this you can move your marker relative to center or around the screen 像这样你可以相对于中心或屏幕周围移动你的标记

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

I prefer Larry McKenzie's answer which it doesn't depend on screen projection (ie mProjection.toScreenLocation()), my guess is the projection resolution will go poor when the map zoom level is low, it made me sometimes couldn't get an accurate position.我更喜欢 Larry McKenzie 的答案,它不依赖于屏幕投影(即 mProjection.toScreenLocation()),我的猜测是当地图缩放级别低时投影分辨率会变差,这让我有时无法获得准确的位置。 So, calculation based on google map spec will definitely solve the problem.所以,基于谷歌地图规范的计算肯定会解决这个问题。

Below is an example code of moving the marker to 30% of the screen size from bottom.下面是将标记从底部移动到屏幕尺寸的 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);

If you don't care about the map zooming in and just want the marker to be at the bottom see below, I think it's a simpler solution如果您不关心地图放大并且只希望标记位于底部(见下文),我认为这是一个更简单的解决方案

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

I had the same issue, I tried the following perfectly working solution我遇到了同样的问题,我尝试了以下完美的解决方案

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

I did a little research and according to the documentation the map is square and at zero zoom level the width and height is 256dp and +/- 85 degrees N/S.我做了一些研究,根据文档,地图是方形的,在零缩放级别时,宽度和高度为 256dp 和 +/- 85 度 N/S。 The map width increases with zoom level so that width and height = 256 * 2N dp.地图宽度随着缩放级别的增加而增加,因此宽度和高度 = 256 * 2N dp。 Where N is the zoom level.其中 N 是缩放级别。 So in theory you can determine the new location by getting the map height and dividing it by 170 total degrees to get dp per degree.因此,理论上您可以通过获取地图高度并将其除以 170 总度数来获得每度 dp 来确定新位置。 Then get the screen height (or mapview height) in dp divided it by two and convert half view size to degrees of latitude.然后将 dp 中的屏幕高度(或地图视图高度)除以二,并将半视图大小转换为纬度。 Then set your new Camera point that many degrees of latitude south.然后将您的新相机点设置为向南多纬度。 I can add code if you need it but I'm on a phone at the moment.如果您需要,我可以添加代码,但我目前正在使用电话。

I also faced this problem and fixed it in a hacky way.我也遇到了这个问题并以一种hacky的方式修复了它。 Let's declare a double field first.让我们先声明一个双字段。 You need to adjust the value of it based on your requirement but I recommend you keep it between 0.001~0.009 otherwise you can miss your marker after the zoom animation.您需要根据您的要求调整它的值,但我建议您将其保持在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 */

For bottom-centered:对于底部居中:

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

For top-centered:对于顶部居中:

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

For left-centered:对于左居中:

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

For right-centered:对于右居:

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

Then finally call the -然后最后调用 -

mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(camera, yourZoom));

I have been trying out all the solutions proposed here, and came with a combined implementation of them.我一直在尝试这里提出的所有解决方案,并结合了它们的实现。 Considering, map projection, tilt, zoom and info window height.考虑到地图投影、倾斜、缩放和信息窗口高度。

It doesn't really place the marker at the bottom of the "camera view", but I think it accommodates the info window and the marker centre pretty well in most cases.它并没有真正将标记放在“相机视图”的底部,但我认为在大多数情况下它可以很好地容纳信息窗口和标记中心。

@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;
}

And then, your custom adapter would have to keep an instance of the info window inflated view, to be able to fetch its height.然后,您的自定义适配器必须保留信息窗口膨胀视图的实例,以便能够获取其高度。

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

Anyone who's still looking to center the camera according to location coordinates仍然希望根据位置坐标将相机居中的任何人

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

Credits学分

After some experiences i've implemented the solution that fine for me.经过一些经验,我已经实施了对我来说很好的解决方案。

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

Here.这里。 First, we pick the physical point on the screen where the marker should be moved.首先,我们选择屏幕上应该移动标记的物理点。 Then, convert it to LatLng.然后,将其转换为 LatLng。 Next step - calculate distance from current marker position (in center) to target.下一步 - 计算从当前标记位置(在中心)到目标的距离。 Finally, we move the center of map straight from the marker to calculated distance.最后,我们将地图中心从标记直接移动到计算距离。

I needed something similar, but with also zoom, tilt and bearing in the equation.我需要类似的东西,但在等式中还需要缩放、倾斜和轴承。

My problem is more complex, but the solution is a sort of generalization so it could be applied also to the problem in the question.我的问题更复杂,但解决方案是一种概括,因此它也可以应用于问题中的问题。

In my case, I update programmatically the position of a marker;就我而言,我以编程方式更新标记的位置; the camera can be rotated, zoomed and tilted, but I want the marker always visible at a specific percentage of the View height from the bottom.相机可以旋转、缩放和倾斜,但我希望标记始终在底部视图高度的特定百分比处可见。 (similar to the car marker position in the Maps navigation) (类似于地图导航中的汽车标记位置)

The solution:解决方案:

I first pick the map location on the center of the screen and the location of a point that would be visible at a percentage of the View from the bottom (using map projection);我首先选择屏幕中心的地图位置和从底部的视图百分比可见的点的位置(使用地图投影); I get the distance between these two points in meters, then I calculate a position, starting from the marker position, moving for the calculated distance towards the bearing direction;我得到这两个点之间的距离(以米为单位),然后我计算一个位置,从标记位置开始,向轴承方向移动计算出的距离; this new position is my new Camera target.这个新位置是我的新相机目标。

The code (Kotlin):代码(科特林):

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

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

with the movePoint method copied from here: https://stackoverflow.com/a/43225262/2478422使用从这里复制的movePoint方法: https : //stackoverflow.com/a/43225262/2478422

and the distanceFromMapCenter method defined as: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)
}

then just define a distanceInMeters method (for example using android Location class)然后只需定义一个distanceInMeters方法(例如使用 android Location 类)

I hope the idea is clear without any further explanations.我希望这个想法很清楚,不需要任何进一步的解释。

One obvious limitation: it applies the logic using the current zoom and tilt, so it would not work if the new camera position requires also a different zoom_level and tilt.一个明显的限制:它应用使用当前缩放和倾斜的逻辑,因此如果新的相机位置也需要不同的 zoom_level 和倾斜,它将不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Google Map API V2 - 如何在用户滚动地图时将标记保留在屏幕中央? - Google Map API V2 - How do I keep a marker in the center of the screen while user is scrolling the map? Android Google Maps v2 - 将标记保留在地图的中心 - Android Google Maps v2 - Keep marker in the center of map 重置标记位置或移动相机后,ListView中的MapView挂起(Google Map Android API v2) - MapView in ListView hangs after resetting marker location or moving camera (Google Map Android API v2) 避免使用Marker(谷歌地图为Android API V2)在点击地图上居中 - Avoid Marker (Google Maps for Android API V2) to center itself on map on click Android Google Map V2在地图屏幕中央绘制十字准线 - Android Google Map V2 Draw a crosshair in center of map screen Android - 自动更新标记位置Google地图api v2 - Android - Auto Update Marker Position Google map api v2 地图标记图标动画(适用于Android的Google Maps API V2) - Map Marker icon animation (Google Maps API V2 for Android) 谷歌地图android api v2标记阴影 - google map android api v2 marker shadows 将自定义属性添加到标记(Google Map Android API V2) - Adding custom property to marker (Google Map Android API V2) 具有设定范围的Google Android Maps API v2上的地图标记 - Map marker on Google Android Maps API v2 with set bounds
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM