1. Fragment 使用時要有一個無參構造函數
如果沒有無參構造函數,而是像按照普通類來使用,只創建有參構造函數,則會出現 android.support.v4.app.Fragment$InstantiationException 錯誤。
原因:Fragment 和 Activity 都是生命周期的組件,不能看做一般的類。如果非要使用有參構造函數,可能在使用的時候第一次傳參沒有問題,但是大概率在后面使用的時候出現問題。因為Fragment的什么周期依附在Activity中,如果Activity為null,那么Fragment肯定不能夠正常使用了,比如手機屏幕的橫豎屏切換導致Activity重建了。
至於為什么是這樣的呢?看下Fragment初始化的源碼,有這么一段:
/** * Create a new instance of a Fragment with the given class name. This is * the same as calling its empty constructor. * * @param context The calling context being used to instantiate the fragment. * This is currently just used to get its ClassLoader. * @param fname The class name of the fragment to instantiate. * @param args Bundle of arguments to supply to the fragment, which it * can retrieve with {@link #getArguments()}. May be null. * @return Returns a new fragment instance. * @throws InstantiationException If there is a failure in instantiating * the given fragment class. This is a runtime exception; it is not * normally expected to happen. */
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { try { Class<?> clazz = sClassMap.get(fname); if (clazz == null) { // Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname); sClassMap.put(fname, clazz); } Fragment f = (Fragment)clazz.newInstance(); if (args != null) { args.setClassLoader(f.getClass().getClassLoader()); f.mArguments = args; } return f; } catch (ClassNotFoundException e) { throw new InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e); } catch (java.lang.InstantiationException e) { throw new InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e); } catch (IllegalAccessException e) { throw new InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e); } }
整個過程中,Fragment的創建其實也是利用了無參數的構造方法去實例化.但關鍵的是,它將Bundle傳類新建的Fragment,這樣舊的Fragment和新的Fragment就能擁有一樣的Bundle,從而達到利用Bundle傳遞參數的目的. Android的SDK文檔也給出來相關的說法:
2. 給 Fragment 傳遞參數
一定要使用 Bundle 方式傳遞參數,而不是通過重載構造函數傳遞參數。
public static VechileFrag newInstance(Vehicle vehicle, String userId, boolean isAdd) { VechileFrag mf = new VechileFrag(); Bundle args = new Bundle(); args.putString("userId", userId); args.putBoolean("isAdd", isAdd); args.putParcelable("vehicle", vehicle); mf.setArguments(args); return mf; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); if (args != null) { userId = args.getString("userId"); isAdd = args.getBoolean("isAdd"); vehicle = args.getParcelable("vehicle"); if (vehicle == null) { vehicle = new Vehicle(); } } }
3. Fragment 與 Activity 通信
在 Fragment 中定義一個接口和要回調的方法, Activity實現Fragment接口,需要時回調 Fragment 方法。
public IVechile mIVechile; public interface IVechile { public void submitCarSuccess(String carId, String plateNo); } @Override public void onAttach(Activity activity) { fueltypes = FuelType.getList(activity); try { mIVechile = (IVechile) activity; } catch (Exception e) { // TODO: handle exception
} } }