Дублікат ідентифікатора, тегу null або батьківського ідентифікатора з іншим фрагментом для com.google.android.gms.maps.MapFragment


333

У мене є додаток з трьома вкладками.

Кожна вкладка має свій макет .xml-файл. Main.xml має власний фрагмент карти. Це те, що з’являється при першому запуску програми.

Все працює добре, за винятком випадків, коли я змінюю вкладки. Якщо я спробую переключитися на вкладку фрагмент карти, я отримаю цю помилку. Перехід на та між іншими вкладками працює чудово.

Що тут може бути не так?

Це мій основний клас і мій main.xml, а також відповідний клас, який я використовую (ви також знайдете журнал помилок внизу)

основний клас

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>

відповідний клас (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();
    }
}

помилка

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

спробуйте це - поверніть super.onCreateView (надувальник, контейнер, збереженийInstanceState); замість super.onCreateView (надувальник, контейнер, збереженийInstanceState); повернути inflater.inflate (R.layout.main, container, false);
Нік

ви не повинні додавати вам фрагмент, коли збереженийInstanceState не є нульовим.
muyiou

Погляньте на цей коментар. Це може допомогти вам: stackoverflow.com/questions/15562416/…
Олександр

2
Будь ласка, змініть прийняту відповідь! Ви вибрали дуже погану відповідь, яка створює витоки пам'яті! Правильна відповідь така: stackoverflow.com/a/19815266/902276
Daniele Segato

Відповіді:


400

Відповідь, що Метт пропонує, працює, але це спричиняє відтворення та перероблення карти, що не завжди бажано. Після безлічі спроб та помилок я знайшов рішення, яке працює для мене:

private static View view;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (view != null) {
        ViewGroup parent = (ViewGroup) view.getParent();
        if (parent != null)
            parent.removeView(view);
    }
    try {
        view = inflater.inflate(R.layout.map, container, false);
    } catch (InflateException e) {
        /* map is already there, just return view as it is */
    }
    return view;
}

На користь, ось "map.xml" (R.layout.map) з R.id.mapFragment (android: id = "@ + id / mapFragment"):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/mapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.SupportMapFragment" />
</LinearLayout>

Я сподіваюся, що це допомагає, але я не можу гарантувати, що це не матиме негативних наслідків.

Редагувати: Були деякі негативні наслідки, наприклад, коли виходили з програми та запускалися знову. Оскільки додаток не обов'язково повністю закрито (а просто увімкнено у фоновому режимі), попередній код, який я подав, не вдасться після перезавантаження програми. Я оновив код на те, що працює для мене, і входячи в карту, і виходячи з нього, і перезавантажую програму, я не надто задоволений випробувальним бітом, але, здається, він працює досить добре. При перегляді сліду стека мені спало на думку, що я можу просто перевірити, чи фрагмент карти знаходиться у FragmentManager, чи не потрібен блок пробного лову, оновлений код.

Більше змін: виявляється, вам потрібен цей пробний зрештою. Щойно перевірка фрагмента карти виявилася, що зрештою він не працює так добре. Blergh.


25
Це неправильна відповідь -1! Ви протікаєте за допомогою статичного модифікатора для перегляду. Першопричиною цієї проблеми є, мабуть, ще одна просочена діяльність, яку неможливо зібрати сміття, тому що ви зберігаєте чітке посилання, яке вказує на це. У випадку, якщо ви кинули InflateException, ви використовуєте представлення, яке має контекст знищеної діяльності! Краще знайдіть інші витоки пам'яті у вашому додатку, це вирішить усі проблеми.
tomrozb

1
Я думаю, ми можемо використовувати WeakReference, щоб уникнути витоку пам'яті?
Десмонд Луа

2
Це також працює для мене +1. Ви не зобов’язані користуватися статичною посиланням на перегляд
Karol Żygłowicz

4
Не робіть цього !!! Це спричиняє витік пам'яті. Єдиною причиною цього є те, що ви надуваєте фрагмент з XML всередині іншого фрагмента. Ви НЕ повинні цього робити! Вам слід використовувати ChildFragmentManager та додати фрагмент у onViewCreate ()!
Даніеле Сегато

1
Так, це дійсно просочить діяльність. Знайшов робоче рішення в stackoverflow.com/a/27592123/683763 . Ідея полягає в ручному видаленні методу SupportMapFragmentin onDestroyView.
ULazdins

277

Проблема полягає в тому, що те, що ви намагаєтеся зробити, не повинно робити. Ви не повинні надувати фрагменти всередині інших фрагментів. З документації на Android :

Примітка. Ви не можете надути макет у фрагмент, коли цей макет включає <fragment>. Вкладені фрагменти підтримуються лише тоді, коли динамічно додаються до фрагменту.

Хоча ви, можливо, зможете виконати завдання за допомогою представлених тут хаків, настійно рекомендую цього не робити. Неможливо бути впевненим, що ці хаки впораються з тим, що робить кожна нова ОС Android, коли ви намагаєтеся надути макет для фрагмента, що містить інший фрагмент.

Єдиний підтримуваний Android спосіб додати фрагмент до іншого фрагмента - це транзакція від дочірнього менеджера фрагментів.

Просто змініть ваш макет XML у порожній контейнер (додайте ідентифікатор, якщо потрібно):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapFragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
</LinearLayout>

Потім у onViewCreated(View view, @Nullable Bundle savedInstanceState)методі Fragment :

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    FragmentManager fm = getChildFragmentManager();
    SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentByTag("mapFragment");
    if (mapFragment == null) {
        mapFragment = new SupportMapFragment();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.mapFragmentContainer, mapFragment, "mapFragment");
        ft.commit();
        fm.executePendingTransactions();
    }
    mapFragment.getMapAsync(callback);
}

4
Див. Stackoverflow.com/questions/13733299/… для прикладів програмного створення фрагмента карти та ініціалізації карти.
Крістофер Джонсон

1
Також дивіться stackoverflow.com/questions/19239175/… для вирішення проблеми очевидних помилок у підтримці дочірнього фрагмента.
Крістофер Джонсон

Я зіткнувся з цією проблемою під 4.3час використання SupportMapFragmentвизначеного в XML. Динамічне створення фрагмента та введення його у подання контейнера вирішили проблему. Дивіться цю відповідь ТАК .
theblang

Дуже корисна відповідь. Як ми можемо зробити зворотний виклик головної функції onCreate ()?
Утопія

2
За інформацією doc, використовуйте SupportMapFragment.newInstance(); developers.google.com/maps/documentation/android-api/map
Jemshit Iskenderov

178

У мене була така ж проблема і була в змозі вирішити її вручну видаленням MapFragmentв onDestroy()способі Fragmentкласу. Ось код, який працює і посилається на MapFragmentідентифікатор у XML:

@Override
public void onDestroyView() {
    super.onDestroyView();
    MapFragment f = (MapFragment) getFragmentManager()
                                         .findFragmentById(R.id.map);
    if (f != null) 
        getFragmentManager().beginTransaction().remove(f).commit();
}

Якщо ви не видалите MapFragmentвручну, він буде зависати, щоб не витратити багато ресурсів на відтворення / показ карти знову. Здається, що збереження основного MapViewвідмінно підходить для перемикання між вкладками вперед і назад, але при використанні у фрагментах така поведінка призводить до створення дубліката MapViewкожного нового MapFragmentз тим самим ідентифікатором. Рішення полягає в тому, щоб вручну видалити MapFragmentта таким чином відтворити основну карту кожного разу, коли фрагмент надувається.

Я також зазначив це в іншій відповіді [ 1 ].


Це призводить до аварійного завершення роботи програми, натиснувши кнопку будинку на пристрої, з опціями "Не тримати діяльність" у параметрах розробника.
липень

24
це працює, але якщо я обертаю екран, додаток виходить з цього винятку: Викликано: java.lang.IllegalStateException: Не можу виконати цю дію після onSaveInstanceState
jramoyo

1
Для тих, хто може зацікавитись, ви можете усунути виняток, викликаний обертанням екрана, зберігаючи внутрішню орієнтацію в конструкторі фрагмента і викликаючи вищевказану транзакцію, лише якщо орієнтація НЕ була змінена. Якщо це було змінено, не потрібно боротися з повторюваним ідентифікатором, оскільки його немає, коли подання створюється заново після обертання. Я можу відредагувати відповідь та надати фрагмент коду, якщо Метт не проти.
Стен

1
Хм для мене фрагмент карти не видаляється. Я повинен робити щось не так, але якщо я викликаю getActivity (). GetSupportFragmentManager (). GetFragments (), фрагмент залишається після виклику Remove (f) .commit. Хтось має ідею, чому? (Я замінив getFragManager на getSupportFragManager)
Даніель Вілсон

10
commitAllowingStateLoss дозволить уникнути виключення IllegalStateException.
Гектор Юдес Сапена

22

Це моя відповідь:

1, Створіть макет xml таким чином:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>

2, у класі Fragment, додайте карту Google програмно.

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * A simple {@link android.support.v4.app.Fragment} subclass. Activities that
 * contain this fragment must implement the
 * {@link MapFragment.OnFragmentInteractionListener} interface to handle
 * interaction events. Use the {@link MapFragment#newInstance} factory method to
 * create an instance of this fragment.
 * 
 */
public class MapFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    private GoogleMap mMap;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_map, container, false);
        SupportMapFragment mMapFragment = SupportMapFragment.newInstance();
        mMap = mMapFragment.getMap();
        FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
        transaction.add(R.id.map_container, mMapFragment).commit();
        return view;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Log.d("Attach", "on attach");
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }
} 

2
mMapFragment.getMap();повертає null. Будь-яка ідея чому?
Анас Азеем

@AnasAzeem, створивши фрагмент, програмно вирішує проблему точно, вам не доведеться отримувати mMap для рішення, можливо, у вашому випадку.
yesildal

@AnasAzeem, ви повинні використовувати getMapAsync, який поверне екземпляр карти правильно після ініціалізації (у фоновому режимі). Це "правильний" спосіб роботи з картами Google і не має нічого спільного з фрагментами
Ганеш Крішнан,

10
  1. Як згадував @Justin Breitfeller, рішення @Vidar Wahlberg - це хакер, який може не працювати в майбутній версії Android.
  2. @Vidar Wahlberg вважає злом, оскільки інше рішення "може призвести до того, що карта буде відтворена та перероблена, що не завжди бажано". Перемальовування карти можна було запобігти, зберігаючи старий фрагмент карти, а не створюючи кожен раз новий екземпляр.
  3. Рішення @Matt не працює для мене (IllegalStateException)
  4. Як цитує @Justin Breitfeller, "Ви не можете надути макет у фрагмент, коли цей макет включає. Вкладені фрагменти підтримуються лише тоді, коли динамічно додаються до фрагменту".

Моє рішення:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,                              Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_map_list, container, false);

    // init
    //mapFragment = (SupportMapFragment)getChildFragmentManager().findFragmentById(R.id.map);
    // don't recreate fragment everytime ensure last map location/state are maintain
    if (mapFragment == null) {
        mapFragment = SupportMapFragment.newInstance();
        mapFragment.getMapAsync(this);
    }
    FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
    // R.id.map is a layout
    transaction.replace(R.id.map, mapFragment).commit();

    return view;
}

2
Дякую @Desmond Ваше рішення працювало бездоганно для мене. Єдине, що вам потрібно пам’ятати - це НЕ створювати карту в макеті. Створення карти в цьому рішенні вбудовано в код, тому змініть <фрагмент android: name = "com.google.android.gms.maps.SupportMapFragment"> наприклад, <LinearLayout id = "@ + id / map />
kosiara - Bartosz Косаржицький

7

Декларуйте об’єкт SupportMapFragment у всьому світі

    private SupportMapFragment mapFragment;

У методі onCreateView () поставити нижче код

mapFragment = (SupportMapFragment) getChildFragmentManager()
            .findFragmentById(R.id.map);
 mapFragment.getMapAsync(this);

В onDestroyView () поставити нижче код

@Override
public void onDestroyView() {
   super.onDestroyView();

    if (mapFragment != null)
        getFragmentManager().beginTransaction().remove(mapFragment).commit();
}

У свій XML-файл помістіть код нижче

 <fragment
    android:id="@+id/map"
    android:name="com.abc.Driver.fragment.FragmentHome"
    class="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

Вищевикладений код вирішив мою проблему, і він працює чудово


3

Я б рекомендував, replace()а не attach()/ detach()у вашій обробці вкладок.

Або перейдіть на ViewPager. Ось зразок проекту, що показує ViewPager, з вкладками, розміщення 10 карт.


Але замінимо помилку видачі outofmemry, ніж те, що слід робити, якщо ми використовуємо вікно від'єднання, ніж виникає проблема з картою.
розробник Vishal для Android

3

Ще одне рішення:

if (view == null) {
    view = inflater.inflate(R.layout.nearbyplaces, container, false);
}

Ось це, якщо не нуль, вам не потрібно повторно ініціалізувати, видалення з батьківського - це непотрібний крок.


Це найкраще рішення для мого випадку. Я отримав цю помилку в результаті використання двох фрагментів для моєї навігаційної графіки, і це вирішило мою проблему.
Кеннеді Камбо

Це найкраще рішення для мого випадку. Я отримав цю помилку в результаті використання двох фрагментів для моєї навігаційної графіки, і це вирішило мою проблему.
Кеннеді Камбо

2

Сьогодні я втратив години, щоб знайти причину, на щастя, ця проблема не через реалізацію MapFragment, на жаль, це не працює, оскільки вкладені фрагменти підтримуються лише через бібліотеку підтримки з версії 11.

Моя реалізація має активність з панеллю дій (у режимі з вкладками) з двома вкладками (немає панелі перегляду), на одній - карта, а на іншій - список записів. Звичайно, я був досить наївний, щоб використовувати MapFragment всередині моїх фрагментів вкладки, і програма voila програвала щоразу, коли я переходила на вкладку map.

(Та сама проблема, яка була б у мене, якщо мій фрагмент вкладки надув би будь-який макет, що містить будь-який інший фрагмент)

Одним із варіантів є використання MapView (замість MapFragment), хоча з деякими накладними витратами (див. Документи MapView як заміна заміни у layout.xml, інший варіант - використовувати бібліотеку підтримки з версії 11, але потім застосувати програмний підхід оскільки вкладені фрагменти не підтримуються за допомогою макета. Або просто програмують навколо програми, чітко знищуючи фрагмент (як у відповіді від Matt / Vidar), btw: такий же ефект досягається за допомогою MapView (варіант 1).

Але насправді я не хотів втрачати карту кожен раз, коли я вкладав вкладку, тобто хотів зберігати її в пам’яті та очищенні лише після активного закриття, тому вирішив просто сховати / показати карту під час вкладки, дивіться FragmentTransaction / hid


2

Для тих, хто все ще стикається з цією проблемою, найкращий спосіб переконатися, що ви не отримаєте цю помилку з картою на вкладці, - це зробити фрагмент розширенням, SupportMapFragmentа не вкладати SupportMapFragmentвсередину фрагмент, який використовується для вкладки.

Я щойно почав цю роботу за ViewPagerдопомогою a FragmentPagerAdapter, з підтримкоюMapFragment на третій вкладці.

Ось загальна структура, зауважте, що немає необхідності переосмислювати onCreateView()метод, і немає потреби надувати будь-який макет xml:

public class MapTabFragment extends SupportMapFragment 
                                    implements OnMapReadyCallback {

    private GoogleMap mMap;
    private Marker marker;


    public MapTabFragment() {
    }

    @Override
    public void onResume() {
        super.onResume();

        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mMap == null) {

            getMapAsync(this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        mMap = googleMap;
        setUpMap();
    }

    private void setUpMap() {

        mMap.setMyLocationEnabled(true);
        mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
        mMap.getUiSettings().setMapToolbarEnabled(false);


        mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {

            @Override
            public void onMapClick(LatLng point) {

                //remove previously placed Marker
                if (marker != null) {
                    marker.remove();
                }

                //place marker where user just clicked
                marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker")
                        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA)));

            }
        });

    }


}

Результат:

введіть тут опис зображення

Ось повний код класу, який я використовував для тестування, який включає Фрагмент заповнювача, який використовується для перших двох вкладок, і Фрагмент карти, що використовується для третьої вкладки:

public class MainActivity extends AppCompatActivity implements ActionBar.TabListener{


    SectionsPagerAdapter mSectionsPagerAdapter;

    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        final ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });

        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));
        }

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {

    }


    public class SectionsPagerAdapter extends FragmentPagerAdapter {

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

        @Override
        public Fragment getItem(int position) {

            switch (position) {
                case 0:
                    return PlaceholderFragment.newInstance(position + 1);
                case 1:
                    return PlaceholderFragment.newInstance(position + 1);
                case 2:
                    return MapTabFragment.newInstance(position + 1);
            }

            return null;
        }

        @Override
        public int getCount() {
            // Show 3 total pages.
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            Locale l = Locale.getDefault();

            switch (position) {
                case 0:
                    return getString(R.string.title_section1).toUpperCase(l);
                case 1:
                    return getString(R.string.title_section2).toUpperCase(l);
                case 2:
                    return getString(R.string.title_section3).toUpperCase(l);
            }
            return null;
        }
    }


    public static class PlaceholderFragment extends Fragment {

        private static final String ARG_SECTION_NUMBER = "section_number";

        TextView text;

        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);

            text = (TextView) rootView.findViewById(R.id.section_label);
            text.setText("placeholder");

            return rootView;
        }
    }

    public static class MapTabFragment extends SupportMapFragment implements
            OnMapReadyCallback {

        private static final String ARG_SECTION_NUMBER = "section_number";

        private GoogleMap mMap;
        private Marker marker;


        public static MapTabFragment newInstance(int sectionNumber) {
            MapTabFragment fragment = new MapTabFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public MapTabFragment() {
        }

        @Override
        public void onResume() {
            super.onResume();

            Log.d("MyMap", "onResume");
            setUpMapIfNeeded();
        }

        private void setUpMapIfNeeded() {

            if (mMap == null) {

                Log.d("MyMap", "setUpMapIfNeeded");

                getMapAsync(this);
            }
        }

        @Override
        public void onMapReady(GoogleMap googleMap) {
            Log.d("MyMap", "onMapReady");
            mMap = googleMap;
            setUpMap();
        }

        private void setUpMap() {

            mMap.setMyLocationEnabled(true);
            mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
            mMap.getUiSettings().setMapToolbarEnabled(false);


            mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {

                @Override
                public void onMapClick(LatLng point) {

                    Log.d("MyMap", "MapClick");

                    //remove previously placed Marker
                    if (marker != null) {
                        marker.remove();
                    }

                    //place marker where user just clicked
                    marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker")
                            .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA)));

                    Log.d("MyMap", "MapClick After Add Marker");

                }
            });

        }
    }
}

2

Я поважаю всі відповіді, але знайшов це рішення для одного вкладиша: Якщо n Число вкладок, то:

 mViewPager.setOffscreenPageLimit(n);

Приклад: У випадку згаданого випадку:

 mViewPager.setOffscreenPageLimit(2);

Перегляд пейджера реалізує чергу, тому вам не доведеться видаляти цей фрагмент. onCreateView викликається лише один раз.


1

Ви повертаєтесь або надуваєте макет двічі, просто перевірте, чи не надувається ви один раз.



0

Ви намагалися посилатися на свій власний MapFragmentклас у файлі макета?

<?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.nfc.demo.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

Ви можете, будь ласка, надсилати поштовий індекс спеціальної карти com.nfc.demo.MapFragment
Mina Fawzy

Я не автор коду - я просто використав код, розміщений у питанні. Ви повинні запитати Германа.
JJD

0

Якщо ви будете використовувати лише відповідь Відара Вальберга, ви отримаєте помилку, коли відкриєте іншу діяльність (наприклад) і повернетесь до карти. Або в моєму випадку відкрийте іншу діяльність, а потім із нової діяльності знову відкрийте карту (без використання кнопки "назад"). Але коли ви поєднаєте рішення Vidar Wahlberg та Matt Matt, ви не будете винятками.

макет

<com.example.ui.layout.MapWrapperLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/map_relative_layout">

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/root">

        <fragment xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/map"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            class="com.google.android.gms.maps.SupportMapFragment" />
    </RelativeLayout>
</<com.example.ui.layout.MapWrapperLayout>

Фрагмент

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    setHasOptionsMenu(true);
    if (view != null) {
        ViewGroup parent = (ViewGroup) view.getParent();
        if (parent != null){
            parent.removeView(view);
        }
    }
    try {
        view = inflater.inflate(R.layout.map_view, null);
        if(view!=null){
            ViewGroup root = (ViewGroup) view.findViewById(R.id.root);
...

@Override
public void onDestroyView() {
    super.onDestroyView();
    Fragment fragment = this.getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.map);
    if (fragment != null)
        getFragmentManager().beginTransaction().remove(fragment).commit();
}

0

У мене це було в viewPager, і збій стався через те, що будь-який фрагмент повинен мати власний тег, дублюючі теги або ідентифікатори для того ж фрагмента заборонені.


0

Я думаю, що в попередній lib для App-Compat для дочірнього фрагмента були помилки. Я спробував @Vidar Wahlberg та @ Matt's ans вони не працювали для мене. Після оновлення бібліотеки appcompat мій код ідеально працює без зайвих зусиль.


0

Що потрібно зауважити, це ваш додаток погано вийде з ладу в будь-якому з двох випадків: -

1) Щоб знову використовувати фрагмент із Maps, фрагмент MapView необхідно видалити, коли ваш фрагмент із відображенням Maps замінений на інший фрагмент у зворотному дзвінку onDestroyView.

в іншому випадку, коли ви спробуєте надути один і той же фрагмент двічі, повториться ідентифікатор дублювання, тег null або батьківський ідентифікатор з іншим фрагментом для com.google.android.gms.maps.MapFragment .

2) По-друге, ви не повинні змішувати операції app.Fragment з операціями API android.support.v4.app.Fragment API, наприклад, не використовувати android.app.FragmentTransaction для видалення v4.app.Фрагмент типу MapView Fragment. Змішування цього знову призведе до краху з боку фрагмента.

Ось приклад фрагмента коду для правильного використання MapView

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.serveroverload.yago.R;

/**
 * @author 663918
 *
 */
public class HomeFragment extends Fragment implements LocationListener {
    // Class to do operations on the Map
    GoogleMap googleMap;
    private LocationManager locationManager;

    public static Fragment newInstance() {
        return new HomeFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.home_fragment, container, false);
        Bundle bdl = getArguments();

        // setuping locatiomanager to perfrom location related operations
        locationManager = (LocationManager) getActivity().getSystemService(
                Context.LOCATION_SERVICE);

        // Requesting locationmanager for location updates
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 1, 1, this);

        // To get map from MapFragment from layout
        googleMap = ((MapFragment) getActivity().getFragmentManager()
                .findFragmentById(R.id.map)).getMap();

        // To change the map type to Satellite
        // googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

        // To show our current location in the map with dot
        // googleMap.setMyLocationEnabled(true);

        // To listen action whenever we click on the map
        googleMap.setOnMapClickListener(new OnMapClickListener() {

            @Override
            public void onMapClick(LatLng latLng) {
                /*
                 * LatLng:Class will give us selected position lattigude and
                 * longitude values
                 */
                Toast.makeText(getActivity(), latLng.toString(),
                        Toast.LENGTH_LONG).show();
            }
        });

        changeMapMode(2);

        // googleMap.setSatellite(true);
        googleMap.setTrafficEnabled(true);
        googleMap.setBuildingsEnabled(true);
        googleMap.setMyLocationEnabled(true);

        return v;
    }

    private void doZoom() {
        if (googleMap != null) {
            googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                    new LatLng(18.520430, 73.856744), 17));
        }
    }

    private void changeMapMode(int mapMode) {

        if (googleMap != null) {
            switch (mapMode) {
            case 0:
                googleMap.setMapType(GoogleMap.MAP_TYPE_NONE);
                break;

            case 1:
                googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                break;

            case 2:
                googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
                break;

            case 3:
                googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
                break;

            case 4:
                googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
                break;

            default:
                break;
            }
        }
    }

    private void createMarker(double latitude, double longitude) {
        // double latitude = 17.385044;
        // double longitude = 78.486671;

        // lets place some 10 random markers
        for (int i = 0; i < 10; i++) {
            // random latitude and logitude
            double[] randomLocation = createRandLocation(latitude, longitude);

            // Adding a marker
            MarkerOptions marker = new MarkerOptions().position(
                    new LatLng(randomLocation[0], randomLocation[1])).title(
                    "Hello Maps " + i);

            Log.e("Random", "> " + randomLocation[0] + ", " + randomLocation[1]);

            // changing marker color
            if (i == 0)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
            if (i == 1)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
            if (i == 2)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_CYAN));
            if (i == 3)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
            if (i == 4)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
            if (i == 5)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
            if (i == 6)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_RED));
            if (i == 7)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_ROSE));
            if (i == 8)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_VIOLET));
            if (i == 9)
                marker.icon(BitmapDescriptorFactory
                        .defaultMarker(BitmapDescriptorFactory.HUE_YELLOW));

            googleMap.addMarker(marker);

            // Move the camera to last position with a zoom level
            if (i == 9) {
                CameraPosition cameraPosition = new CameraPosition.Builder()
                        .target(new LatLng(randomLocation[0], randomLocation[1]))
                        .zoom(15).build();

                googleMap.animateCamera(CameraUpdateFactory
                        .newCameraPosition(cameraPosition));
            }
        }

    }

    /*
     * creating random postion around a location for testing purpose only
     */
    private double[] createRandLocation(double latitude, double longitude) {

        return new double[] { latitude + ((Math.random() - 0.5) / 500),
                longitude + ((Math.random() - 0.5) / 500),
                150 + ((Math.random() - 0.5) * 10) };
    }

    @Override
    public void onLocationChanged(Location location) {

        if (null != googleMap) {
            // To get lattitude value from location object
            double latti = location.getLatitude();
            // To get longitude value from location object
            double longi = location.getLongitude();

            // To hold lattitude and longitude values
            LatLng position = new LatLng(latti, longi);

            createMarker(latti, longi);

            // Creating object to pass our current location to the map
            MarkerOptions markerOptions = new MarkerOptions();
            // To store current location in the markeroptions object
            markerOptions.position(position);

            // Zooming to our current location with zoom level 17.0f
            googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(position,
                    17f));

            // adding markeroptions class object to the map to show our current
            // location in the map with help of default marker
            googleMap.addMarker(markerOptions);
        }

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onDestroyView() {
        // TODO Auto-generated method stub
        super.onDestroyView();

        locationManager.removeUpdates(this);

        android.app.Fragment fragment = getActivity().getFragmentManager()
                .findFragmentById(R.id.map);
        if (null != fragment) {
            android.app.FragmentTransaction ft = getActivity()
                    .getFragmentManager().beginTransaction();
            ft.remove(fragment);
            ft.commit();
        }
    }

}

XML

 <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
       />

Результат виглядає приблизно так: -введіть тут опис зображення

Сподіваюся, це комусь допоможе.


0

У цьому рішенні не потрібно приймати статичну змінну;

Button nextBtn;

private SupportMapFragment mMapFragment;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    if (mRootView != null) {
        ViewGroup parent = (ViewGroup) mRootView.getParent();
        Utility.log(0,"removeView","mRootView not NULL");
        if (parent != null) {
            Utility.log(0, "removeView", "view removeViewed");
            parent.removeAllViews();
        }
    }
    else {
        try {
            mRootView = inflater.inflate(R.layout.dummy_fragment_layout_one, container, false);//
        } catch (InflateException e) {
    /* map is already there, just return view as it is  */
            e.printStackTrace();
        }
    }

    return  mRootView;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    FragmentManager fm = getChildFragmentManager();
    SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.mapView);
    if (mapFragment == null) {
        mapFragment = new SupportMapFragment();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.mapView, mapFragment, "mapFragment");
        ft.commit();
        fm.executePendingTransactions();
    }
    //mapFragment.getMapAsync(this);
    nextBtn = (Button) view.findViewById(R.id.nextBtn);
    nextBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Utility.replaceSupportFragment(getActivity(),R.id.dummyFragment,dummyFragment_2.class.getSimpleName(),null,new dummyFragment_2());
        }
    });

}`

0

Я трохи запізнююсь на вечірку, але Нен з цих відповідей мені допоміг у моєму випадку. Я використовував карту Google як SupportMapFragment і PlaceAutocompleteFragment як у своєму фрагменті. Оскільки всі відповіді вказували на той факт, що проблема полягає в тому, що SupportMapFragment - це карта, яку потрібно відтворити і перемалювати. Але після копання з’ясували, що моя проблема була насправді з PlaceAutocompleteFragment

Отже, ось робоче рішення для тих, хто стикається з цією проблемою через SupportMapFragment та SupportMapFragment

 //Global SupportMapFragment mapFragment;
 mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mapFragment);
    FragmentManager fm = getChildFragmentManager();

    if (mapFragment == null) {
        mapFragment = SupportMapFragment.newInstance();
        fm.beginTransaction().replace(R.id.mapFragment, mapFragment).commit();
        fm.executePendingTransactions();
    }

    mapFragment.getMapAsync(this);

    //Global PlaceAutocompleteFragment autocompleteFragment;


    if (autocompleteFragment == null) {
        autocompleteFragment = (PlaceAutocompleteFragment) getActivity().getFragmentManager().findFragmentById(R.id.place_autoCompleteFragment);

    }

І в onDestroyView очистіть SupportMapFragment і SupportMapFragment

@Override
public void onDestroyView() {
    super.onDestroyView();


    if (getActivity() != null) {
        Log.e("res","place dlted");
        android.app.FragmentManager fragmentManager = getActivity().getFragmentManager();
        android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.remove(autocompleteFragment);
        fragmentTransaction.commit(); 
       //Use commitAllowingStateLoss() if getting exception 

        autocompleteFragment = null;
    }


}

0

Спробуйте встановити ідентифікатор (android: id = "@ + id / maps_dialog") для батьківського макета mapView. Працює для мене.


0

Усі, хто зараз приходить сюди, який отримує подібний тип помилок під час відкриття того Dialogчи іншого Fragmentза допомогою Google Місця на карті AutocompleteSupportFragment, спробуйте цей одноклапник (я не знаю, наскільки це безпечно, але це працює для мене):

autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();

перед тим, як відхилити / знищити фрагмент.


-1
<?xml version="1.0" encoding="utf-8"?>

  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
   android:layout_height="match_parent" >


<com.google.android.gms.maps.MapView
    android:id="@+id/mapview"
    android:layout_width="100dip"
    android:layout_height="100dip"
    android:layout_alignParentTop="true"
    android:layout_alignRight="@+id/textView1"
    android:layout_marginRight="15dp" >
</com.google.android.gms.maps.MapView>

Чому ви не вставите карту за допомогою об’єкта MapView замість MapFragment? Я не впевнений, чи є якісь обмеження в MapView, хоча я вважаю це корисним.


Вибачте, я дізнався, що це застаріла попередня версія API Google Maps
Ankur Gautam,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.