ViewPager com Google Maps API v2: misteriosa visão negra

95

Eu integrei o novo fragmento Google Maps API v2 em um pager de exibição. Ao rolar a partir do fragmento do mapa, uma visão preta se sobrepõe aos fragmentos adjacentes. Alguém resolveu?

Editar: captura de tela

insira a descrição da imagem aqui

public static class PagerAdapter extends FragmentPagerAdapter{

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {

        Fragment pageFragment;

        switch (position) {
        case 0:
            pageFragment = new TabAFragment();
            break;

        case 1:
            pageFragment = new TabBFragment();
            break;

        case 2:
            pageFragment = SupportMapFragment.newInstance();
            break;

        default:
            pageFragment = null;
            break;
        }

        return pageFragment;
    }
}
Pepe
fonte
Isso acontece em mais de um dispositivo? Talvez algumas linhas de código do seu ViewPager e a implementação do mapa possam ajudar a entender melhor o seu problema.
Adinia
Sim, acontece em todos os dispositivos que experimentei: samsung, htc, android 4.0, 2.3, etc. Eu integrei o mapa no meu aplicativo, e em um pequeno aplicativo limpo com apenas o pager e o mapa, com o mesmo resultado.
Pepe
@Pepe, você poderia dizer, como você obtém o GoogleMapobjeto do ViewPager? Preso nisso por semanas. Se você não entendeu bem a pergunta, então olhe minha pergunta, você encontrará meu post. Por favor, responda.
azizbekian
Quero acrescentar que, ao fazer um, screen recordo problema parece não acontecer.
theblang de

Respostas:

115

Eu era capaz de parar a superfície preta que está sendo deixado para trás após a transição, colocando uma outra visão com um fundo transparente em cima do ViewPagerinterior de um FrameLayout:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v4.view.ViewPager
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </android.support.v4.view.ViewPager>

    <!-- hack to fix ugly black artefact with maps v2 -->
    <FrameLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:background="@android:color/transparent" />

</FrameLayout>
Jeff Gilfelt
fonte
1
Ótimo! Não é perfeito, porque às vezes, enquanto o pager se move, aparece parte da tela preta. Mas agora não permanece mais na tela. Obrigado! (você tem alguma explicação para este hack?)
Pepe
7
Na verdade não. Acabei de notar onde os controles de zoom e localização estavam na parte superior da superfície do mapa e a trilha preta não apareceu, então coloquei uma visualização no topo para cobrir todo o mapa e funcionou. Concordo que não é perfeito - o fundo preto parece fazer parte da janela GLSurfaceView code.google.com/p/gmaps-api-issues/issues/detail?id=4639
Jeff Gilfelt
5
Parece que corrigiu meu problema com o ViewPager. Obrigado, mas parece que tenho um problema semelhante com um SlideMenu (menu à esquerda como G + / Yahoo), fazendo a mesma coisa. Alguma ideia sobre como consertar uma implementação do SlideMenu?
Chrispix
@Chrispix não anima o container, usa a animação apenas no menu lateral, ele consertou para mim.
meh
1
Eu fiz isso e a solução programática abaixo, eles funcionam enquanto o aplicativo está em primeiro plano, mas quando o aplicativo está fechado e ainda em execução e você volta a ele, o mapa permanece no topo ainda.
L7ColWinters
43

Tive o mesmo problema com SlidingMenu e ViewPager. Percebi que os botões de controle no fragmento do mapa não são pretos no artefato deixado para trás. Resolvi o problema substituindo o onCreateView()método deMapFragment (SupportMapFragment)

@Override
public View onCreateView(LayoutInflater inflater, 
                         ViewGroup view, 
                         Bundle savedInstance) {

    View layout = super.onCreateView(inflater, view, savedInstance);
    FrameLayout frameLayout = new FrameLayout(getActivity());
    frameLayout.setBackgroundColor(
        getResources().getColor(android.R.color.transparent));
    ((ViewGroup) layout).addView(frameLayout,
        new ViewGroup.LayoutParams(
            LayoutParams.FILL_PARENT, 
            LayoutParams.FILL_PARENT
        )
    );
    return layout;
}
Lubos Horacek
fonte
Eu também o uso para o SlidingMenu. Mas eu tenho o problema de cintilação (com a animação deslizante) e é inaceitável, parece horrível. Você também tem? Testei no HTC One S. O que é interessante, só tenho esse problema com a animação de abertura e não ao fechar SlidingMenu
Michał K
Nenhuma das correções funcionou para mim. Tenho praticamente o mesmo problema que Michal K. Usar SlidingMenu e abrir ou fechar lentamente em uma velocidade contínua e não há problema. Abra ou pressione para trás para fechar a janela e parece que o mapa está um pouco atrasado, deixando um espaço em branco até que o slide termine, onde o mapa retorna à sua posição correta.
qubz
@qubz: Sobre o 'menu deslizante', experimente animar o menu, não o mapa. Resolveu o problema para mim, se o mapa não estiver se movendo.
meh
@meh: O mapa pode estar se movendo enquanto estou abrindo / fechando o SlidingMenu. Ao mover, quero dizer CameraUpdate. Vou ver se consigo pausar a animação, embora isso vá atrapalhar a UX.
qubz
@Lubos Horacek: Você poderia postar um código de exemplo. Estou recebendo um erro de exceção de ponteiro nulo ..
avinash
7

O Google lançou uma correção para isso, mas apenas para 4.1+ (você não precisa baixar uma nova versão do PlayServices, eles usaram um sinalizador do lado do servidor)

Aqui está o que estou usando para contornar o problema - garante que você não desperdice nenhum ciclo de CPU em dispositivos> = 4,1

public class FixMapFragment extends SupportMapFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, 
                             ViewGroup container, 
                             Bundle savedInstanceState) {

        View view = super.onCreateView(inflater, container, savedInstanceState);

        // Fix for black background on devices < 4.1
        if (android.os.Build.VERSION.SDK_INT < 
            android.os.Build.VERSION_CODES.JELLY_BEAN) {
            setMapTransparent((ViewGroup) view);
        }
        return view;
    }

    private void setMapTransparent(ViewGroup group) {
        int childCount = group.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = group.getChildAt(i);
            if (child instanceof ViewGroup) {
                setMapTransparent((ViewGroup) child);
            } else if (child instanceof SurfaceView) {
                child.setBackgroundColor(0x00000000);
            }
        }
    }
}
Tspoon
fonte
Eu acho que você faz referência a classe da seguinte forma: FixMapFragment mapFragment = (FixMapFragment) fragmentManager.findFragmentById(R.id.map);e no arquivo de layout: <fragment android:id="@+id/map" android:name="com.example.fragments.FixMapFragment" .... Porém, o mapa ainda pisca, tanto quanto posso dizer (Android 4.0.4).
JJD
@JJD eu tenho um mapview que pisca, há uma maneira de resolver isso?
Rat-a-tat-a-tat Ratatouille
@ Rat-a-tat-a-tatRatatouille Não há solução alternativa confiável para evitar a oscilação da visualização do mapa, tanto quanto eu sei.
JJD de
Não sei se o que fiz por agora está correto ou não, mas funciona. Eu defini a visibilidade do mapviews como invisível e, depois de alguns segundos, defini a visibilidade do mapviews de volta para visível. reduz a cintilação.
Rat-a-tat-a-tat Ratatouille
6

Minha solução alternativa: faça uso da nova interface de instantâneo do GoogleMap e exiba um instantâneo do mapa enquanto rola a página.

Aqui está meu código para definir o instantâneo (está no mesmo fragmento do mapa, chamado 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;
    }
}

Onde "mapFragment" é meu SupportedMapFragment e "iv" é um ImageView (torne-o match_parent).

E aqui estou controlando o pergaminho:

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

Meu fragmento com mapa (FragmentMap) está na posição 1, então preciso controlar a rolagem da posição 0 a 1 e da posição 1 a 2 (a primeira cláusula if). "getRegisteredFragment ()" é uma função em meu FragmentPagerAdapter personalizado, no qual tenho um SparseArray (Fragment) chamado "RegisterFragments".

Portanto, sempre que você rola para ou a partir do mapa, sempre vê um instantâneo dele. Isso funciona muito bem para mim.

Namenlos
fonte
As outras respostas não funcionaram para mim - esta é a única que funcionou. Obrigado!
alice_silver_man
4

Eu tive o mesmo problema de tela preta ao rolar a exibição de lista, estava usando fragmento de mapa com exibição de lista na mesma tela. Resolvi esse problema com o uso da propriedade mágica em xml, onde estou falando de exibição de lista, apenas temos que colocar o android: scrollingCache = "false". meu problema foi corrigido, tente esta propriedade para parar de atrasar e piscar em seus mapas.


fonte
2

Você não deu muitos detalhes, então há muito que posso sugerir. Eu tive um problema semelhante que a tela oscilou entre as visualizações no pager. Descobriu-se que era o mapView inflando ao alternar entre as páginas pela primeira vez.

Para ser claro, eu tinha 3 páginas em meu pager e meu mapa era a última página.

Para resolver isso, descobri que definir o número de páginas fora da tela como 2 (funcionou no caso acima) para carregar todas as visualizações de uma vez quando meu fragmento começou.

Consulte http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit (int)

Graham Smith
fonte
Obrigado, mas não funcionou. O problema é que a parte da tela coberta pelo mapa, ao mudar para outro fragmento, permanece preta, cobrindo a visão desse outro fragmento.
Pepe
hmmm interessante. Vou pensar sobre isso.
Graham Smith
Não consigo encontrar setOffscreenPageLimit na página a que você está vinculado
mplungjan
0

Existe mais uma solução para este problema. Estou mostrando MapFragment dentro de outro fragmento. O MapFragment é adicionado dinamicamente a um FrameLayout.

A solução é usar frameLayout.setVisibility (View.Visible) e frameLayout.setVisibility (View.Gone) em eventos de abertura e fechamento do menu deslizante. É necessário adicionar uma visão extra. E a área preta desapareceu completamente.

getSlidingMenu().setOnOpenListener(
        new OnOpenListener() {
            @Override
            public void onOpen() {
                frameLayout.setVisibility(View.GONE);
            }
        });

getSlidingMenu().setOnClosedListener(
        new OnClosedListener() {

            @Override
            public void onClosed() {
                frameLayout.setVisibility(View.VISIBLE);
            }
        });
JehandadK
fonte
0

Nenhum dos métodos acima funcionou para mim, nem mesmo outros que encontrei em outro lugar. Finalmente, escolhi uma solução parcial. Eu ouço as mudanças de página por meio do onPageChangeListener do ViewPager, e oculto o mapa quando sua página começa a rolar, e também o mostro novamente quando ele para de rolar. Eu também adicionei uma Visualização em branco para ser exibida durante a rolagem.

azyoot
fonte
Ele pode ser estendido para capturar a imagem atual do View em um bitmap e, em seguida, exibi-lo em um ImageView enquanto o pager está rolando. Dessa forma, os usuários não perceberiam o hack por trás disso. No entanto, achei esse cálculo um pouco caro, pois tornaria mais lenta a resposta da IU ao deslizar.
azyoot
-2

Basta copiar meu fragmento de mapa personalizado para o seu projeto. E se você tiver linhas pretas com ScrollView na parte superior e inferior definidas para o seu android ScrollView: fadingEdge = "none"

public class CustomMapFragment extends SupportMapFragment {
private OnActivityCreatedListener onActivityCreatedListener;

public CustomMapFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup view, Bundle savedInstance) {
    View layout = super.onCreateView(inflater, view, savedInstance);

    FrameLayout frameLayout = new FrameLayout(getActivity());
    frameLayout.setBackgroundColor(getResources().getColor(android.R.color.transparent));
    ((ViewGroup) layout).addView(frameLayout,
            new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    return layout;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (getOnActivityCreatedListener() != null)
        getOnActivityCreatedListener().onCreated();
}

public OnActivityCreatedListener getOnActivityCreatedListener() {
    return onActivityCreatedListener;
}

public void setOnActivityCreatedListener(
        OnActivityCreatedListener onActivityCreatedListener) {
    this.onActivityCreatedListener = onActivityCreatedListener;
}

public interface OnActivityCreatedListener {
    void onCreated();
}

}

user2728452
fonte