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

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

  • ViewPager
  • ScrollView
  • ListView
  • 或上述的SlidingMenu

当你移动它时,它会在原来的像素位置留下一个黑洞。 看起来像这样

这个问题可以通过在SurfaceView上指定一个透明的背景或者在它上面放置一个透明的View来部分解决。 我只是不能确定如果只为我的结果是不可接受的,或者如果我有一个不同的问题,使它看起来如何。

要了解它的外观,请观看本video 。

(抱歉,质量,但问题可以很容易地看到)

当一个普通的列表视图(橙色屏幕)被拉开,同时打开SlidingMenu ,animation是光滑的,很好。 但是一旦MapFragment出现, MapFragment就会出现一些奇怪的刷新/闪烁/同步问题。

testingHTC One S和三星Galaxy ACE。


我的超级疯狂的解决办法是:每次开始animation时,都要截取MapFragment (应该可以使用SurfaceView),并在animation期间放置它。 但是我真的不知道该怎么做…… 以地图的截图是不可能的 ,但也许有人会从中得到灵感。

或者也许禁用其他方式重新绘制地图,我不知道。


更新:

find了这个 。

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

另一个更新看来,这个问题已经在设备4.1+上解决了。 他们只是使用TextureView。 但在较低版本中,我们仍然需要使用解决方法。

当然,正确的解决scheme是让Google解决这个问题(请参阅Android Maps V2 issue 4639: http : //code.google.com/p/gmaps-api-issues/issues/detail? id=4639)。

不过,我的一位同事build议把地图扩展到容器之外。 如果我们将地图片段扩展到其容器的可见区域之外,如下所示:

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

我们可以减less/消除闪烁。 较新的设备(例如Galaxy Nexus)在黑客入侵之后显示没有闪烁,较旧的设备(例如LG Optimus V)显示闪烁减less。 我们必须扩大双方的利润空间,这样信息窗口才会被选中。


更新:Google在8月28日解决了这个问题,但我猜测它仍然需要发布。

你可以使用这个库

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

这对我来说工作得很好。

这是一个疯狂的想法,不要移动MapView 😉

也就是说,在FrameLayout中在您的应用程序后面创build一个MapView全屏宽度。 然后把你的意见放在最上面,然后在你需要显示的位置打一个洞到MapView。 移动时,您需要更新地图位置以保持同步,以便用户始终看到地图的同一部分,但这应该比移动表面视图更好。

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

实际上是在你的窗户后面看到的,而在窗户上打了一个洞让你看到它。 因此,你可以把东西放在你的窗口之上,但是在你的窗口后面不会出现任何东西。 原帖

这个架构是你看到这些闪烁的原因之一。

有些闪烁有时会出现双缓冲区: Android编码博客进一步解释 。

您也可以尝试子类SurfaceView来尝试绘制不同的背景。 我还没有看到这样的一个例子,它不会将z-index改为顶层的。 看看这个SOpost

否则,我build议只有在您的视图集中后才能获取SurfaceHolder (尝试并在此之前进行临时视图),并在未聚焦和屏幕上移除SurfaceView

地图的截图是不可能的,但也许有人会从这里得到启发。

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

这里是我的代码来设置快照(它是在地图相同的片段,称为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(使其匹配)。

在这里,我正在控制卷轴:

 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(片段)。

所以,无论您何时从地图上滚动,都会看到快照。 这对我来说很好。

在我的场景中,我有一个初始的片段,它在一个button上单击与其中的一个MapFragment进行交换。 我注意到,这种闪烁只发生在第一次交换片段时 – popup背堆栈,并且在相同types的另一个片段中交换不会导致闪烁。

所以算出它与添加一个初始的SurfaceView到窗口有关,我尝试添加一个SurfaceView的空实例到我最初加载的片段,在它的主视图后面。 和宾果,下一个片段animation时不再闪烁!

我知道这不是最干净的解决scheme,但它的工作原理。 Google MapFragment(或我的情况下的SupportMapFragment)仍然正常工作,因为未使用的SurfaceView位于前一个片段中。

我应该添加我使用的V2 API。

更新 @VenomVendor

我将空的SurfaceView添加到要显示的第一个片段中,在索引0处 – 在主视图之后。 在我的情况下,主页片段是包含Google地图片段的片段之前的片段,不确定是否重要:

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

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

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

在SlidingMenu的manageLayers方法中注释掉硬件加速。 这似乎是唯一的解决scheme,因为SurfaceViews不工作。

另一个替代解决scheme是使用新的GoogleMap.snapshot()函数,并使用地图的屏幕截图。 如此处所述。

我有列表视图滚动黑屏相同的问题,我在同一个屏幕上使用列表视图的地图片段我已经解决了这个问题, 在xml中我使用的魔术属性我说话列表视图只是我们必须把android: scrollingCache =“false”。我的问题是固定的试试这个属性来阻止滞后和闪烁在你的地图。

伙计们,我发现最好的解决scheme是通过这个实例化你的支持地图。 奇迹

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

尝试添加..

。getHolder()setFormat(PixelFormat.TRANSLUCENT);

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

如果可能,将FrameLayout更改为RelativeLayout

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

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

这对我工作…添加地图:zOrderOnTop =“true”为您的片段,因为这…

 <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" 

这是一个非常简单的解决方法,我用来摆脱ViewPager中的闪烁。 我可以想象,对于任何Fragment或其他dynamic添加的MapView来说都是如此。

这个问题似乎并不是MapView本身,而是运行MapView所需的资源。 这些资源只有在您第一次在您的活动中启动MapView时才会加载,并且可以像您所期望的那样再次用于每个后续的MapView。 资源的这种加载是导致屏幕闪烁的原因。

所以要删除闪烁,我只是在我的Activity的布局中包含另一个MapView(在活动中的位置是不相关的)。 一旦活动加载,我只是将额外的MapView的可视性设置为GONE。 这意味着当您使用MapViews启动任何片段时,您的MapView所需的所有资源都已准备就绪,而不会出现延迟,不闪烁和所有快乐。

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

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

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

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