zoukankan      html  css  js  c++  java
  • 如何向一个Fragment传递参数---setArguments方法的介绍*

    在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,如果我问你怎么向一个Fragment传递参数,你是不是会首先想到通过构造方法,当面试被问到这个问题的时候我也是这么想的,后来发现自己错了,现在给大家讲一下究竟该怎么做。

    首先我们看构造方法这种方式为什么不行,根据Android文档说明,当一个fragment重新创建的时候,系统会再次调用 Fragment中的默认构造函数。 注意这里:是 默认构造函数。 
    这句话更直白的意思就是:当你小心翼翼的创建了一个带有重要参数的Fragment的之后,一旦由于什么原因(横竖屏切换)导致你的Fragment重新创建。——-很遗憾的告诉你,你之前传递的参数都不见了,因为recreate你的Fragment的时候,调用的是默认构造函数。

    首先我们通过构造函数来传递参数,代码如下

    public class MainActivity extends FragmentActivity {
    
        private FragmentManager manager;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            manager = getSupportFragmentManager();
            /*
             * 这里为什么要进行空判断,因为在屏幕旋转的时候,系统会执行onSaveInstanceState
             * 方法去保存当前activity的状态,然后activity会重建,执行onCreate方法,如果我们不判断
             * savedInstanceState是否为空,那么每次就会执行下面的commit操作,向Fragmnet传递参数,
             * 这样参数的却会保留下来,但是我们不应该每次都去传递参数。当进行了空判断时,当Activity重建
             * 的时候,会调用Fragment的默认构造函数,所以我们传递过去的参数不能保留了。
             */
            if(savedInstanceState == null){
                manager.beginTransaction().replace(R.id.fl_main, new FragmentOne("params")).commit();
            }
        }
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            System.out.println("=========savedInstanceState ");
            super.onSaveInstanceState(outState);
        }
    }
    public class FragmentOne extends Fragment {
    
        private TextView textView;
        private String params = "default";
        public FragmentOne(){
            System.out.println("===========default constructor");
        }
        /**
         * 通过构造方法接收传递过来的参数
         * @param content
         */
        public FragmentOne(String content){
            params = content;
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment1, container,false);
            textView = (TextView) view.findViewById(R.id.textview);
            textView.setText(params);
            return view;
        }
    }

    这里写图片描述

    可以看到此时传递过来的参数已经不见了,说明通过构造方法传递参数是不行的。

    我们看看控制台的打印

    这里写图片描述

    注意: 
    这里我们必须写出默认的构造函数,因为Fragment重建的时候,会调用默认的构造函数,也就是空参数的构造函数,如果我们只是给出了带参数的构造函数,系统是不会为我们创建空参数的构造函数的,如果你不写,在Fragment重建的时候就会发生下面的错误。

    这里写图片描述

    接下来看看官方推荐的setArguments方法:

    public class MainActivity extends FragmentActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (savedInstanceState == null) {
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.fl_main, FragmentOne.newInstance("params"))
                        .commit();
            }
        }
    }
    public class FragmentOne extends Fragment{
        private TextView textView;
        public View onCreateView(LayoutInflater inflater,
                @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_one, null);
            textView = (TextView) view.findViewById(R.id.textview);
            if(getArguments()!=null){
                //取出保存的值
                textView.setText(getArguments().getString("name"));
            }
            return view;
        }
        public static  FragmentOne newInstance(String text){
            FragmentOne fragmentOne = new FragmentOne();
            Bundle bundle = new Bundle();
            bundle.putString("name", text);
            //fragment保存参数,传入一个Bundle对象
            fragmentOne.setArguments(bundle);
            return fragmentOne;
        }
    }

    这里写图片描述

    可以看到,屏幕旋转以后参数也保留下来了。

    接下来我们通过源码看看Bundle这个参数到底如何保留下来的, 
    点进去Fragment的setArguments方法:

      public void setArguments(Bundle args) {
            if (mIndex >= 0) {
                throw new IllegalStateException("Fragment already active");
            }
            mArguments = args;
        }

    首先将当前的bundle对象赋值给一个全局的mArguments对象,mArguments时FragmentState对象的一个属性,FragmentState时Fragment的一个内部类,代表着Fragment的状态。

    final class FragmentState implements Parcelable {
        final String mClassName;
        final int mIndex;
        final boolean mFromLayout;
        final int mFragmentId;
        final int mContainerId;
        final String mTag;
        final boolean mRetainInstance;
        final boolean mDetached;
        final Bundle mArguments;
        final boolean mHidden;
    
        Bundle mSavedFragmentState;
    
        Fragment mInstance;
    
        public FragmentState(Fragment frag) {
            mClassName = frag.getClass().getName();
            mIndex = frag.mIndex;
            mFromLayout = frag.mFromLayout;
            mFragmentId = frag.mFragmentId;
            mContainerId = frag.mContainerId;
            mTag = frag.mTag;
            mRetainInstance = frag.mRetainInstance;
            mDetached = frag.mDetached;
            mArguments = frag.mArguments;
            mHidden = frag.mHidden;
        }

    然后Activity保存状态的时候会调用onSaveInstanceState方法

    protected void onSaveInstanceState(Bundle outState) {
            outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    
            outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
            // 调用saveAllState方法保存Fragment状态
            Parcelable p = mFragments.saveAllState();
            if (p != null) {
                // 将结果保存到Bundle中
                outState.putParcelable(FRAGMENTS_TAG, p);
            }
            if (mAutoFillResetNeeded) {
                outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
                getAutofillManager().onSaveInstanceState(outState);
            }
            getApplication().dispatchActivitySaveInstanceState(this, outState);
        }

    saveAllState方法最终调用到FragmentManager的saveAllState方法中

    Parcelable saveAllState() {     
            // 找到所有的存活的Fragment
            int N = mActive.size();
            // 代表Fragment状态的数组
            FragmentState[] active = new FragmentState[N];
            boolean haveFragments = false;
            for (int i=0; i<N; i++) {
                Fragment f = mActive.valueAt(i);
                if (f != null) {
                    if (f.mIndex < 0) {
                        throwException(new IllegalStateException(
                                "Failure saving state: active " + f
                                + " has cleared index: " + f.mIndex));
                    }
    
                    haveFragments = true;
                    // 找到所有的Fragment,为FragmentState数组初始化
                    FragmentState fs = new FragmentState(f);
                    active[i] = fs;
                    // 保证Fragment已经创建了并且没有参数保存过
                    if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                    // 保存Fragment的状态
                        fs.mSavedFragmentState = saveFragmentBasicState(f);
            FragmentManagerState fms = new FragmentManagerState();
            // active是上面代表Fragment状态的数组,至此,fragment的状态就被保存到
            了FragmentManagerState中
            fms.mActive = active;
            fms.mAdded = added;
            fms.mBackStack = backStack;
            fms.mNextFragmentIndex = mNextFragmentIndex;
            if (mPrimaryNav != null) {
                fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
            }
            saveNonConfig();
            return fms;
        }

    接下来我们看看恢复数据的流程

    在Activity的onCreate中有下面的代码

    // 取出之前保存的数据
     Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
                mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                        ? mLastNonConfigurationInstances.fragments : null);

    最终调用到了FragmentManager的restoreAllState方法

    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
            if (state == null) return;
               // 取出保存的数据
            FragmentManagerState fms = (FragmentManagerState)state;
            if (fms.mActive == null) return;
    
           // ... 省去部分代码
            // Build the full list of active fragments, instantiating them from
            // their saved state.
            // 根据之前保存的状态初始化新的Fragment
            mActive = new SparseArray<>(fms.mActive.length);
            for (int i=0; i<fms.mActive.length; i++) {
                FragmentState fs = fms.mActive[i];
                if (fs != null) {
                    FragmentManagerNonConfig childNonConfig = null;
                    if (childNonConfigs != null && i < childNonConfigs.size()) {
                        childNonConfig = childNonConfigs.get(i);
                    }
                    // 调用instantiate方法创建Fragment
                    Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
                    if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                    mActive.put(f.mIndex, f);
                    // Now that the fragment is instantiated (or came from being
                    // retained above), clear mInstance in case we end up re-restoring
                    // from this FragmentState again.
                    fs.mInstance = null;
                }
            }
    public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
                Fragment parent, FragmentManagerNonConfig childNonConfig) {
            if (mInstance == null) {
                final Context context = host.getContext();
                if (mArguments != null) {
                    mArguments.setClassLoader(context.getClassLoader());
                }
                // 初始化Fragment,把之前保存的参数传过去
                if (container != null) {
                    mInstance = container.instantiate(context, mClassName, mArguments);
                } else {
                    mInstance = Fragment.instantiate(context, mClassName, mArguments);
                }
    
                if (mSavedFragmentState != null) {
                    mSavedFragmentState.setClassLoader(context.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.mHidden = mHidden;
                mInstance.mFragmentManager = host.mFragmentManager;
                if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                        "Instantiated fragment " + mInstance);
            }
            mInstance.mChildNonConfig = childNonConfig;
            return mInstance;
        }

    // 初始化的方法

    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);
                    if (!Fragment.class.isAssignableFrom(clazz)) {
                        throw new InstantiationException("Trying to instantiate a class " + fname
                                + " that is not a Fragment", new ClassCastException());
                    }
                    sClassMap.put(fname, clazz);
                }
                // 调用Fragment无参数的构造函数
                Fragment f = (Fragment) clazz.getConstructor().newInstance();
                if (args != null) {
                    args.setClassLoader(f.getClass().getClassLoader());
                    // 设置参数,然后我们就可以用getArgument方法获取了
                    f.setArguments(args);
                }
                return f;

    注意: 
    setArguments方法的调用必须要在Fragment与Activity关联之前。 
    这句话可以这样理解,setArgument方法的使用必须要在FragmentTransaction 的commit之前使用。

  • 相关阅读:
    2019-10-28-开源项目
    2018-8-10-win10-uwp-MetroLog-入门
    2018-5-20-C#-BBcode-转-Markdown
    2018-8-10-win10-UWP-序列化
    2018-2-13-win10-uwp-BadgeLogo-颜色
    2019-1-25-WPF-ListBox-的选择
    2019-1-5-Windows-的-Pen-协议
    android studio打印
    Java 基本数据类型
    FreeRTOS 任务通知模拟计数型信号量
  • 原文地址:https://www.cnblogs.com/chenxibobo/p/9590656.html
Copyright © 2011-2022 走看看