web-dev-qa-db-fra.com

Le déplacement de MapFragment (SurfaceView) provoque un scintillement du fond noir

J'essaie d'implémenter une nouvelle Android API Google Maps (v2). Cependant, cela ne semble pas bien aller avec SlidingMen . Comme vous le savez peut-être, MapFragment est basée sur SurfaceView. Le problème est que SurfaceView n'aime pas le déplacer - je veux dire le placer dans des vues mobiles:

Lorsque vous le déplacez, il laisse un trou noir à l'endroit où se trouvaient initialement ses pixels. Cela ressemble à this .

Ce problème peut être partiellement résolu en spécifiant un arrière-plan transparent sur le SurfaceView ou même en plaçant un View transparent dessus. Je n'arrive pas à comprendre si ce n'est que pour moi que les résultats sont inacceptables, ou si j'ai un problème différent qui le fait ressembler.

Pour voir à quoi cela ressemble, veuillez regarder CETTE VIDÉO .

(désolé pour la qualité, mais le problème peut être vu de toute façon facilement)

Lorsqu'une vue de liste normale (l'écran orange) est retirée lors de l'ouverture de SlidingMenu, l'animation est fluide et agréable. Mais dès que MapFragment apparaît, il y a un problème bizarre de rafraîchissement/scintillement/synchronisation sur la carte.

Testé sur HTC One S et Samsung Galaxy ACE.


Mon idée super folle de la résoudre: chaque fois que l'animation d'ouverture démarre, prenez une capture d'écran du MapFragment (cela devrait être possible avec SurfaceView) et déposez-la pendant la durée de l'animation. Mais je ne sais vraiment pas comment faire ... Prendre une capture d'écran d'une carte n'est pas possible , mais peut-être que quelqu'un s'en inspirera.

Ou peut-être désactiver le redessinage de la carte d'une autre manière, je ne sais pas.


MISE À JOUR:

Trouvé ce .

Il semble que SurfaceView puisse être remplacé par TextureView pour supprimer ces limitations. Mais puisque MapFragment est basé sur SurfaceView, je ne sais pas si cela peut être réalisé.

Une autre mise à jour Il semble que ce problème a été résolu sur les appareils 4.1+. Ils ont juste utilisé TextureView. mais sur les versions inférieures, nous devons encore utiliser des solutions de contournement.

53
Michał K

Bien sûr, la solution appropriée consistera pour Google à résoudre le problème (voir Android Maps V2 issue 4639: http://code.google.com/p/gmaps-api- issues/issues/detail? id = 4639 ).

Cependant, l'un de mes collègues a suggéré d'étendre simplement la carte au-delà de son conteneur. Si nous étendons le fragment de carte au-delà de la région visible de son conteneur, comme ceci:

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

nous pouvons réduire/éliminer le scintillement. Les appareils plus récents (par exemple Galaxy Nexus) ne montrent aucun scintillement après ce piratage, et les appareils plus anciens (par exemple LG Optimus V) montrent un scintillement réduit. Nous devons étendre les marges des deux côtés afin que les fenêtres d'informations soient centrées quand ils sont sélectionnés.


Mise à jour: ce problème a été résolu par Google le 28 août, mais je suppose qu'il doit encore être intégré dans une version.

14
Daniel Schuler

Vous pouvez utiliser cette bibliothèque

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

cela fonctionne bien pour moi.

6
Erick Martinez

Voici une idée folle, ne déplacez pas le MapView ;-)

Autrement dit, créez une largeur de plein écran MapView derrière votre application dans un FrameLayout. Ensuite, posez vos vues sur le dessus et percez un trou dans MapView dans la position dont vous avez besoin pour l'afficher. Lors du déplacement, vous devrez mettre à jour la position de la carte pour rester synchronisé afin que l'utilisateur continue de voir la même section de la carte, mais cela devrait être mieux mis à jour que de déplacer une vue de surface.

4
Ryan

Eh bien, citant Dianne Hackborn (un Android)

La vue de la surface est en fait DERRIÈRE votre fenêtre et un trou percé dans la fenêtre pour que vous puissiez le voir. Vous pouvez donc mettre des objets dessus dans votre fenêtre, mais rien dans votre fenêtre ne peut apparaître derrière. Message d'origine

Cette architecture fait partie des raisons pour lesquelles vous voyez ces scintillements.

Certains scintillements apparaissent parfois en raison de doubles tampons: le blog de codage Android explique plus en détail .

Vous pouvez également essayer de sous-classer le SurfaceView pour essayer de dessiner l'arrière-plan différemment. Je n'ai pas vu un exemple de cela qui ne change pas le z-index pour être celui du sommet. Découvrez ce SO post

Sinon, je recommande de n'obtenir le SurfaceHolder qu'après que votre vue est focalisée (essayez de faire une vue temporaire jusque-là) et de supprimer le SurfaceView sans être au point et à l'écran.

3
Ben Max Rubinstein

Prendre une capture d'écran d'une carte n'est pas possible, mais peut-être que quelqu'un s'en inspirera.

C'est possible avec interface snapshot de GoogleMap . Vous pouvez maintenant afficher un instantané de la carte tout en faisant défiler la page. Avec cela, j'ai résolu mon problème avec le ViewPager, vous pouvez peut-être changer le code pour l'adapter à votre SlidingMenu.

Voici mon code pour définir l'instantané (il est dans le même fragment que la carte, appelé 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;
    }
}

Où "mapFragment" est mon SupportedMapFragment et "iv" est un ImageView (faites-le match_parent).

Et ici, je contrôle le défilement:

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

Mon fragment avec carte (FragmentMap) est en position 1, j'ai donc besoin de contrôler le défilement de la position 0 à 1 et de la position 1 à 2 (la première clause if). "getRegisteredFragment ()" est une fonction de mon FragmentPagerAdapter personnalisé, dans laquelle j'ai un SparseArray (Fragment) appelé "registeredFragments".

Ainsi, chaque fois que vous faites défiler vers ou depuis votre carte, vous en voyez toujours un instantané. Cela fonctionne très bien pour moi.

2
Namenlos

Une autre solution alternative consiste à utiliser la nouvelle fonction GoogleMap.snapshot() et à utiliser une capture d'écran de la carte à la place; comme décrit ici .

1
joecks

Dans mon scénario, j'avais un fragment initial, qui sur un clic de bouton échangeait un deuxième fragment avec un MapFragment à l'intérieur. J'ai remarqué que ce scintillement ne se produisait que la première fois que vous échangiez le fragment - en éclatant la pile arrière, et en échangeant un autre fragment du même type ne provoquait pas de scintillement.

Donc, pensant que cela avait quelque chose à voir avec l'ajout d'une SurfaceView initiale à la fenêtre, j'ai essayé d'ajouter une instance vide d'une SurfaceView à mon fragment initialement chargé, derrière sa vue principale. Et bingo, plus de scintillement lors de l'animation dans le prochain fragment!

Je sais que ce n'est pas la solution la plus propre, mais cela fonctionne. Le Google MapFragment (ou SupportMapFragment dans mon cas) fonctionne toujours bien, car le SurfaceView inutilisé se trouve dans le fragment précédent.

Je dois ajouter que j'utilise l'API v2.

Mise à jour @VenomVendor

J'ajoute la SurfaceView vide au premier fragment à afficher, à l'index 0 - derrière la vue principale. Dans mon cas, le fragment Home est le fragment précédent à celui qui contient le fragment Google Maps, je ne sais pas si cela importe:

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

ajouter Android:hardwareAccelerated="true" dans votre fichier manifeste.

par exemple: <application Android:name=".Sample" Android:hardwareAccelerated="true" />

1
Nagalakshmi

J'ai eu le même problème avec une vue Web et SlidingMenu et j'ai résolu en modifiant le code source de SlidingMenu comme expliqué ici par le développeur principal.

Commentez l'accélération matérielle dans la méthode manageLayers de SlidingMenu. Cela semble être la seule solution car SurfaceViews ne fonctionne pas.

1
sourcerebels

Pour empêcher MapView (il s'agit en fait d'une SurfaceView) de créer des trous noirs, j'ajoute une vue vide avec une couleur d'arrière-plan transparente pour couvrir toute la MapView, de sorte qu'elle puisse empêcher MapView de générer un "trou" noir. Cela peut fonctionner pour votre situation.

1
Acheese

Changez votre FrameLayout en RelativeLayout si possible

0
Reza

J'ai eu le même problème d'écran noir lors du défilement de la vue de liste, j'utilisais un fragment de carte avec la vue de liste dans le même écran, j'ai résolu ce problème en utilisant la propriété magique en xml où je parle en vue de liste il suffit de mettre Android: scrollingCache = "false". mon problème est résolu essayez cette propriété pour arrêter le retard et le scintillement dans vos cartes.

0
user1414160

essayez d'ajouter ..

getHolder (). setFormat (PixelFormat.TRANSLUCENT);

au constructeur de votre SurfaceView. Le TextureView devrait également fonctionner, mais vous demandera de supprimer le support pour les appareils sub api 14.

0
Lee

J'ai eu le même problème et j'ai utilisé une solution de contournement basée sur la modification de la "Visibilité".

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

Cela a fonctionné pour moi ... Ajouter la carte: zOrderOnTop = "true" à votre fragment comme ceci ...

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

N'oubliez pas d'ajouter ceci à votre ScrollView parent

xmlns:map="http://schemas.Android.com/apk/res-auto"
0
karenms

Les gars, la meilleure solution que j'ai trouvée était d'instancier votre carte de support à travers cela. Fait des merveilles

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