it-roy-ru.com

Дублированный идентификатор, нулевой тег или родительский идентификатор с другим фрагментом для com.google.Android.gms.maps.MapFragment

У меня есть приложение с тремя вкладками.

Каждая вкладка имеет свой собственный .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
309
hermann

Ответ, предложенный Мэттом, работает, но он заставляет карту заново создаваться и перерисовываться, что не всегда желательно…. После множества проб и ошибок я нашел решение, которое работает для меня:

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>

Я надеюсь, что это помогает, но я не могу гарантировать, что это не будет иметь никаких негативных последствий.

Правка: Были некоторые побочные эффекты, такие как выход из приложения и запуск его снова. Поскольку приложение не обязательно полностью закрыто (а просто помещено в спящий режим в фоновом режиме), предыдущий код, который я отправил, потерпит неудачу при перезапуске приложения. Я обновил код, чтобы он работал для меня, входя и выходя из карты, а также выходя и перезапуская приложение. Я не слишком доволен битом try-catch, но, похоже, он работает достаточно хорошо. Когда я смотрел на трассировку стека, мне пришло в голову, что я могу просто проверить, находится ли фрагмент карты в FragmentManager, нет необходимости в блоке try-catch, код обновлен.

Больше правок: Оказывается, вам нужен этот try-catch в конце концов. В конце концов, проверка фрагмента карты не сработала. Blergh.

398
Vidar Wahlberg

Проблема в том, что то, что вы пытаетесь сделать, не должно быть сделано. Вы не должны раздувать фрагменты внутри других фрагментов. Из Android документация :

Примечание. Вы не можете раздувать макет на фрагмент, когда этот макет включает в себя <фрагмент>. Вложенные фрагменты поддерживаются только при добавлении к фрагменту динамически.

Хотя вы можете выполнить задачу с помощью хаков, представленных здесь, я настоятельно рекомендую вам не делать этого. Невозможно быть уверенным, что эти хаки будут обрабатывать то, что делает каждая новая ОС 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>

Затем в методе Fragment onViewCreated(View view, @Nullable Bundle savedInstanceState):

@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);
}
253
Justin Breitfeller

У меня была та же проблема, и я смог решить ее, вручную удалив 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 ].

167
Matt

Это мой ответ:

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();
    }
} 
21
Zou
  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;
}
10
Desmond Lua

Объявите объект 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"
    />

Выше код решил мою проблему, и она работает нормально

4
Digvijay Machale

Я бы порекомендовал replace(), а не attach()/detach() при обработке ваших вкладок.

Или переключитесь на ViewPager. Вот пример проекта , показывающий ViewPager, с вкладками, размещающий 10 карт. 

3
CommonsWare

Другое решение:

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

Вот и все, если не NULL, вам не нужно повторно инициализировать его удаление из родителя является ненужным шагом.

2
AnonymousDev

Я потерял часы сегодня, чтобы найти причину, к счастью, эта проблема не из-за реализации MapFragment, к сожалению, это не работает, потому что вложенные фрагменты поддерживаются только через библиотеку поддержки начиная с версии 11.

Моя реализация имеет действие с панелью действий (в режиме вкладок) с двумя вкладками (без окна просмотра), одна из которых имеет карту, а другая - список записей. Конечно, я был довольно наивен, чтобы использовать MapFragment внутри моих фрагментов вкладок, и вуаля приложение вылетало каждый раз, когда я переключался на map-tab.

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

Один из вариантов - использовать MapView (вместо MapFragment), хотя с некоторыми накладными расходами (см. Документы MapView в качестве замены в файле layout.xml, другой вариант - использовать библиотеку поддержки, начиная с версии 11, но затем используйте программный подход, поскольку вложенные фрагменты не поддерживаются ни макетом, ни просто обходятся программно путем явного уничтожения фрагмента (как в ответе Мэтта/Видара), btw: тот же эффект достигается с помощью MapView (опция 1).

Но на самом деле, я не хотел терять карту каждый раз, когда вкладывал, то есть я хотел держать ее в памяти и очищать только после закрытия активности, поэтому я решил просто скрыть/показать карту во время вкладки, см. FragmentTransaction/скрывать

2
comeGetSome

Я уважаю все ответы, но я нашел это одно линейное решение: Если n - количество вкладок, то:

 mViewPager.setOffscreenPageLimit(n);

Пример: В случае, упомянутом:

 mViewPager.setOffscreenPageLimit(2);

View pager реализует очередь, поэтому вам не нужно позволять ему удалять этот фрагмент. onCreateView вызывается только один раз.

2
Jayant Arora

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

Я только что получил это с помощью ViewPager с FragmentPagerAdapter, с SupportMapFragment в третьей вкладке.

Вот общая структура, обратите внимание, что нет необходимости переопределять метод 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)));

            }
        });

    }


}

Результат:

enter image description here

Вот полный код класса, который я использовал для тестирования, включая фрагмент-заполнитель, использованный для первых двух вкладок, и фрагмент карты, использованный для третьей вкладки:

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
Daniel Nugent

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

0
maddy d

В этом решении вам не нужно принимать статические переменные;

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
Lalit kumar

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

0
hieudev develo

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

0
user1396018

Я немного опоздал на вечеринку, но ни один из этих ответов не помог мне в моем случае. Я использовал карту 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
Ratul Bin Tazul

Обратите внимание на то, что ваше приложение будет плохо работать в любом из двух случаев: -

1) Для повторного использования фрагмента с Картами фрагмент MapView должен быть удален, когда Ваш фрагмент, показывающий Карты, был заменен другим фрагментом в обратном вызове onDestroyView.

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

2) Во-вторых, вы не должны смешивать операции app.Fragment с Android.support.v4.app.Fragment API-операции, например, не использовать Android.app.FragmentTransaction для удаления v4.app.Fragment типа Фрагмент MapView. Смешивание этого снова приведет к падению со стороны фрагмента. 

Вот пример кода для правильного использования 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"
       />

Результат выглядит так: -  enter image description here

Надеюсь, это кому-нибудь поможет.

0
Hitesh Sahu

Вложенные фрагменты в настоящее время не поддерживаются . Try Пакет поддержки, редакция 11 .

0
Ivan

Вы пытались сослаться на свой пользовательский класс 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>
0
JJD

Если вы будете использовать только ответ Vidar Wahlberg, вы получите ошибку, когда откроете другую деятельность (например) и вернетесь к карте. Или, в моем случае, откройте другой вид деятельности, а затем снова откройте карту из нового вида деятельности (без использования кнопки «Назад»)…. Но если вы объедините решение Vidar Wahlberg и решение 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
Vlad Hudnitsky