Eu tenho um aplicativo com três guias.
Cada guia possui seu próprio arquivo .xml de layout. O main.xml possui seu próprio fragmento de mapa. É o que aparece quando o aplicativo é iniciado pela primeira vez.
Tudo funciona bem, exceto quando eu alterno entre as guias. Se eu tentar voltar à guia de fragmentos do mapa, recebo este erro. Mudar para e entre outras guias funciona perfeitamente.
Oque pode estar errado aqui?
Esta é minha classe principal e meu main.xml, bem como uma classe relevante que eu uso (você também encontrará o log de erros na parte inferior)
classe principal
package com.nfc.demo;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Toast;
public class NFCDemoActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar
.newTab()
.setText("Map")
.setTabListener(
new TabListener<MapFragment>(this, "map",
MapFragment.class)));
bar.addTab(bar
.newTab()
.setText("Settings")
.setTabListener(
new TabListener<SettingsFragment>(this, "settings",
SettingsFragment.class)));
bar.addTab(bar
.newTab()
.setText("About")
.setTabListener(
new TabListener<AboutFragment>(this, "about",
AboutFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
// setContentView(R.layout.main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public static class TabListener<T extends Fragment> implements
ActionBar.TabListener {
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(Activity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(Activity activity, String tag, Class<T> clz,
Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
// Check to see if we already have a fragment for this tab,
// probably from a previously saved state. If so, deactivate
// it, because our initial state is that a tab isn't shown.
mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
FragmentTransaction ft = mActivity.getFragmentManager()
.beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName(),
mArgs);
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT)
.show();
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
classe relevante (MapFragment.java)
package com.nfc.demo;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.main, container, false);
}
public void onDestroy() {
super.onDestroy();
}
}
erro
android.view.InflateException: Binary XML file line #7:
Error inflating class fragment
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at com.nfc.demo.MapFragment.onCreateView(MapFragment.java:15)
at android.app.Fragment.performCreateView(Fragment.java:1695)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:885)
at android.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1255)
at android.app.BackStackRecord.run(BackStackRecord.java:672)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:441)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException:
Binary XML file line #7: Duplicate id 0x7f040005, tag null, or
parent id 0xffffffff with another fragment for
com.google.android.gms.maps.MapFragment
at android.app.Activity.onCreateView(Activity.java:4722)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
... 19 more
Respostas:
A resposta que Matt sugere funciona, mas faz com que o mapa seja recriado e redesenhado, o que nem sempre é desejável. Após várias tentativas e erros, encontrei uma solução que funciona para mim:
Para uma boa medida, aqui está "map.xml" (R.layout.map) com R.id.mapFragment (android: id = "@ + id / mapFragment"):
Espero que isso ajude, mas não posso garantir que não tenha efeitos adversos.
Editar: houve alguns efeitos adversos, como ao sair do aplicativo e iniciá-lo novamente. Como o aplicativo não é necessariamente completamente desligado (mas apenas adormecido em segundo plano), o código anterior que eu enviei falharia ao reiniciar o aplicativo. Atualizei o código para algo que funcione para mim, entrando e saindo do mapa e saindo e reiniciando o aplicativo. Não estou muito feliz com o bit try-catch, mas parece funcionar bem o suficiente.
Ao olhar para o rastreamento da pilha, ocorreu-me que eu poderia apenas verificar se o fragmento do mapa está no FragmentManager, sem necessidade do bloco try-catch, o código atualizado.Mais edições: afinal, você precisa dessa tentativa de captura. A verificação do fragmento do mapa acabou por não funcionar tão bem, afinal. Blergh.
fonte
SupportMapFragment
inonDestroyView
.O problema é que o que você está tentando fazer não deve ser feito. Você não deve inflar fragmentos dentro de outros fragmentos. Da documentação do Android :
Embora você possa realizar a tarefa com os hacks apresentados aqui, sugiro que você não faça isso. É impossível ter certeza de que esses hacks manipularão o que cada novo sistema operacional Android faz quando você tenta aumentar o layout de um fragmento contendo outro fragmento.
A única maneira suportada pelo Android de adicionar um fragmento a outro fragmento é através de uma transação do gerenciador de fragmentos filho.
Simplesmente altere seu layout XML em um contêiner vazio (adicione um ID, se necessário):
Então, no
onViewCreated(View view, @Nullable Bundle savedInstanceState)
método Fragment :fonte
4.3
ao usar umSupportMapFragment
definido em XML. Criar dinamicamente o fragmento e injetá-lo em uma exibição de contêiner resolveu o problema. Veja esta resposta SO .SupportMapFragment.newInstance();
developers.google.com/maps/documentation/android-api/mapEu tive o mesmo problema e consegui resolvê-lo removendo manualmente
MapFragment
oonDestroy()
método daFragment
classe Aqui está o código que funciona e faz referência aoMapFragment
por ID no XML:Se você não remover o
MapFragment
manual, ele ficará suspenso para não custar muitos recursos para recriar / mostrar a visualização do mapa novamente. Parece que manter o subjacenteMapView
é ótimo para alternar entre as guias, mas quando usado em fragmentos, esse comportamento faz com que uma duplicataMapView
seja criada sobre cada novoMapFragment
com o mesmo ID. A solução é remover manualmente oMapFragment
e recriar o mapa subjacente sempre que o fragmento for inflado.Também observei isso em outra resposta [ 1 ].
fonte
Esta é a minha resposta:
1, crie um xml de layout como a seguir:
2, na classe Fragment, adicione um mapa do Google programaticamente.
fonte
mMapFragment.getMap();
retornanull
. Alguma idéia do porquê?Minha solução:
fonte
Declarar o objeto SupportMapFragment globalmente
No método onCreateView (), coloque o código abaixo
Em onDestroyView (), coloque o código abaixo
No seu arquivo xml, coloque o código abaixo
O código acima resolveu meu problema e está funcionando bem
fonte
Eu recomendaria, em
replace()
vez deattach()
/detach()
no seu tratamento de guias.Ou mude para
ViewPager
. Aqui está um exemplo de projeto mostrando umViewPager
, com guias, hospedando 10 mapas.fonte
Outra solução:
É isso, se não for nulo, você não precisará reinicializá-lo. A remoção do pai é uma etapa desnecessária.
fonte
Hoje perdi horas para encontrar o motivo. Felizmente, esse problema não é causado pela implementação do MapFragment. Infelizmente, isso não funciona porque os fragmentos aninhados são suportados apenas pela biblioteca de suporte da rev 11.
Minha implementação possui uma atividade com a barra de ação (no modo com guias) com duas guias (sem visor), uma com o mapa e a outra com uma lista de entradas. É claro que tenho sido ingênuo em usar o MapFragment dentro dos meus fragmentos de tabulação, e pronto, o aplicativo falhava toda vez que eu voltava para a tab-map.
(O mesmo problema que eu também teria caso meu fragmento de tabulação inflasse qualquer layout que contenha qualquer outro fragmento).
Uma opção é usar o MapView (em vez de MapFragment), mas com alguma sobrecarga (consulte o MapView Docs como substituto do layout.xml, outra opção é usar a biblioteca de suporte da versão 11, mas adotar a abordagem programática como os fragmentos aninhados não são suportados pelo layout ou apenas trabalhando de forma programática destruindo explicitamente o fragmento (como na resposta de Matt / Vidar), btw: o mesmo efeito é alcançado usando o MapView (opção 1).
Mas, na verdade, eu não queria perder o mapa toda vez que separava, ou seja, queria mantê-lo na memória e limpar apenas após o fechamento da atividade, por isso decidi simplesmente ocultar / mostrar o mapa enquanto guia, consulte FragmentTransaction / hide
fonte
Para aqueles que ainda estão enfrentando esse problema, a melhor maneira de garantir que você não receba esse erro com um mapa em uma guia é fazer com que o fragmento se estenda em
SupportMapFragment
vez de aninharSupportMapFragment
dentro do fragmento usado para a guia.Acabei de trabalhar com um
ViewPager
com umFragmentPagerAdapter
, com o SupportMapFragment na terceira guia.Aqui está a estrutura geral, observe que não há necessidade de substituir o
onCreateView()
método e não há necessidade de inflar nenhum xml de layout:Resultado:
Aqui está o código de classe completo que eu costumava testar, que inclui o fragmento de espaço reservado usado nas duas primeiras guias e o fragmento de mapa usado na terceira guia:
fonte
Eu respeito todas as respostas, mas eu encontrei esta solução de uma linha: Se n é o número de guias, então:
Exemplo: No caso mencionado:
Visualizar pager implementa uma fila para que você não precise remover esse fragmento. onCreateView é chamado apenas uma vez.
fonte
Você está retornando ou inflando o layout duas vezes, basta verificar se você inflou apenas uma vez.
fonte
Fragmentos aninhados não são suportados no momento. Experimente o Support Support, revisão 11 .
fonte
Você está tentando fazer referência à sua
MapFragment
classe personalizada no arquivo de layout?fonte
Se você usar apenas a resposta Vidar Wahlberg, receberá um erro ao abrir outra atividade (por exemplo) e voltar ao mapa. Ou, no meu caso, abra outra atividade e, a partir da nova atividade, abra o mapa novamente (sem usar o botão voltar). Mas quando você combina a solução Vidar Wahlberg e a solução Matt, não há exceções.
disposição
Fragmento
fonte
Eu tinha isso no viewPager e a falha ocorreu porque qualquer fragmento tinha que ter sua própria tag, não são permitidas tags duplicadas ou ids para o mesmo fragmento.
fonte
Eu acho que houve alguns erros na versão anterior da App-Compat para fragmento filho. Tentei @Vidar Wahlberg e @ Matt e eles não funcionaram para mim. Depois de atualizar a biblioteca appcompat, meu código funciona perfeitamente sem nenhum esforço extra.
fonte
O que deve ser observado aqui é que seu aplicativo falhará muito em um dos dois casos: -
Aqui está um exemplo de trecho de código para o uso correto do MapView
XML
O resultado fica assim: -
Espero que ajude alguém.
fonte
Nesta solução, você não precisa pegar variáveis estáticas;
fonte
Estou um pouco atrasado para a festa, mas nenhuma dessas respostas me ajudou no meu caso. Eu estava usando o mapa do Google como SupportMapFragment e PlaceAutocompleteFragment, ambos no meu fragmento. Como todas as respostas apontaram para o fato de que o problema é o SupportMapFragment ser o mapa a ser recriado e redesenhado.
Então, aqui está a solução de trabalho para quem está enfrentando esse problema por causa do SupportMapFragment e SupportMapFragment
E no onDestroyView desmarque as opções SupportMapFragment e SupportMapFragment
fonte
Tente definir um ID (android: id = "@ + id / maps_dialog") para o layout pai do mapView. Funciona para mim.
fonte
Qualquer pessoa que esteja aqui agora que está recebendo esse tipo de erro ao abrir um
Dialog
ou outroFragment
com o Google PlacesAutocompleteSupportFragment
, tente esta lista (não sei o quão seguro isso é, mas funciona para mim):autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
antes de descartar / destruir seu fragmento.
fonte
Por que você não insere um mapa usando o objeto MapView em vez de MapFragment? Não tenho certeza se há alguma limitação no MapView, embora eu tenha achado útil.
fonte