【问题标题】:Moving MapFragment (SurfaceView) causes black background flickering移动 MapFragment (SurfaceView) 导致黑色背景闪烁
【发布时间】:2013-01-03 09:06:05
【问题描述】:

我正在尝试实现新的 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。但在较低版本上,我们仍然必须使用变通方法。

【问题讨论】:

  • 地图将在滑动时继续处理命令和动画。这是一个很好的用户体验。在地图滑动时截屏覆盖地图会减损 UX 和 AFAIK,这是不可能的,请参阅 stackoverflow.com/questions/13773658/…
  • stackoverflow.com/a/13910364/1121889 用您的地图 v2 替换 viewpager
  • 我没有ViewPager。另外,我已经阅读了您引用的答案(甚至对此发表了评论)。
  • 我在表面视图方面遇到了同样的问题。我已将surfaceView 修改为textureView。我不知道mapfragment是否可以。
  • 我认为可以接受。我也认为它最终会被修复。如果在谷歌上点击有问题,我会为你加注星标。

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


【解决方案1】:

当然,正确的解决方案是由 Google 解决问题(请参阅 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 日由 Google 修复,但我猜它仍需要发布。

【讨论】:

  • 这个很有意思,我一定会试试的
  • 仅供参考,我必须添加 `android:layout_gravity="center"' 才能完成这项工作。很好的建议,谢谢!
  • 先生,这对我不起作用,还有其他解决方法吗?请帮忙
  • 这应该在当前的 Google Maps API 中得到修复。您是否尝试过升级您的 Google Play 服务库?那将在 $SDK_PATH/extras/google/google_play_services/libproject/google-play-services_lib
【解决方案2】:

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

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

【讨论】:

  • 这很有趣,但实施起来会很糟糕,特别是在视图页面或需要多个 MapFragment 并且每个显示不同地图的东西中。而SlidingMenu 将整个“上方”视图移到一边,这意味着我必须完全改变图书馆的工作方式。否则,非常有趣,所以 +1 :)
  • 完全同意将其作为通用解决方案实施是困难/不明智的,但可以仅适用于特定的一次性用例。希望它可以帮助推动更聪明的人考虑更好的解决方案;-)
  • 希望有一个更简单的解决方案。我想使用带有滑动菜单的地图的需求将会增长,并且不仅仅是一次性用例。
  • 我在对话框中的 viewpager 中有一个 mapview。通过将地图放在视图分页器后面并使视图分页器的第一项成为透明占位符(禁用地图手势)来解决。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

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

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

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

    您也可以尝试对SurfaceView 进行子类化,以尝试以不同的方式绘制背景。我还没有看到不会将 z-index 更改为顶部的示例。 Check out this SO post

    否则,我建议仅在您的视图聚焦后获取SurfaceHolder(尝试在此之前创建一个临时视图)并在未聚焦和屏幕上时移除SurfaceView

    【讨论】:

    • 奇怪的是,当你setZOrderOnTop(true) 时,闪烁并没有消失。它不再是黑色了,它变成了白色。但它仍然存在。性能或同步肯定存在一些问题,因为黑色背景不会保留在那里,它只会闪烁几分之一秒。如果我可以将 SlidingMenu 动画的帧与 SurfaceView 的刷新同步,它可能会消失...
    • 确实如此,但这实际上只允许您通过确保 SurfaceView 的背景颜色与其容器背景的颜色相同来避免问题的高可见性。我认为您不应该尝试同步帧速率或其他可能复杂的属性,因为这种行为甚至存在于您的手控制的最慢动作中。正如我所提到的,您可以通过子类化 SurfaceView 以编程方式在画布内绘制,但这会大大降低其性能。
    • 我不认为我跟随。 “在画布内绘图”会发生什么变化?我认为 MapFragment 在画布内绘制,但速度不够快。如果您的意思是更改背景使其与前景地图匹配(黑色闪烁出现在地图应该出现的位置,而不是出现在 SlidingMenu 的位置),那么我怀疑这是可能的,因为地图不是纯色背景,我必须截取屏幕截图或类似的东西。
    【解决方案5】:

    无法截取地图,但也许有人会从中得到启发。

    snapshot interface from 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(使其成为 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)。

    因此,无论何时滚动到地图或从地图滚动,您总会看到它的快照。这对我来说效果很好。

    【讨论】:

    • 这是 joecks 几天前注意到的,但感谢代码。
    【解决方案6】:

    在我的场景中,我有一个初始 Fragment,在单击按钮时,它会在第二个 Fragment 中交换,其中包含一个 MapFragment。我注意到这种闪烁仅在您第一次交换片段时发生 - 弹出返回堆栈,并在另一个相同类型的片段中交换不会导致闪烁。

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

    我知道这不是最干净的解决方案,但它确实有效。 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);
        ...
    }
    

    【讨论】:

      【解决方案7】:

      这对我有用... 将 map: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"
      

      【讨论】:

        【解决方案8】:

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

        【讨论】:

        • 它可以防止“黑洞”问题,但不是我描述的问题。请参阅我的问题的这一部分:“这个问题可以通过在 SurfaceView 上指定透明背景或什至在其上放置透明视图来部分解决......”
        • 哦~抱歉我没注意到 8-)
        【解决方案9】:

        我在使用 webview 和 SlidingMenu 时遇到了同样的问题,我通过修改 SlidingMenu 源代码解决了,正如主要开发人员 here 所解释的那样。

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

        【讨论】:

        • 如果我没记错的话,如果没有硬件加速,Map v2 就无法工作。
        【解决方案10】:

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

        【讨论】:

        • 哇,终于!这看起来很有希望
        【解决方案11】:

        尝试添加..

        getHolder().setFormat(PixelFormat.TRANSLUCENT);

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

        【讨论】:

          【解决方案12】:

          我在滚动列表视图时遇到了同样的黑屏问题,我在同一屏幕中使用带有列表视图的地图片段我已经通过使用我所在的神奇属性 in xml 解决了这个问题谈话列表视图只是我们必须放 android:scrollingCache="false"。我的问题已修复尝试使用此属性来停止地图中的滞后和闪烁。

          【讨论】:

            【解决方案13】:

            伙计们,我发现的最佳解决方案是通过此实例化您的支持地图。创造奇迹

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

            【讨论】:

              【解决方案14】:

              如果可能,将 FrameLayout 更改为 RelativeLayout

              【讨论】:

                【解决方案15】:

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

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

                【讨论】:

                  【解决方案16】:

                  这是我用来摆脱 ViewPager 中闪烁的一个非常简单的解决方法。我想这同样适用于任何 Fragment 或其他动态添加的 MapView。

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

                  所以为了消除闪烁,我只是在我的 Activity 布局中包含了另一个 MapView(在 Activity 中的位置无关紧要)。加载 Activity 后,我只需将额外 MapView 的 Visibility 设置为 GONE。这意味着,当您使用 MapView 启动任何 Fragment 时,您的 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();
                  }
                  

                  【讨论】:

                  • 但是您使用的是已弃用的 Map API v1,它与 SurfaceView 和我的问题无关。我知道你在说什么,我在尝试实例化多个 MapView 时遇到了同样的问题,但不幸的是,这不是我问题的答案。
                  • 这是使用 Map API v2。 “com.google.android.gms.maps.MapView”。 GMS 位是 v2。
                  • 也许我会再解释一下。我认为您遇到的闪烁不是因为地图的移动。我在 ViewPager 上有地图,它非常乐意四处滑动。快速移动时有黑边,但没有您所说的闪烁。在我的测试中,闪烁是由于资源加载引起的,我发现可以通过在加载活动时有效地预缓存地图数据来避免这种情况。
                  • “快速移动时有黑边,但不是您所说的闪烁” - 不是一回事吗?你看到问题中的视频了吗?您的 ViewPager 有何不同?很抱歉误读了您的答案和“v1”评论。
                  • 这不是解决手头问题的办法。
                  猜你喜欢
                  • 2012-04-19
                  • 1970-01-01
                  • 2014-01-22
                  • 2012-02-05
                  • 1970-01-01
                  • 2012-08-27
                  • 2020-09-22
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多