重新加载片段后无法修改地图片段


  1. 我有导航抽屉HomeFragment活动

  2. HomeFragment包含两个嵌套片段(子片段):SupportMapFragmentActionFragment

我在HomeFragment中将GoogleMap对象sMap声明为静态,因此嵌套片段ActionFragment以访问和修改它。

第一次运行时一切正常。映射可以在ActionFragment中进行操作。但是,当HomeFragment被移除并在稍后的时间点重新加载时,映射只能从HomeFragment(父片段)修改,而不能再从ActionFragment(嵌套片段)修改。

我不明白为什么在第一个实例中有效的东西在重新加载片段时不起作用。以下是我为了更容易理解而尽量减少的代码。


HomeFragment.java(父片段)

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(父片段的布局)

<?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(嵌套或子片段)

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

任何帮助都将不胜感激,因为我已经为此挣扎了很长一段时间。非常感谢。

问题

问题是类加载器只加载ActionFragment一次,这就是为什么静态字段只在第一轮初始化的原因。

第一次运行时会发生什么:

1) HomeFragment#onActivityCreated()

  • HomeFragment#sMap为空->通过findViewById()定位
  • HomeFragment#sMap=[1]

2) ActionFragment 的类加载器

  • ActionFragment#sMap为空->从HomeFragment.getMap()加载
  • ActionFragment#sMap=[1]

。。。家被毁了

3) HomeActivity#onDestroyView()

  • HomeFragment#sMap=空

。。。家被重新创建

4) HomeFragment#onActivityCreated()

  • HomeFragment#sMap为空->通过findViewById()定位
  • HomeFragment#sMap=[2]

由于类ActionFragment已经加载到内存中,因此不会再次加载该类,因此静态字段ActionFragment#sMap的代码将不会再次执行。它仍然引用旧的(无效的)[1]实例。

解决方案

为了解决这个问题,您可以执行以下操作:

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

使用此代码,每次附加ActionFragment时,它都会请求HomeFragment的最新映射实例。

糟糕的设计。保留静态var来存储对映射的引用是错误的。假设您有两个HomeFragment或多个活动,其中包含HomeFragment。这行不通。

我认为你应该使用标签来寻找碎片。或者更好地在活动中创建一些接口,实现片段之间的通信。无论如何,你不能使用静态字段的地图,你做的方式。

你可以使用这个代码:

((SupportMapFragment) getActivity().getSupportFragmentManager()
                        .findFragmentById(R.id.home_map)).getMap();

每次需要的时候都能得到地图。

问题是您正试图将包含SupportMapFragment的布局扩展到另一个片段HomeFragment中。嵌套片段仅在程序上受支持。来自文档:

注意:当布局为碎片时,不能将该布局膨胀为碎片包括片段。只有在添加时才支持嵌套片段动态地转换为片段。

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" />

主页片段

    @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){}
        }
    }

ActionFragment中,使用findFragmentByIdapi,而不是维护从HomeFragment获得的对GoogleMap的静态引用。