在ScrollView中使用MapFragment

54
我有一个新的 MapFragment 放在一个 ScrollView 中。实际上它是一个 SupportMapFragment,但无论如何都可以。它能工作,但有两个问题:
  1. 滚动时,会留下黑色遮罩。黑色正好覆盖了地图所在的区域,除了 +/- 缩放按钮所在的洞口。请参见下面的截图。这是在 Android 4.0 上。
  2. 当用户与地图交互以防止 ScrollView 拦截触摸事件时,视图不使用 requestDisallowInterceptTouchEvent()。因此,如果您尝试在地图中垂直平移,它只会滚动包含的 ScrollView。理论上我可以派生一个视图类 MapView 并添加该功能,但如何让 MapFragment 使用我的自定义 MapView 而不是标准的视图呢?

Partial screenshot of scrolling issue


1
"我在ScrollView中有一个新的MapFragments,如果它能正常工作,我会感到惊讶。" "如何让MapFragment使用我自定义的MapView而不是标准的?" -- 据我所知,你不能这样做。你需要创建自己的片段来托管你的 MapView。" - CommonsWare
我也遇到了同样的问题,但我在Fragment中使用了原始的MapView。无论是在ScrollView还是ViewPager中滚动时,都会留下黑色蒙版的同样问题。 - Chris Horner
在我的 Nexus S(Android 4.1.2)上,我没有遇到这个问题。然而,在我测试过的所有 Android 2.2-2.3 设备上,我都遇到了完全相同的问题。 - Ralf
8个回答

53

在地图视图片段上应用一个透明的图片似乎可以解决这个问题。它可能不是最美观的,但看起来可以工作。以下是显示这种方法的XML片段:

<RelativeLayout
        android:id="@+id/relativeLayout1"
        android:layout_width="match_parent"
        android:layout_height="300dp" >

        <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.MapFragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"/>

        <ImageView
            android:id="@+id/imageView123"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/temp_transparent" />         
</RelativeLayout>

4
我可以确认这个方法可行(尽管屏幕的某些部分仍会闪烁)。我最终使用了FrameLayout作为父容器,并在透明视图中使用了一个纯粹的<View>,设置了android:background="@android:color/transparent" - Ralf
1
谢谢,伙计。我参考了很多不起作用的东西,但你的简单而棒。 - appukrb
当使用MapView而不是MapFragment时,是否有类似的解决方法? - Jorge Mendez

36

对于我来说,添加一个透明的 ImageView 并不能完全消除黑色遮罩。在滚动地图时,地图的顶部和底部仍然显示黑色遮罩。

所以,我在这个答案中找到了解决方法,并进行了小改动。 我添加了以下内容:

android:layout_marginTop="-100dp"
android:layout_marginBottom="-100dp"

因为我的地图片段是垂直滚动视图,所以我对其进行了调整。因此,我的布局现在是这样的:

<RelativeLayout
    android:id="@+id/map_layout"
    android:layout_width="match_parent"
    android:layout_height="300dp">

    <fragment
        android:id="@+id/mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="-100dp"
        android:layout_marginBottom="-100dp"
        android:name="com.google.android.gms.maps.MapFragment"/>

    <ImageView
        android:id="@+id/transparent_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@color/transparent" />

</RelativeLayout>   
为了解决问题的第二部分,我为我的主ScrollView设置了requestDisallowInterceptTouchEvent(true)。当用户触摸透明图像并移动时,我禁用了MotionEvent.ACTION_DOWNMotionEvent.ACTION_MOVE上的透明图像触摸,以便地图片段可以接受触摸事件。
ScrollView mainScrollView = (ScrollView) findViewById(R.id.main_scrollview);
ImageView transparentImageView = (ImageView) findViewById(R.id.transparent_image);

transparentImageView.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action) {
           case MotionEvent.ACTION_DOWN:
                // Disallow ScrollView to intercept touch events.
                mainScrollView.requestDisallowInterceptTouchEvent(true);
                // Disable touch on transparent view
                return false;

           case MotionEvent.ACTION_UP:
                // Allow ScrollView to intercept touch events.
                mainScrollView.requestDisallowInterceptTouchEvent(false);
                return true;

           case MotionEvent.ACTION_MOVE:
                mainScrollView.requestDisallowInterceptTouchEvent(true);
                return false;

           default: 
                return true;
        }   
    }
});

这对我起作用了,希望它能帮助到你。


1
@Abdul Wahab:是的。我是这样设置的,现在可以在地图上点击标记了。 - Simulant
谢谢亲爱的,这很有帮助。 - Abdul Wahab
谢谢,这解决了两个问题,这就是为什么应该被接受为答案的原因。 - Alex K.
mGoogleMap.getUiSettings().setMyLocationButtonEnabled(true) 对于这个解决方案无效。 - Tim

8

这可能源于与此问题相同的根源。那里的解决方案是使用一个透明的框架,比透明图像轻一些。


5

经过大量的研究和开发,我们终于完成了:

fragment_one.xml 应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/scrollViewParent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="400dip" >

            <com.google.android.gms.maps.MapView
                android:id="@+id/mapView"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <View
                android:id="@+id/customView"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:background="@android:color/transparent" />
        </RelativeLayout>

        <!-- Your other elements are here -->

    </LinearLayout>

</ScrollView>

您的Java类FragmentOne.java看起来像:

private GoogleMap mMap;
private MapView mapView;
private UiSettings mUiSettings;
private View customView

onCreateView

mapView = (MapView) rootView.findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);

if (mapView != null) {
   mMap = mapView.getMap();
   mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
   mUiSettings = mMap.getUiSettings();
   mMap.setMyLocationEnabled(true);
   mUiSettings.setCompassEnabled(true); 
   mUiSettings.setMyLocationButtonEnabled(false);
}


scrollViewParent = (ScrollView)rootView.findViewById(R.id.scrollViewParent);
customView = (View)rootView.findViewById(R.id.customView);

customView.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        // Disallow ScrollView to intercept touch events.
                        scrollViewParent.requestDisallowInterceptTouchEvent(true);
                        // Disable touch on transparent view
                        return false;

                    case MotionEvent.ACTION_UP:
                        // Allow ScrollView to intercept touch events.
                        scrollViewParent.requestDisallowInterceptTouchEvent(false);
                        return true;

                    case MotionEvent.ACTION_MOVE:
                        scrollViewParent.requestDisallowInterceptTouchEvent(true);
                        return false;

                    default:
                        return true;
                }
            }
        });

我遇到了额外的复杂性,因为我处于一个嵌套的小部件结构中。但最终通过使用您的解决方案,我找到了出路。(我的滚动视图只能通过使用view.getParent()才能访问) - Spidfire
1
非常好用,当得票最高的答案在我的情况下失败时,它帮助了我...谢谢。 - Sachin Varma

2

不需要费力地使用自定义滚动视图和透明布局文件。只需使用更轻的谷歌地图版本,您的问题就可以解决。

  map:liteMode="true"

在您的布局文件中的地图片段中添加上述属性,问题将得到解决。

我认为更快、更轻量级和更好的答案。 - arniotaki

2
今日免费次数已满, 请开通会员/明日再来
<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:text="Another elements in scroll view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />


        <com.erolsoftware.views.MyMapFragmentContainer
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="250dp">

            <fragment
                xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/eventMap"
                tools:context="com.erolsoftware.eventapp.EventDetails"
                android:name="com.google.android.gms.maps.SupportMapFragment"/>

        </com.erolsoftware.views.MyMapFragmentContainer>

        <TextView
            android:text="Another elements in scroll view2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />



    </LinearLayout>




</ScrollView>
容器类:
public class MyMapFragmentContainer extends LinearLayout {


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN)
        {
            ViewParent p = getParent();
            if (p != null)
                p.requestDisallowInterceptTouchEvent(true);
        }

        return false;
    }

    public MyMapFragmentContainer(Context context) {
        super(context);
    }

    public MyMapFragmentContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyMapFragmentContainer(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

}

0
对于问题的第二部分 - 你可以从SupportMapFragment派生一个片段类,并在布局中使用它。 然后,您可以重写 Fragment#onCreateView 并在其中实例化自定义MapView。
如果这不起作用,您总是可以创建自己的片段 - 然后您只需要注意自己调用所有生命周期方法(onCreate,onResume等)。 这个答案有一些更多细节。

0
<ScrollView
            android:layout_width="match_parent"
    ::
    ::
    ::
      <FrameLayout
            android:id="@+id/map_add_business_one_rl"
            android:layout_width="match_parent"
            android:layout_height="100dp"
         >
                        <fragment

                            android:id="@+id/map"
                            android:name="com.google.android.gms.maps.SupportMapFragment"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"
                            android:layout_marginTop="-100dp"
                            android:layout_marginBottom="-100dp"

                            />

        </FrameLayout>

    ::
    ::
    ::
     </ScrollView>

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