Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment

前端 未结 23 1559
-上瘾入骨i
-上瘾入骨i 2020-11-22 00:25

I have an application with three tabs.

Each tab has its own layout .xml file. The main.xml has its own map fragment. It\'s the one that shows up when the application

相关标签:
23条回答
  • 2020-11-22 01:02

    The answer Matt suggests works, but it cause the map to be recreated and redrawn, which isn't always desirable. After lots of trial and error, I found a solution that works for me:

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

    For good measure, here's "map.xml" (R.layout.map) with 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>
    

    I hope this helps, but I can't guarantee that it doesn't have any adverse effects.

    Edit: There were some adverse effects, such as when exiting the application and starting it again. Since the application isn't necessarily completely shut down (but just put to sleep in the background), the previous code i submitted would fail upon restarting the application. I've updated the code to something that works for me, both going in & out of the map and exiting and restarting the application, I'm not too happy with the try-catch bit, but it seem to work well enough. When looking at the stack trace it occurred to me that I could just check if the map fragment is in the FragmentManager, no need for the try-catch block, code updated.

    More edits: Turns out you need that try-catch after all. Just checking for the map fragment turned out not to work so well after all. Blergh.

    0 讨论(0)
  • 2020-11-22 01:03

    I have lost hours today to find the reason, fortunately this issue is not because of MapFragment implementation, fnfortunately, this does not work because nested fragments are only supported through support library from rev 11.

    My implementation has an activity with actionbar (in tabbed mode) with two tabs (no viewpager), one having the map and the other having a list of entries. Of course I've been quite naive to use MapFragment inside my tab-fragments, et voila the app crashed everytime I switched back to map-tab.

    ( The same issue I also would have in case my tab-fragment would inflate any layout containing any other fragment ).

    One option is to use the MapView (instead of MapFragment), with some overhead though ( see MapView Docs as drop-in replacement in the layout.xml, another option is to use support-library up from rev. 11 but then take programmatic approach since nested fragments are neither supported via layout. Or just working around programmatically by explicitely destroying the fragment (like in the answer from Matt / Vidar), btw: same effect is achieved using the MapView (option 1).

    But actually, I did not want to loose the map everytime I tab away, that is, I wanted to keep it in memory and cleanup only upon activity close, so I decided to simply hide/show the map while tabbing, see FragmentTransaction / hide

    0 讨论(0)
  • 2020-11-22 01:03

    Things to Note here is your app will crash badly in either of two cases:-

    1) In order to reuse fragment with Maps again MapView Fragment must be removed when your fragment showing Maps got replaced with other fragment in onDestroyView callback.

    else when you try to inflate same fragment twice Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment error will happen.

    2) Secondly you must not mix app.Fragment operations with android.support.v4.app.Fragment API operations eg.do not use android.app.FragmentTransaction to remove v4.app.Fragment type MapView Fragment. Mixing this will again result into crash from fragment side.

    Here is sample code snippet for correct usage of 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"
           />
    

    Result looks like this :-

    Hope it will help Somebody.

    0 讨论(0)
  • 2020-11-22 01:04

    Nested fragments are not currently supported. Try Support Package, revision 11.

    0 讨论(0)
  • 2020-11-22 01:05

    For those who are still running into this issue, the best way to make sure you don't get this error with a Map in a Tab is to make the Fragment extend SupportMapFragment instead of nesting a SupportMapFragment inside the Fragment used for the Tab.

    I just got this working using a ViewPager with a FragmentPagerAdapter, with the SupportMapFragment in the third Tab.

    Here is the general structure, note there is no need to override the onCreateView() method, and there is no need to inflate any layout 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)));
    
                }
            });
    
        }
    
    
    }
    

    Result:

    enter image description here

    Here is the full class code that I used to test with, which includes the placeholder Fragment used for the first two Tabs, and the Map Fragment used for the third Tab:

    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");
    
                    }
                });
    
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 01:05

    I think there was some bugs in previous App-Compat lib for child Fragment. I tried @Vidar Wahlberg and @Matt's ans they did not work for me. After updating the appcompat library my code run perfectly without any extra effort.

    0 讨论(0)
提交回复
热议问题