zoukankan      html  css  js  c++  java
  • Android开发之Fragment传递參数的几种方法

    Fragment在Android3.0開始提供,而且在兼容包中也提供了Fragment特性的支持。

    Fragment的推出让我们编写和管理用户界面更快捷更方便了。


    但当我们实例化自己定义Fragment时,为什么官方推荐Fragment.setArguments(Bundle bundle)这样的方式来传递參数,而不推荐通过构造方法直接来传递參数呢?为了弄清这个问题,我们能够做一个測试。分别測试下这两种方式的不同

    首先。我们来測试下通过构造方法传递參数的情况

    1. public class FramentTestActivity extends ActionBarActivity {  
    2.       
    3.     @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         super.onCreate(savedInstanceState);  
    6.         setContentView(R.layout.activity_main);  
    7.   
    8.         if (savedInstanceState == null) {  
    9.             getSupportFragmentManager().beginTransaction()  
    10.                     .add(R.id.container, new TestFragment("param")).commit();  
    11.         }  
    12.           
    13.     }  
    14.   
    15.     public static class TestFragment extends Fragment {  
    16.   
    17.         private String mArg = "non-param";  
    18.           
    19.         public TestFragment() {  
    20.             Log.i("INFO""TestFragment non-parameter constructor");  
    21.         }  
    22.           
    23.         public TestFragment(String arg){  
    24.             mArg = arg;  
    25.             Log.i("INFO""TestFragment construct with parameter");  
    26.         }  
    27.   
    28.         @Override  
    29.         public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    30.                 Bundle savedInstanceState) {  
    31.             View rootView = inflater.inflate(R.layout.fragment_main, container,  
    32.                     false);  
    33.             TextView tv = (TextView) rootView.findViewById(R.id.tv);  
    34.             tv.setText(mArg);  
    35.             return rootView;  
    36.         }  
    37.     }  
    38.   
    39. }  

    能够看到我们传递过来的数据正确的显示了。如今来考虑一个问题。假设设备配置參数发生变化,这里以横竖屏切换来说明问题,显演示样例如以下



    发生了什么问题呢?我们传递的參数哪去了?为什么会显示默认值?不急着讨论这个问题。接下来我们来看看Fragment.setArguments(Bundle bundle)这样的方式的执行情况

    1. public class FramentTest2Activity extends ActionBarActivity {  
    2.          
    3.         @Override  
    4.         protected void onCreate(Bundle savedInstanceState) {  
    5.               super.onCreate(savedInstanceState);  
    6.              setContentView(R.layout. activity_main);  
    7.   
    8.               if (savedInstanceState == null) {  
    9.                     getSupportFragmentManager().beginTransaction()  
    10.                                  .add(R.id. container, TestFragment.newInstance("param")).commit();  
    11.              }  
    12.   
    13.        }  
    14.   
    15.         public static class TestFragment extends Fragment {  
    16.   
    17.               private static final String ARG = "arg";  
    18.                
    19.               public TestFragment() {  
    20.                     Log. i("INFO""TestFragment non-parameter constructor" );  
    21.              }  
    22.   
    23.               public static Fragment newInstance(String arg){  
    24.                     TestFragment fragment = new TestFragment();  
    25.                     Bundle bundle = new Bundle();  
    26.                     bundle.putString( ARG, arg);  
    27.                     fragment.setArguments(bundle);  
    28.                      return fragment;  
    29.              }  
    30.                
    31.               @Override  
    32.               public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    33.                            Bundle savedInstanceState) {  
    34.                     View rootView = inflater.inflate(R.layout. fragment_main, container,  
    35.                                   false);  
    36.                     TextView tv = (TextView) rootView.findViewById(R.id. tv);  
    37.                     tv.setText(getArguments().getString( ARG));  
    38.                      return rootView;  
    39.              }  
    40.        }  
    41.   
    42. }  


    我们再来看看横竖屏切换后的执行情况



    看到了吧,我们传递的參数在横竖屏切换的情况下完善保存了下来,正确的显示给用户
    那么这究竟是怎么回事呢,我们知道设备横竖屏切换的话,当前展示给用户的Activity默认情况下会又一次创建并展现给用户,那依附于Activity的Fragment会进行怎样处理呢,我们能够通过源代码来查看
    先来看看Activity的onCreate(Bundle saveInstance)方法

    1.  protected void onCreate(Bundle savedInstanceState) {  
    2.     if (DEBUG_LIFECYCLE ) Slog.v( TAG, "onCreate " + this + ": " + savedInstanceState);  
    3.     if (mLastNonConfigurationInstances != null) {  
    4.         mAllLoaderManagers = mLastNonConfigurationInstances .loaders ;  
    5.     }  
    6.     if (mActivityInfo .parentActivityName != null) {  
    7.         if (mActionBar == null) {  
    8.             mEnableDefaultActionBarUp = true ;  
    9.         } else {  
    10.             mActionBar .setDefaultDisplayHomeAsUpEnabled( true);  
    11.         }  
    12.     }  
    13.     if (savedInstanceState != null) {  
    14.         Parcelable p = savedInstanceState.getParcelable( FRAGMENTS_TAG );  
    15.         mFragments .restoreAllState(p, mLastNonConfigurationInstances != null  
    16.                 ? mLastNonConfigurationInstances .fragments : null);  
    17.     }  
    18.     mFragments .dispatchCreate();  
    19.     getApplication().dispatchActivityCreated( this , savedInstanceState);  
    20.     mCalled = true ;  
    21. }  

    因为我们的Fragment是由FragmentManager来管理,所以能够跟进FragmentManager.restoreAllState()方法。通过对当前活动的Fragmnet找到以下的代码块

    1.   for (int i=0; i<fms.mActive.length; i++) {  
    2.            FragmentState fs = fms.mActive[i];  
    3.            if (fs != null) {  
    4.               Fragment f = fs.instantiate(mActivity, mParent);  
    5.                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);  
    6.                mActive.add(f);  
    7.                // Now that the fragment is instantiated (or came from being  
    8.                // retained above), clear mInstance in case we end up re-restoring  
    9.                 // from this FragmentState again.  
    10.                 fs.mInstance = null;  
    11.            } else {  
    12.                mActive.add(null);  
    13.                 if (mAvailIndices == null) {  
    14.                     mAvailIndices = new ArrayList<Integer>();  
    15.                }  
    16.                if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);  
    17.                mAvailIndices.add(i);  
    18.            }  
    19. }  

    接下来我们能够看看FragmentState.instantitate()方法的实现

    1. public Fragment instantiate(Activity activity, Fragment parent) {  
    2.         if (mInstance != null) {  
    3.             return mInstance ;  
    4.         }  
    5.          
    6.         if (mArguments != null) {  
    7.             mArguments .setClassLoader(activity.getClassLoader());  
    8.         }  
    9.          
    10.         mInstance = Fragment.instantiate(activity, mClassName , mArguments );  
    11.          
    12.         if (mSavedFragmentState != null) {  
    13.             mSavedFragmentState .setClassLoader(activity.getClassLoader());  
    14.             mInstance .mSavedFragmentState = mSavedFragmentState ;  
    15.         }  
    16.         mInstance .setIndex(mIndex , parent);  
    17.         mInstance .mFromLayout = mFromLayout ;  
    18.         mInstance .mRestored = true;  
    19.         mInstance .mFragmentId = mFragmentId ;  
    20.         mInstance .mContainerId = mContainerId ;  
    21.         mInstance .mTag = mTag ;  
    22.         mInstance .mRetainInstance = mRetainInstance ;  
    23.         mInstance .mDetached = mDetached ;  
    24.         mInstance .mFragmentManager = activity.mFragments;  
    25.         if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,  
    26.                 "Instantiated fragment " + mInstance );  
    27.   
    28.         return mInstance ;  
    29.     }  

    能够看到终于转入到Fragment.instantitate()方法

    1. public static Fragment instantiate(Context context, String fname, Bundle args) {  
    2.    try {  
    3.        Class<?

      > clazz = sClassMap .get(fname);  

    4.        if (clazz == null) {  
    5.            // Class not found in the cache, see if it's real, and try to add it  
    6.            clazz = context.getClassLoader().loadClass(fname);  
    7.            sClassMap .put(fname, clazz);  
    8.        }  
    9.        Fragment f = (Fragment)clazz.newInstance();  
    10.        if (args != null) {  
    11.            args.setClassLoader(f.getClass().getClassLoader());  
    12.            f. mArguments = args;  
    13.        }  
    14.        return f;  
    15.    } catch (ClassNotFoundException e) {  
    16.        throw new InstantiationException( "Unable to instantiate fragment " + fname  
    17.                + ": make sure class name exists, is public, and has an"  
    18.                + " empty constructor that is public" , e);  
    19.    } catch (java.lang.InstantiationException e) {  
    20.        throw new InstantiationException( "Unable to instantiate fragment " + fname  
    21.                + ": make sure class name exists, is public, and has an"  
    22.                + " empty constructor that is public" , e);  
    23.    } catch (IllegalAccessException e) {  
    24.        throw new InstantiationException( "Unable to instantiate fragment " + fname  
    25.                + ": make sure class name exists, is public, and has an"  
    26.                + " empty constructor that is public" , e);  
    27.    }  

    通过此方法能够看到。终于会通过反射无參构造实例化一个新的Fragment,而且给mArgments初始化为原先的值,而原来的Fragment实例的数据都丢失了。并又一次进行了初始化

    通过上面的分析,我们能够知道Activity又一次创建时,会又一次构建它所管理的Fragment,原先的Fragment的字段值将会所有丢失,可是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递參数
  • 相关阅读:
    net.sf.json.JSONException: There is a cycle in the hierarchy!
    数据源的配置
    java枚举使用详解
    hibernate级联保存,更新个人遇到的问题
    NonUniqueObjectException 问题
    No.2 dotnetcore&docker--数据库等在Docker上的配置
    No.1 dotnetcore&docker--环境搭建
    No.5 dotnetcore&docker--采用Ambry做文件服务器
    No.3 dotnetcore&docker--搭建一个nuget服务器
    关于APM数据采集实例以及Eureka整合的一个想法
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/6744444.html
Copyright © 2011-2022 走看看