片段中的 GoogleMap 在方向更改时变为空白

GoogleMap in fragment goes blank at orientation change

我有一个 DrawerLayout,其主要内容中有一个片段。此片段中有一个 GoogleMap。当应用程序启动时,它运行良好,但在方向更改时地图变为空白。我不明白为什么..有人可以解释一下吗?

这是完整的代码。

主要活动

public class MainActivity extends FragmentActivity {

    public static FragmentManager mapFragmentManager;
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private ActionBarDrawerToggle mDrawerToggle;
    public static Context con;
    private CharSequence mDrawerTitle,mTitle;
    private String[] drawerOptions;
    static TextView corTextView;
    static EditText addressEditText,commentEditText;
    final static int image_pick=100,new_pic=112;
    static ImageView loadPic;
    static Bitmap bitmap;

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

        //set drawer navigation
        mTitle = mDrawerTitle = getTitle();
        drawerOptions = getResources().getStringArray(R.array.options_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
        mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, drawerOptions));
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);
        mDrawerToggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                mDrawerLayout,         /* DrawerLayout object */
                R.drawable.ic_drawer,  /* nav drawer image to replace 'Up' caret */
                R.string.drawer_open,  /* "open drawer" description for accessibility */
                R.string.drawer_close  /* "close drawer" description for accessibility */
                ) {
            public void onDrawerClosed(View view) {
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            public void onDrawerOpened(View drawerView) {
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        if (savedInstanceState == null) {
            selectItem(0);
        }

        //load main content
        mapFragmentManager = getSupportFragmentManager();
        android.app.FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, new LocationFragment()).commit();

    }

    @Override
    protected void onStop() {
        super.onStop();
        try{
            MainActivity.mapFragmentManager.beginTransaction().remove(MainActivity.mapFragmentManager.findFragmentById(R.id.location_map)).commit();
        }catch(Exception e){}
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }


    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        switch(item.getItemId()) {
        case R.id.action_websearch:
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }


    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    }

    private void selectItem(int position) {
                    mDrawerList.setItemChecked(position, true);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

    @Override
    public void setTitle(CharSequence title) {
        mTitle = title;
        getActionBar().setTitle(mTitle);
    }


    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggls
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    public static class LocationFragment extends Fragment{
        public LocationFragment() { }

        private static View view;
        private static GoogleMap mMap;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
            if (container == null) { return null;  }

            if(view==null){
                view = (View) inflater.inflate(R.layout.map_fragment, container, false);
            }
            corTextView = (TextView)view.findViewById(R.id.cor_location_tv);
            addressEditText = (EditText)view.findViewById(R.id.address_location_tv);
            commentEditText = (EditText)view.findViewById(R.id.commentEditText);

            Button sendButton = (Button)view.findViewById(R.id.send_button);
            sendButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    sendData();
                }
            });

            loadPic = (ImageView)view.findViewById(R.id.load_pic_img);
            loadPic.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    AlertDialog.Builder builder =  new AlertDialog.Builder(con);
                    final CharSequence[] items = {"Take Pic Now","Load From Gallery"};
                    builder.setItems(items, new OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            switch (which){
                            case 0: takePicture(); break;
                            case 1: pickPicture(); break;
                            }
                        }
                    });
                    builder.show();
                }
            });

            setUpMapIfNeeded(); 

            return view;
        }


        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            // TODO Auto-generated method stub
            super.onActivityResult(requestCode, resultCode, data);
            if(resultCode==Activity.RESULT_OK){
                Uri source = data.getData();
                String imagepath = F.getRealPathFromURI(getActivity(), source);
                bitmap=F.getMiniThumbnailFromPath(con.getContentResolver(), imagepath);
                loadPic.setImageDrawable(new BitmapDrawable(con.getApplicationContext().getResources(),bitmap));
                loadPic.setScaleType(ImageView.ScaleType.FIT_CENTER);
            }
        }

        /***** Sets up the map if it is possible to do so *****/
        public static void setUpMapIfNeeded() {
            // Do a null check to confirm that we have not already instantiated the map.
            if (mMap == null) {
                // Try to obtain the map from the SupportMapFragment.
                mMap = ((SupportMapFragment) mapFragmentManager.findFragmentById(R.id.location_map)).getMap();
                // Check if we were successful in obtaining the map.
                if (mMap != null)
                    setUpMap();
            }
        }


        private static void setUpMap() {
            mMap.setMyLocationEnabled(true);
            LocationManager locationManager = (LocationManager) con.getSystemService(LOCATION_SERVICE);
            Criteria criteria = new Criteria();
            String provider = locationManager.getBestProvider(criteria, true);
            Location location = locationManager.getLastKnownLocation(provider);

            if(location!=null){
                locationChanged(location);
            }
            locationManager.requestLocationUpdates(provider, 20000, 0,listener );
        }


       public void sendData(){
            String comment= commentEditText.getText().toString();
            String address = addressEditText.getText().toString();
            if(address.equals("") || address.length()<5){
                Toast.makeText(con, "Type an address...", Toast.LENGTH_SHORT).show();
            }
            else if(bitmap ==null || bitmap.isRecycled()){
                Toast.makeText(con, "Load a picture..", Toast.LENGTH_SHORT).show();
            }
            else{
                //send data
            }
       }

       public static void setAddressFromCoordinates(double latitude, double longitude){
           corTextView.setText("Latitude:" +  latitude  + ", Longitude:"+ longitude );  
           String address = F.translateCoordinatesToAddress(con, latitude, longitude);
           if(!addressEditText.getText().toString().equals("") && address.equals("")){}
           else{addressEditText.setText(address);}
       }


       public static void locationChanged(Location location){
            double latitude = location.getLatitude();
            double longitude = location.getLongitude();     

            mMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(latitude, longitude)));
            mMap.animateCamera(CameraUpdateFactory.zoomTo(15));
            setAddressFromCoordinates(latitude,longitude);

            mMap.clear();
            mMap.addMarker(new MarkerOptions().position(new LatLng(latitude, longitude)).title("My Home").snippet("Home Address"));
            mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latitude, longitude), 13));

           CameraPosition cameraPosition = new CameraPosition.Builder()
           .target(new LatLng(latitude, longitude))      // Sets the center of the map to location user
           .zoom(17)                   // Sets the zoom
           .bearing(90)                // Sets the orientation of the camera to east
           .tilt(40)                   // Sets the tilt of the camera to 30 degrees
           .build();                   // Creates a CameraPosition from the builder
           mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
       }




      public static LocationListener listener = new LocationListener(){
        @Override
        public void onLocationChanged(Location location) {
            locationChanged(location);
        }
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) { }
        @Override
        public void onProviderEnabled(String provider) { }
        @Override
        public void onProviderDisabled(String provider) { }
      };


        public void takePicture(){
            Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(cameraIntent, new_pic);
        }
        public void pickPicture(){
            startActivityForResult(new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI), image_pick);
        }


    }


}

activity_main.xml

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

   <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

   <ListView
        android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
</android.support.v4.widget.DrawerLayout>

map_fragment.xml

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:orientation="vertical"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
     android:layout_height="wrap_content" >

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >

    <ImageView
        android:id="@+id/load_pic_img"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/take_pic" />

    <TextView
        android:layout_gravity="center"
        android:textColor="#376682"
        android:gravity="center"
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Chosen Problem Category"
        android:textAppearance="?android:attr/textAppearanceSmall" />


    <Button
        android:id="@+id/button1"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="5dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Select Problem Category" />


    <TextView
        android:id="@+id/relative_info_title"
        android:layout_marginLeft="15dp"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Relative Information"
        android:textAppearance="?android:attr/textAppearanceSmall" />
    <EditText
        android:id="@+id/commentEditText"
        android:gravity="center"
        android:hint="write your comment..."
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/aproximate_address_title"
        android:layout_marginLeft="15dp"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Aproximate Address"
        android:textAppearance="?android:attr/textAppearanceSmall" />





    <TextView 
        android:id="@+id/cor_location_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

    <EditText 
        android:id="@+id/address_location_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="if address not found type it..."
        />
    <fragment
        android:id="@+id/location_map"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        class="com.google.android.gms.maps.SupportMapFragment" />


    <Button
        android:id="@+id/send_button"
        android:background="#01b19f"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginBottom="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send" />

    </LinearLayout>
</ScrollView>

为什么要使用静态地图归档?

private static GoogleMap mMap;

尝试删除所有静态标记,使用对象归档。

private GoogleMap mMap;
private View view;
...

希望对您有所帮助。

当 LayoutInflator 膨胀视图时,它使用当前上下文在视图对象中创建一个引用

view = (View) inflater.inflate(R.layout.map_fragment, container, false);

问题出在你的静态视图字段:

private static View view;

方向更改(即屏幕旋转)导致 activity 重新启动,再次调用 'onCreate()' 方法并启动 Activity 和非保留片段的新实例。

当 Activity 重新启动并调用 onCreate() 方法时,您的代码会创建 LocationFragment() 的新实例。

当创建了一个新的 Fragment,但您的静态视图字段包含对前一个 Fragment 的 View 的引用时,视图中的 Context 将保留对旧 Fragment 的 Context 的无效引用。因此,您不应使用静态视图字段来保存引用

解决此问题的最简单方法是使视图字段非静态

private View view;

此外,您将 运行 陷入性能问题,因为您正在使用同步方法加载地图:

mMap = ((SupportMapFragment) mapFragmentManager.findFragmentById(R.id.location_map)).getMap();

相反,您应该使用

((SupportMapFragment)mapFragmentManager.findFragmentById(R.id.location_map)).getMapAsync(this);

并让你的 LocationFragment class 实现 OnMapReadyCallback

这会在加载地图时让屏幕保持空白,但会在加载地图时避免屏幕阻塞或冻结

如果您愿意,可以通过在 LocationFragment 上使用 setRetainInstance() 来解决这个问题,这会导致片段在旋转时不重绘,并为您保留相同的 GoogleMaps 对象和视图。要保留片段实例,请在片段的 onCreate() 方法中添加 setRetainInstance(true)

public class LocationFragment extends Fragment {
....
protected void onCreate(Bundle saved) {
super.onCreate(saved);
setRetainInstance(true);
...
}

并在您的 Activity 中替换

mapFragmentManager = getSupportFragmentManager();
    android.app.FragmentManager fragmentManager =      getFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.content_frame, new     LocationFragment()).commit();

String TAG = "LocationFrag";
Fragment locationFragment = getSupportedFragmentManager().findFragmentByTag(TAG);

if (locationFragment != null && locationFragment instanceof LocationFragment) {
    getSupportedFragmentManager().replace(R.id.content_frame, locationFragment, TAG); 
}
getSupportedFragmentManager().replace(R.id.content_frame, new LocationFragment(), TAG);