移动MapFragment(SurfaceView)会导致黑色背景闪烁

时间:2013-01-19 22:24:13

标签: android surfaceview android-maps android-maps-v2 mapfragment

我正在尝试实施新的Android Google Maps API(v2)。但是SlidingMenu似乎并不顺利。如您所知,MapFragment实施基于SurfaceView。问题是SurfaceView不喜欢移动它 - 我的意思是把它放在可移动的视图中:

当您移动它时,它会在其像素最初放置的位置留下黑洞。它看起来像this

通过在SurfaceView上指定透明背景或甚至在其上放置透明View,可以部分解决此问题。我只是无法想象,如果只是对我来说结果是不可接受的,或者我有一个不同的问题,使它看起来如何。

要查看其外观,请观看THIS VIDEO

(对不起质量,但无论如何都可以轻松看到问题)

在打开SlidingMenu时拉开正常列表视图(橙色屏幕)时,动画非常流畅。但是只要出现MapFragment,就会在地图上出现一些奇怪的刷新/闪烁/同步问题。

在HTC One S和三星Galaxy ACE上进行测试。


我解决它的超级疯狂的想法:每次开始动画开始时,截取MapFragment的截图(应该可以使用SurfaceView)并将其放置一段时间动画。但我真的不知道怎么做...... Taking screenshot of a map isn't possible,但也许有人会受到启发。

或者禁止以其他方式重新绘制地图,我不知道。


更新

找到this

看起来可以将SurfaceView更改为TextureView以消除这些限制。但由于MapFragment基于SurfaceView,我不知道它是否可以实现。

另一次更新 看来此问题已在4.1+设备上得到解决。他们只使用了TextureView。但在较低版本中,我们仍然需要使用变通方法。

17 个答案:

答案 0 :(得分:14)

当然,正确的解决方案是谷歌解决问题(请参阅Android Maps V2问题4639:http://code.google.com/p/gmaps-api-issues/issues/detail?id=4639)。

然而,我的一位同事建议只是将地图扩展到其容器之外。如果我们将地图片段扩展到其容器的可见区域之外,如下所示:

android:layout_marginLeft="-40dp"
android:layout_marginRight="-40dp"

我们可以减少/消除闪烁。较新的设备(例如Galaxy Nexus)在此黑客攻击后没有显示闪烁,而较旧的设备(例如LG Optimus V)显示出减少的闪烁。我们必须扩展两边的边距,以便在选择信息窗口时居中。


更新:这个问题已于8月28日由谷歌修复,但我猜它仍然需要进入版本。

答案 1 :(得分:6)

您可以使用此库

https://github.com/NyxDigital/NiceSupportMapFragment/

这对我来说很好。

答案 2 :(得分:4)

这是一个疯狂的想法,不要移动MapView; - )

也就是说,在FrameLayout中在应用程序后面创建一个MapView全屏宽度。然后将您的视图放在顶部,并在需要显示它的位置向MapView打孔。移动时,您需要更新地图位置以保持同步,以便用户不断看到地图的相同部分,但这应该比移动表面视图更好。

答案 3 :(得分:3)

那么, 引用Dianne Hackborn(Android框架工程师)

  

表面视图实际上是在你的窗户后面,并在窗口打了一个洞让你看到它。因此,您可以在窗口中放置内容,但窗口中的任何内容都不会出现在窗口中。   Original post

这种架构是你看到这些闪烁的原因的一部分。

由于双缓冲区有时会出现一些闪烁:Android coding blog explains further

您还可以尝试子类化SurfaceView以尝试以不同方式绘制背景。我还没有看到一个这样的例子,它不会将z-index更改为top的。 Check out this SO post

否则,我建议您只在关注视图后获取SurfaceHolder(尝试在此之前进行临时视图)并在未对焦和屏幕时移除SurfaceView

答案 4 :(得分:2)

  

无法拍摄地图的屏幕截图,但也许会有人受此启发。

可以使用snapshot interface from GoogleMap。现在,您可以在滚动页面时显示地图的快照。有了这个,我用ViewPager解决了我的问题,也许你可以改变代码以适应你的SlidingMenu。

这是我设置快照的代码(它与map相同的片段,名为FragmentMap.java):

public void setSnapshot(int visibility) {
    switch(visibility) {
    case View.GONE:
        if(mapFragment.getView().getVisibility() == View.VISIBLE) {
            getMap().snapshot(new SnapshotReadyCallback() {
                @Override
                public void onSnapshotReady(Bitmap arg0) {
                    iv.setImageBitmap(arg0);
                }
            });
            iv.setVisibility(View.VISIBLE);
            mapFragment.getView().setVisibility(View.GONE);
        }
        break;
    case View.VISIBLE:
        if(mapFragment.getView().getVisibility() == View.GONE) {
            mapFragment.getView().setVisibility(View.VISIBLE);
            iv.setVisibility(View.GONE);
        }
        break;
    }
}

“mapFragment”是我的SupportedMapFragment,“iv”是ImageView(make it match_parent)。

我在这里控制卷轴:

pager.setOnPageChangeListener(new OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if(position == 0 && positionOffsetPixels > 0 || position == 1 && positionOffsetPixels > 0) {
                ((FragmentMap)adapter.getRegisteredFragment(1)).setSnapshot(View.GONE);
            } else if(position == 1 && positionOffsetPixels == 0) {
                ((FragmentMap)adapter.getRegisteredFragment(1)).setSnapshot(View.VISIBLE);
            }
        }
        @Override
        public void onPageScrollStateChanged(int arg0) {}
        @Override
        public void onPageSelected(int arg0) {}
    });

我的地图片段(FragmentMap)位于第1位,因此我需要控制从位置0到1以及从位置1到2(第一个if子句)的滚动。 “getRegisteredFragment()”是我的自定义FragmentPagerAdapter中的一个函数,其中我有一个名为“registeredFragments”的SparseArray(Fragment)。

因此,无论何时滚动到地图或从地图滚动,您都会看到它的快照。这对我很有用。

答案 5 :(得分:1)

在清单文件中添加android:hardwareAccelerated="true"

例如:<application android:name=".Sample" android:hardwareAccelerated="true" />

答案 6 :(得分:1)

另一种替代解决方案是使用新的GoogleMap.snapshot()功能,并使用地图的屏幕截图;正如here所述。

答案 7 :(得分:1)

在我的场景中,我有一个初始片段,它在按钮点击时交换了第二个片段,里面有一个MapFragment。我注意到这个闪烁只发生在你第一次交换片段时 - 弹出后面的堆栈,并且交换相同类型的另一个片段不会导致闪烁。

因此,想象它与向窗口添加初始SurfaceView有关,我尝试将SurfaceView的空实例添加到我最初加载的片段中,在它的主视图后面。宾果游戏,在下一个片段动画时不再闪烁!

我知道这不是最干净的解决方案,但它确实有效。谷歌MapFragment(或我的案例中的SupportMapFragment)仍然可以正常工作,因为未使用的SurfaceView位于上一个片段中。

我应该添加我正在使用v2 API。

更新 @VenomVendor

我将空的SurfaceView添加到要显示的第一个片段,在主视图后面的索引0处。在我的例子中,Home片段是包含Google Maps片段的片段,不确定是否重要:

import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class HomeFragment extends FragmentBase {
private SurfaceView sview;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_home, container, false);

    //this fixes google maps surface view black screen animation issue
    sview = new SurfaceView(getActivity());
    sview.setZOrderOnTop(true);    // necessary
    SurfaceHolder holder = sview.getHolder();
    holder.setFormat(PixelFormat.TRANSPARENT);

    container.addView(sview, 0);
    ...
}

答案 8 :(得分:1)

我遇到了与webview和SlidingMenu相同的问题,我通过修改主要开发人员解释的here的SlidingMenu源代码来解决。

  

注释掉SlidingMenu的manageLayers方法中的硬件加速。这似乎是SurfaceViews不起作用的唯一解决方案。

答案 9 :(得分:1)

为了防止创建黑洞的MapView(实际上是SurfaceView),我添加了一个透明背景颜色的空视图来覆盖整个MapView,这样就可以防止MapView生成黑洞“洞”。它可能适合您的情况。

答案 10 :(得分:0)

我在滚动列表视图时出现了同样的黑屏问题,我在同一个屏幕上使用了列表视图的地图片段我已经使用xml 中的神奇属性解决了这个问题我在哪里谈话列表视图只是我们必须把 android:scrollingCache =“false”。我的问题修复试试这个属性来阻止你的地图中的滞后和闪烁。

答案 11 :(得分:0)

我找到的最佳解决方案是通过此实例化您的支持地图。奇迹

SupportMapFragment mapFragment = SupportMapFragment.newInstance(new GoogleMapOptions().zOrderOnTop(true));

答案 12 :(得分:0)

尝试添加..

getHolder()setFormat(PixelFormat.TRANSLUCENT)。

到SurfaceView的构造函数中。 TextureView也应该可以工作,但是需要你放弃对sub api 14设备的支持。

答案 13 :(得分:0)

如果可能,将您的FrameLayout更改为RelativeLayout

答案 14 :(得分:0)

我遇到了同样的问题,并使用了基于更改“可见性”的解决方法。

private GoogleMap mMap;
private SupportMapFragment mMapFragment;

mMapFragment = ((SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.mapFragment));
mMap = mMapFragment.getMap();

//Change the visibility to 'INVISIBLE' before the animation to go to another fragment.
mMapFragment.getView().setVisibility(View.INVISIBLE);

//Change the visibility to 'VISIBLE' after coming back to the original fragment that has map fragment.
mMapFragment.getView().setVisibility(View.VISIBLE);

答案 15 :(得分:0)

这对我有用...... 添加地图:zOrderOnTop =&#34; true&#34;你的片段......

<fragment
                android:id="@+id/map"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:name="com.google.android.gms.maps.SupportMapFragment""
                map:zOrderOnTop="true"/>

请记住将此添加到您的父ScrollView

xmlns:map="http://schemas.android.com/apk/res-auto"

答案 16 :(得分:-1)

这是一个非常简单的解决方法,我曾经摆脱了ViewPager中的闪烁。我认为对于任何片段或其他动态添加的MapView都适用。

问题似乎并不是MapView本身,而是运行MapView所需的资源。这些资源仅在您第一次启动活动中的MapView并在每个后续MapView中重复使用时加载,如您所料。这种资源加载是导致屏幕闪烁的原因。

所以要删除闪烁,我只是在我的Activity布局中包含了另一个MapView(活动中的位置无关紧要)。一旦加载了Activity,我就将额外MapView的Visibility设置为GONE。这意味着当您使用没有延迟,没有闪烁,没有闪光的MapView启动任何碎片时,MapView所需的所有资源都已准备就绪。

这当然是一种解决方法,而不是对潜在问题的真正“解决方案”,但它会解决副作用。

所以要掩盖用于完整性的代码:

随机(但干净地)放置在我的活动布局中:

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

然后在我的活动类

private MapView flashMap;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

            // Code omitted

    try {
        MapsInitializer.initialize(this);
    } catch (GooglePlayServicesNotAvailableException e) {
        e.printStackTrace();
    }
    flashMap = (MapView)findViewById(R.id.flash_map);
    flashMap.onCreate(savedInstanceState);
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
        flashMap.onSaveInstanceState(outState);
}

@Override
public void onResume() {
    super.onResume();
    flashMap.onResume();
    flashMap.setVisibility(View.GONE); // Just like it was never there...
}

@Override
public void onPause() {
    flashMap.onPause();
    super.onPause();
}

@Override
public void onDestroy() {
    flashMap.onDestroy();
    super.onDestroy();
}

@Override
public void onLowMemory() {
    super.onLowMemory();
    flashMap.onLowMemory();
}