问题
I have an activity with Navigation Drawer and HomeFragment.
HomeFragment contains two nested fragments (child fragments): SupportMapFragment and ActionFragment.
I declare the GoogleMap object sMap
as static in HomeFragment, so it can be accessed and modified by the nested fragment ActionFragment.
Everything works fine in the first run. The map can be manipulated from within the ActionFragment. But when the HomeFragment is removed and reloaded at a later point of time, the map could only be modified from the HomeFragment (parent fragment) and not from the ActionFragment (nested fragment) anymore.
I am failing to understand why something that works in the first instance doesn't work when the fragment is reloaded. Below are the codes which I have kept to minimum for easier understanding.
HomeFragment.java (Parent fragment)
public class HomeFragment extends Fragment {
private static GoogleMap sMap;
public static GoogleMap getMap() {
return sMap;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate layout
return inflater.inflate(R.layout.f_home, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Obtain the GoogleMap object
try {
if (sMap == null) {
sMap = ((SupportMapFragment) getActivity().getSupportFragmentManager()
.findFragmentById(R.id.home_map)).getMap();
}
} catch (Exception e) {
e.printStackTrace();
}
// Load child fragment
if (savedInstanceState == null) {
Fragment _actionFragment = new ActionFragment();
FragmentTransaction _ft = getChildFragmentManager().beginTransaction();
_ft.replace(R.id.action_container_bottom, _actionFragment);
_ft.commit();
}
}
@Override
public void onDestroyView(){
super.onDestroyView();
// Remove the map fragment to prevent errors on the next load
if(sMap != null){
try {
getActivity().getSupportFragmentManager()
.beginTransaction()
.remove(getActivity().getSupportFragmentManager()
.findFragmentById(R.id.home_map))
.commit();
sMap = null;
} catch (Exception e){}
}
}
}
f_home.xml (Layout of parent fragment)
<?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">
<fragment
android:id="@+id/home_map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/action_container_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</RelativeLayout>
ActionFragment.java (Nested or Child fragment)
public class ActionFragment extends Fragment {
private static GoogleMap sMap = HomeFragment.getMap();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.f_action, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// check if map is created successfully or not
if (sMap != null) {
sMap.clear();
sMap.setPadding(0, 0, 0, 300);
sMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
sMap.setBuildingsEnabled(false);
sMap.setMyLocationEnabled(false);
sMap.getUiSettings().setRotateGesturesEnabled(false);
sMap.getUiSettings().setTiltGesturesEnabled(false);
sMap.getUiSettings().setZoomControlsEnabled(false);
sMap.moveCamera(CameraUpdateFactory.zoomTo(12));
}
}
}
Any help would be immensely appreciated as I am struggling with this for a long time. Thank you!
回答1:
Bad design. It's wrong to keep static var for storing reference to a map. Suppose you have two HomeFragment or several activities that have HomeFragment inside. It won't work.
I think you should use tags to find fragments. Or better create some interface in activity that implements communication between your fragments. Anyway you must not use static field for a map in a way you do.
You can use this code:
((SupportMapFragment) getActivity().getSupportFragmentManager()
.findFragmentById(R.id.home_map)).getMap();
to get map every time you need.
回答2:
Problem
The problem is that the classloader only loads ActionFragment
once, that's why your static field is only initialized the first round.
What happens in first run:
1) HomeFragment#onActivityCreated()
HomeFragment#sMap
is null -> locate via findViewById()HomeFragment#sMap
= [1]
2) classloader for ActionFragment
ActionFragment#sMap
is null -> load fromHomeFragment.getMap()
ActionFragment#sMap
= [1]
... home gets destroyed ...
3) HomeActivity#onDestroyView()
HomeFragment#sMap
= null
... home gets recreated ...
4) HomeFragment#onActivityCreated()
HomeFragment#sMap
is null -> locate via findViewById()HomeFragment#sMap
= [2]
Since the class ActionFragment
has already been loaded into memory, the class won't be loaded again and thus the code for your static field ActionFragment#sMap
will not be executed again. it's still refering to the old (invalid) [1] instance.
Solution
In order to fix this issue, you can do the following:
public class ActionFragment extends Fragment {
private GoogleMap mMap;
public void onAttach(Activity activity) {
super.onAttach(activity);
mMap = HomeFragment.getMap();
}
public void onDetach() {
super.onDetach() {
mMap = null;
}
}
With this code, each time the ActionFragment
is attached, it will request the latest map-instance of HomeFragment
.
回答3:
Issue is you are trying to inflate a layout containing SupportMapFragment
into another fragment HomeFragment
. Nested fragments are only supported programatically. From docs:
Note: You cannot inflate a layout into a fragment when that layout includes a fragment. Nested fragments are only supported when added to a fragment dynamically.
f_home.xml
<!--<fragment
android:id="@+id/home_map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />-->
<FrameLayout
android:id="@+id/home_map"
android:layout_width="match_parent"
android:layout_height="match_parent" />
HomeFragment
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Obtain the GoogleMap object
try {
if (sMap == null) {
/*sMap = ((SupportMapFragment) getActivity().getSupportFragmentManager()
.findFragmentById(R.id.home_map)).getMap();*/
mapFragment = new SupportMapFragment();
sMap = mapFragment.getMap();
getChildFragmentManager()
.beginTransaction()
.replace(R.id.home_map, mapFragment)
.commit();
}
} catch (Exception e) {
e.printStackTrace();
}
// Load child fragment
if (savedInstanceState == null) {
Fragment _actionFragment = new ActionFragment();
FragmentTransaction _ft = getChildFragmentManager().beginTransaction();
_ft.replace(R.id.action_container_bottom, _actionFragment);
_ft.commit();
}
}
@Override
public void onDestroyView(){
super.onDestroyView();
// Remove the map fragment to prevent errors on the next load
if(sMap != null){
try {
/*getActivity().getSupportFragmentManager()
.beginTransaction()
.remove(getActivity().getSupportFragmentManager()
.findFragmentById(R.id.home_map))
.commit();*/
getChildFragmentManager()
.beginTransaction()
.remove(mapFragment)
.commit();
sMap = null;
mapFragment = null;
} catch (Exception e){}
}
}
In ActionFragment
instead of maintaining a static reference to GoogleMap
obtained from HomeFragment
, use findFragmentById
api.
来源:https://stackoverflow.com/questions/26854205/map-fragment-couldnt-be-modified-after-fragment-reload