android 之Fragment相关
-
- Fragment的使用
- fragment基本信息
- fragment在activity中的调用步骤
- fragment切换replaceadd
- FragmentManager的功能
- fragment和activity通信
- fragment的生命周期
- 四大状态
- 生命周期主要回调函数介绍
- 生命周期完整图
- 活动和碎片生命周期对比
- 使用fragment argument
- 附加extra信息
- 获取extra信息
- 直接获取extra信息方式的缺点
- fragment argument
- 附加argument给fragment
- 获取 argument
- 通过fragment获取返回结果
- fragment的保留
- 保留fragment实例
- 设备旋转与保留的fragment
- 注意
- 设备旋转处理与 onSaveInstanceStateBundle 方法
- 动态加载布局技巧之后转移到专门的布局文
- 使用限定符Qualifiers
- 使用最小宽度限定符Smallest-width Qualifier
- Fragment的使用
Fragment的使用
public class LeftFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view = inflater.inflate(R.layout.left_fragment, container, false);return view;}}
fragment基本信息
参考链接:http://blog.csdn.net/xyz_lmn/article/details/6927763
一、 FragmentManager可以做如下一些事情:
1、使用findFragmentById() (用于在activity layout中提供一个UI的fragment)或findFragmentByTag()
(适用于有或没有UI的fragment)获取activity中存在的fragment
2、将fragment从后台堆栈中弹出, 使用 popBackStack() (模拟用户按下BACK 命令).
3、使用addOnBackStackChangeListener()注册一个监听后台堆栈变化的listener.
添加变化到 FragmentTransaction的顺序不重要, 除以下例外:
(1)必须最后调用 commit().
(2)如果添加多个fragment到同一个容器, 那么添加的顺序决定了它们在view hierarchy中显示的顺序.当执行一个移除fragment的事务时, 如果没有调用 addToBackStack(), 那么当事务提交后, 那个fragment会被销毁,并且用户不能导航回到它. 有鉴于此, 当移除一个fragment时,如果调用了 addToBackStack(), 那么fragment会被停止, 如果用户导航回来,它将会被恢复.提示: 对于每一个fragment事务, 你可以应用一个事务动画, 通过在提交事务之前调用setTransition()实现.
调用 commit() 并不立即执行事务.恰恰相反, 它将事务安排排期, 一旦准备好, 就在activity的UI线程上运行(主线程).如果有必要, 无论如何, 你可以从你的UI线程调用 executePendingTransactions() 来立即执行由commit()提交的事务. 但这么做通常不必要, 除非事务是其他线程中的job的一个从属.
警告: 你只能在activity保存它的状态(当用户离开activity)之前使用commit()提交事务.
如果你试图在那个点之后提交, 会抛出一个异常.这是因为如果activity需要被恢复, 提交之后的状态可能会丢失.对于你觉得可以丢失提交的状况, 使用 commitAllowingStateLoss().
fragment在activity中的调用步骤
- 获取FragmentManager。
- 获取事物FragmentTransaction,
- transaction对fragment进行操作,如replace(),add(),remove()等。
- 提交事物。transaction.commit()
FragmentManager fragmentManager = getFragmentManager();FragmentTransaction transaction = fragmentManager.beginTransaction();transaction.replace(R.id.right_layout, fragment);transaction.commit();
fragment切换replace\add
一个container上面的多个fragment切换,如果使用replace,每次切换的时候,Fragment都会重新实例化,重新加载一边数据,这样非常消耗性能和用户的数据流量。
正确的切换方式是add(),切换时hide(),add()另一个Fragment;再次切换时,只需hide()当前,show()另一个。这样就能做到多个Fragment切换不重新实例化
public void switchFragment(Fragment from, Fragment to) { if (from == null || to == null) return; FragmentTransaction transaction = getSupportFragmentManager() .beginTransaction().setCustomAnimations(R.anim.tran_pre_in, R.anim.tran_pre_out); if (!to.isAdded()) { // 隐藏当前的fragment,add下一个到Activity中 transaction.hide(from).add(R.id.fl_main, to).commit(); } else { // 隐藏当前的fragment,显示下一个 transaction.hide(from).show(to).commit(); } }
优缺点分析
在大部分情况下,这两个的表现基本相同。一般,会使用一个FrameLayout来当容器,而每个Fragment被add 或者 replace 到这个FrameLayout的时候,都是显示在最上层的。所以你看到的界面都是一样的。但是,使用add的情况下,这个FrameLayout其实有2层,多层肯定要比一层的来得浪费,所以还是推荐使用replace。当然有时候还是需要使用add的。比如要实现轮播图的效果(切换),每个轮播图都是一个独立的Fragment,而他的容器FrameLayout需要add多个Fragment,这样他就可以根据提供的逻辑进行轮播了。
FragmentManager的功能
- 使用findFragmentById()或findFragmentByTag()方法来获取指定的Fragment。
- 调用popBackStack()方法将Fragment从后台栈中弹出(模拟用户按下BACK键)
- 调用addOnBackStackChangeListener()注册一个监听器,用于监听后台栈的变化。
- 如果需要添加、删除、替换Fragment,则需要借助与FragmentTransaction对象。FragmentTransaction代表Activity对Fragment执行的多个改变。
fragment和activity通信
- fragment在activity的布局之中
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);调用 FragmentManager 的 findFragmentById()方法,可以在活动中得到相应碎片的实例,然后就能轻松地调用碎片里的方法了。
- fragment通过getActivity()方法来得到和当前fragment相关联的活动实例
MainActivity activity = (MainActivity) getActivity();有了活动实例之后,在碎片中调用活动里的方法就变得轻而易举了。另外当碎片中需要使用 Context对象时,也可以使用 getActivity()方法,因为获取到的活动本身就是一个 Context对象了。
- 两个fragment交互
一个碎片中获取相关联的活动,再通过这个活动去获取另一个碎片实例。
- fragment通过回调和activity交互
直接拿疯狂android讲义的例子,一看就明白
public class BookListFragment extends ListFragment{ private Callbacks mCallbacks; // 定义一个回调接口,该Fragment所在Activity需要实现该接口 // 该Fragment将通过该接口与它所在的Activity交互 public interface Callbacks { public void onItemSelected(Integer id); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 为该ListFragment设置Adapter setListAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, BookContent.ITEMS)); } // 当该Fragment被添加、显示到Activity时,回调该方法 @Override public void onAttach(Activity activity) { super.onAttach(activity); // 如果Activity没有实现Callbacks接口,抛出异常 if (!(activity instanceof Callbacks)) { throw new IllegalStateException( "BookListFragment所在的Activity必须实现Callbacks接口!"); } // 把该Activity当成Callbacks对象 mCallbacks = (Callbacks)activity; } // 当该Fragment从它所属的Activity中被删除时回调该方法 @Override public void onDetach() { super.onDetach(); // 将mCallbacks赋为null。 mCallbacks = null; } // 当用户点击某列表项时激发该回调方法 @Override public void onListItemClick(ListView listView , View view, int position, long id) { super.onListItemClick(listView, view, position, id); // 激发mCallbacks的onItemSelected方法 mCallbacks.onItemSelected(BookContent .ITEMS.get(position).id); } public void setActivateOnItemClick(boolean activateOnItemClick) { getListView().setChoiceMode( activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE); }}
在activity中,在onItemSelected会创建一个新额fragment并且赋值他的arguments,这在接下来会讲到。
public class SelectBookActivity extends Activity implements BookListFragment.Callbacks{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 加载/res/layout目录下的activity_book_twopane.xml布局文件 setContentView(R.layout.activity_book_twopane); } // 实现Callbacks接口必须实现的方法 @Override public void onItemSelected(Integer id) { // 创建Bundle,准备向Fragment传入参数 Bundle arguments = new Bundle(); arguments.putInt(BookDetailFragment.ITEM_ID, id); // 创建BookDetailFragment对象 BookDetailFragment fragment = new BookDetailFragment(); // 向Fragment传入参数 fragment.setArguments(arguments); // 使用fragment替换book_detail_container容器当前显示的Fragment getFragmentManager().beginTransaction() .replace(R.id.book_detail_container, fragment) .commit(); //① }}
获取到自己的arguments
public class BookDetailFragment extends Fragment{ public static final String ITEM_ID = "item_id"; // 保存该Fragment显示的Book对象 BookContent.Book book; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 如果启动该Fragment时包含了ITEM_ID参数 if (getArguments().containsKey(ITEM_ID)) { book = BookContent.ITEM_MAP.get(getArguments() .getInt(ITEM_ID)); //① } }}
fragment的生命周期
四大状态
1、运行状态
fragment课件,并且关联的activity处于运动状态。
2、暂停状态
活动进入暂停,与其关联的可见fragment就进入到暂停状态。
3、停止状态
活动进入停止状态,与其关联的fragment也进入停止状态
或者:通过调用FragmentTransaction的 remove()、replace()方法将碎片从活动中移除,但有在事务提
交之前调用 addToBackStack()方法,这时的碎片也会进入到停止状态。总的来说,进入
停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收
4、销毁状态
碎片总是依附于活动而存在的,因此当活动被销毁时,与它相关联的碎片就会进入
到销毁状态。或者通过调用 FragmentTransaction 的 remove()、replace()方法将碎片从活
动中移除,但在事务提交之前并没有调用 addToBackStack()方法,这时的碎片也会进入
到销毁状态。
生命周期主要回调函数介绍
生命周期完整图
活动和碎片生命周期对比
在碎片中你也是可以通过 onSaveInstanceState()方法来保存数据的,
因为进入停止状态的碎片有可能在系统内存不足的时候被回收。保存下来的数据在
onCreate()、onCreateView()和 onActivityCreated()这三个方法中你都可以重新得到,它们都含
有一个 Bundle 类型的 savedInstanceState 参数。
使用fragment argument
附加extra信息
fragment 中启动Activity并且携带extra信息。
Intent i = new Intent(getActivity(), XXXActivity.class); i.putExtra(key, value); startActivity(i);
获取extra信息
fragment启动了一个activity,这个activity中的fragment获取其所携带的extra信息。
getActivity().getIntent().getSerializableExtra(key);//注意类型转换
直接获取extra信息方式的缺点
只需几行简单的代码,就可以实现让fragment直接获取托管activity的intent。但是这种方式是以牺牲fragment的封装性为代价的。这个fragment不再是可复用的构建单元,总需要由某个具体的activity托管着。
fragment argument
每个fragment实例都可以附带一个Bundle对象,该Bundle包含有key-value对。要创建fragment argument,首先需创建 Bundle 对象。然后,使用 Bundle 限定类型的“put”
方法(类似于 Intent 的方法),将argument添加到bundle中(如以下代码所示)。
Bundle args = new Bundle();args.putSerializable(key, Object);args.putInt(key, myInt);args.putCharSequence(MY_STRING, myString);
附加argument给fragment
Fragment.setArguments(Bundle)
调用上面的方法将argument bundle 传递给fragment。注意,该任务必须在fragment创建后、添加给activity前完成。
为满足以上苛刻的要求,Android开发者遵循的习惯做法是:
(1)添加名为 newInstance() 的静态方法给 Fragment 类。使用该方法,完成fragment实例及bundle对象的创建,然后将argument放入bundle中,最后再附加给fragment。
(2)托管activity需要fragment实例时,需调用 newInstance() 方法,而非直接调用其构造方法。而且,为满足fragment创建argument的要求,activity可传入任何需要的参数给 newInstance() 方法。
public static XXFragment newInstance((视情况定参数)){ Bundle args = new Bundle(); args.putSerializable(key, value); XXFragment fragment = new XXFragment(); fragment.setArguments(args) return fragment;
交互的activity和fragment不需要也无法同时保持通用独立性。 XXActivity 必须了解XXFragment 的内部细节,比如知晓它内部有一个 newInstance(UUID) 方法 。这很正常。托管activity就应该知道有关托管fragment方法的细节,但fragment则不必知道其托管activity的细节问题。至少在需要保持fragment通用独立性的时候是如此。
获取 argument
Fragment的getArguments()方法,接着调用Bundle的限定类型的“get”方法
通过fragment获取返回结果
调用 Fragment.startActivityForResult(…) 方法,而非Activity的startActivityForResult(…) 方法;选择覆盖 Fragment.onActivityResult(…) 方法,而非 Activity.onActivityResult(…) 方法。
除将返回结果从托管activity传递给fragment的额外实现代码之外, Fragment.startActivity-ForResult(Intent,int) 方法的实现代码与 Activity 的同名方法基本相同。从fragment中返回结果的处理稍有不同。fragment能够从activity中接收返回结果,但其自身无法产生返回结果。只有activity拥有返回结果。因此,尽管 Fragment 有自己的startActivityForResult(…)和 onActivityResult(…) 方法,但却不具有任何 setResult(…) 方法。
fragment的保留
保留fragment实例
在fragment的onCreat()方法中
fragment的 retainInstance 属性值默认为false。这表明其不会被保留。因此,设备旋转时
fragment会随托管activity一起销毁并重建。调用 setRetainInstance(true) 方法可保留fragment。已保留的fragment不会随activity一起被销毁。相反,它会被一直保留并在需要时原封不动的传递给新的activity。
对于已保留的fragment实例,其全部实例变量(如 mPlayButton 、 MPlayer 和 mStopButton )值也将保持不变,因此可放心继续使用
设备旋转与保留的fragment
保留的fragment的工作原理:可销毁和重建fragment的视图,但无需销毁fragment自身。设备配置发生改变时, FragmentManager 首先销毁队列中的fragment的视图。在设备配置改变时,总是销毁与重建fragment与activity的视图,都是基于同样的理由:新的配置可能需要新的资源来匹配;当有更合适的匹配资源可以利用时,则需重新创建视图。紧接着, FragmentManager 检查每个fragment的 retainInstance 属性值。
如属性值为false(初始默认值), FragmentManager 会立即销毁该fragment实例。随后,为适应新的设备配置,新activity的新 FragmentManager 会创建一个新的fragment及其视图。
属性值为true时。则该fragment的视图立即被销毁,但fragment本身不会被销毁。为适应新的设备配置,当新的activity创建后,新的 FragmentManager 会找到被保留的fragment,并重新创建它的视图,
(1) fragment必须同时满足两个条件才能进入保留状态:
已调用了fragment的 setRetainInstance(true) 方法
因设备配置改变(通常为设备旋转),托管activity正在被销毁
(2)Fragment处于保留状态的时间非常短暂,即fragment脱离旧activity到重新附加给立即创建的
新activity之间的一段时间
注意
只有当activity因设备配置发生改变被销毁时,fragment才会短时间处于被保留状态。如果activity是因操作系统需要回收内存而被销毁,则所有被保留的fragment也会被随之销毁。
设备旋转处理与 onSaveInstanceState(Bundle) 方法
onSaveInstanceState(…) 方法的设计用途——保存并恢复应用的UI状态
Fragment.onSaveInstanceState(…) 方法与保留fragment方法的主要区别在于,数据可以保存多久。如只需短暂保留数据,能应对设备配置改变就可以了,则保留fragment可以很轻松地解决问题。如果是保存对象,则更能体会使用保fragment的便利。因为我们再也无需操心要保存的对象是否已实现 Serializable 接口了。如需持久地保存数据,保留fragment的方式就行不通了。用户暂时离开应用后,如系统因回收内存需要销毁activity,则保留的fragment也会被随之销毁。
动态加载布局技巧(之后转移到专门的布局文)
使用限定符Qualifiers
使用最小宽度限定符(Smallest-width Qualifier)
最小宽度限定符允许我们对屏幕的宽度指定一个最小指(以 dp 为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕宽度小于这个值的设备就加载另一个布局。比如: res 目录下新建 layout-sw600dp 文件夹
更多相关文章
- Android:静态注册BroadcastReceiver
- Android(安卓)教你一步步搭建MVP+Retrofit+RxJava网络请求框架
- Android(安卓)Studio增量更新方法
- Android中与外部进程通信和调用外部程序
- Android中persistent属性用法详解
- 秒懂Android(安卓)Studio的奇技淫巧
- Android开发艺术探索
- Android(安卓)View的onTouch、onClick和onLongClick事件分析
- Android网页WebView图片文件上传的问题