本文共 6422 字,大约阅读时间需要 21 分钟。
在开发Android应用时,Fragment 作为一种轻量级的UI 组件,能够帮助我们更高效地管理和复用用户界面。然而,关于如何传递参数给自定义 Fragment,官方推荐使用 Fragment.setArguments(Bundle bundle) 而不是直接在构造方法中传递参数,这背后有着深刻的原因。以下是详细的分析和解释:
Fragment 的生命周期与 Activity 的生命周期紧密相关。每当 Activity 的配置发生变化(如屏幕旋转或切换 Activity),FragmentManager 会将当前显示的 Fragment 的状态进行保存,并在必要时重新恢复这些状态。这种状态保存机制是 Fragment 在 Android 中的核心特性之一。
通过构造方法传递参数的局限性
如果使用构造方法直接传递参数,例如:
public TestFragment(String arg) { // 初始化逻辑} 并在 Activity 中创建 Fragment 时:
fragment = new TestFragment("参数"); 这种方法存在以下问题:
Fragment.setArguments(Bundle bundle) 的优势
官方推荐的 Fragment.setArguments(Bundle bundle) 方法提供了更优雅且更可靠的参数传递机制。通过创建一个 Bundle 对象,包含所需的参数,并将其传递给 Fragment,可以确保参数在 Fragment 的生命周期中得到正确保存和恢复。
构造方法传递参数的示例
public static class TestFragment extends Fragment { private String mArg = "non-param"; public TestFragment() { Log.i("INFO", "TestFragment non-parameter constructor"); } public TestFragment(String arg) { mArg = arg; Log.i("INFO", "TestFragment construct with parameter"); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); TextView tv = (TextView) rootView.findViewById(R.id.tv); tv.setText(mArg); return rootView; }} public class FramentTestActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new TestFragment("param")).commit(); } }} 使用 Fragment.setArguments(Bundle bundle) 的示例
public static class TestFragment extends Fragment { private static final String ARG = "arg"; private String mArg; public TestFragment() { Log.i("INFO", "TestFragment non-parameter constructor"); } public static Fragment newInstance(String arg) { TestFragment fragment = new TestFragment(); Bundle bundle = new Bundle(); bundle.putString(ARG, arg); fragment.setArguments(bundle); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); TextView tv = (TextView) rootView.findViewById(R.id.tv); tv.setText(getArguments().getString(ARG)); return rootView; }} public class FramentTest2Activity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, TestFragment.newInstance("param")).commit(); } }} 构造方法传递参数的表现
当设备发生横竖屏切换时,Activity 会重新创建,并恢复之前的 Fragment 状态。由于参数是通过构造方法传递的,Fragment 的状态不会自动包含参数,导致参数丢失,UI 将显示默认值。
Fragment.setArguments(Bundle bundle) 的表现
由于参数通过 Fragment 的状态机制保存,横竖屏切换后,Fragment 会自动从 Bundle 中读取参数,确保 UI 正确显示传递的参数。
Activity 的 onCreate 方法
Activity 的 onCreate 方法负责初始化组件,并恢复 Fragment 的状态。FragmentManager 会处理 Fragment 的保存和恢复过程。
@Overrideprotected void onCreate(Bundle savedInstanceState) { if (DEBUG_LIFECYCLE) Log.v(TAG, "onCreate " + this + ": " + savedInstanceState); // 处理 Fragment 的恢复 if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.fragments : null); } mFragments.dispatchCreate(); getApplication().dispatchActivityCreated(this, savedInstanceState); mCalled = true;} FragmentManager 的 restoreAllState 方法
restoreAllState 方法负责从 Bundle 中恢复 Fragment 的状态,确保参数和 Fragment 的配置被正确重建。
for (int i = 0; i < fms.mactive.length; i++) { fragmentstate fs = fms.mActive[i]; if (fs != "null") { fragment f = fs.instantiate(mActivity, mParent); Log.v(TAG, "restoreAllState: active #" + i + ": " + f); mactive.add(f); fs.minstance = "null"; // 清除可能重复恢复的状态 } else { mactive.add(null); } mavailindices.add(i);} FragmentState 的 instantiate 方法
instantiate 方法负责根据 Fragment 的配置重新创建 Fragment 实例,并恢复其状态。
public Fragment instantiate(Activity activity, Fragment parent) { if (mInstance != null) { return mInstance; } if (mArguments != null) { mArguments.setClassLoader(activity.getClassLoader()); } mInstance = Fragment.instantiate(activity, mClassName, mArguments); if (mSavedFragmentState != null) { mSavedFragmentState.setClassLoader(activity.getClassLoader()); mInstance.mSavedFragmentState = mSavedFragmentState; } mInstance.setIndex(mIndex, parent); mInstance.mFromLayout = mFromLayout; mInstance.mRestored = true; mInstance.mFragmentId = mFragmentId; mInstance.mContainerId = mContainerId; mInstance.mTag = mTag; mInstance.mRetainInstance = mRetainInstance; mInstance.mDetached = mDetached; mInstance.mFragmentManager = activity.mFragments; if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, "Instantiated fragment " + mInstance); return mInstance;} 通过对比两种参数传递方式,可以看出 Fragment.setArguments(Bundle bundle) 的优势在于其与 Fragment 的状态保存机制完美结合,确保参数在设备配置变化时能够正确保存和恢复。而通过构造方法传递参数则不具备这种机制,容易导致参数丢失,影响 UI 的正确显示。因此,官方推荐的 Fragment.setArguments(Bundle bundle) 方法是更优的选择,能够更好地处理 Fragment 的状态和参数传递,确保应用在横竖屏切换等场景下的稳定性和一致性。
转载地址:http://jtsr.baihongyu.com/