愿你的快乐与岁月无关,愿你的纯真与经历无关。
沧海桑田后依旧乘风破浪,尘埃落定后依旧炽热欢畅!!!
整理一下自己的Fragment知识:
当今手机已然成为了日用品,就连平板电脑也成为办公所需,那么相应的程序开发在必要的界面就应该实现手机和平板要进行兼顾的情景,甚至在手机进行横竖屏幕的切换时候也要达到适配的效果,看看下面这两张图片:
手机竖屏:
手机横屏:
上面的两张图片是我手机上文件管理的使用情况,当手机是竖屏的时候就只显示第一张图片,当手机切换至横屏的时候就会显示为第二个界面,我们也可以达到这种效果,就要使用到Fragment的知识,我们看看吧!
碎片Fragment
是一种可以嵌入在活动当中的UI片段,可以是程序更合理的利用屏幕空间。Fragmeng也有自己的UI界面,也有自己的生命周期。和Activity有些像。
Fragment的声明周期
图片来源:百度搜索
对于Activity的生命周期就不多说,Fragment的声明周期多出5种方法,说明一下:
onAttach():当碎片与活动建立联系的时候调用。
onCreateView():为碎片创建视图时调用。
onActivityCreated():确保与碎片相关联的活动一定已经创建完毕的时候调用。
···
onDestoryView():当与碎片关联的视图被移除的时候调用。
onDetach():当碎片与活动解除关联的时候调用。
使用Fragmeng
那么我们看看怎么使用Fragment:
在Activity中使用Fragment有两种方法:
1:使用静态调用
step1: 创建一个FirstFragment类并继承Fragment,如下:
public class FirstFragment extends Fragment {
private static final String TAG = "FirstFragment";
public FirstFragment() {
// Required empty public constructor
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.i(TAG, "onAttach: ");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: ");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.i(TAG, "onCreateView: ");
return inflater.inflate(R.layout.fragment_first, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.i(TAG, "onActivityCreated: ");
}
@Override
public void onStart() {
super.onStart();
Log.i(TAG, "onStart: ");
}
@Override
public void onResume() {
super.onResume();
Log.i(TAG, "onResume: ");
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
}
@Override
public void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i(TAG, "onDestroyView: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
}
@Override
public void onDetach() {
super.onDetach();
Log.i(TAG, "onDetach: ");
}
}
对于Fragment加载的布局:fragment_first.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="wedfrend.wang.privateproject.fragment.FirstFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
FrameLayout>
上述方法中只有在onCreatView()
添加了加载布局代码:
inflater.inflate(R.layout.fragment_first, container, false);
并将View返回。
step2:创建一个FragmentActivity继承AppCompatActivity:
这里多说一句:在之前的android开发中,我们创建Activity继承的是Activity,创建含有FragmentActivity继承的是FragmentActivity,但是在V7包中,我们只需要继承AppCompatActivity就可以了:
public class FragmentActivity extends AppCompatActivity{
private static final String TAG = "FragmentActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: ");
setContentView(R.layout.activity_recycle_view);
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart: ");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume: ");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "onRestart: ");
}
}
对应布局activity_recycle_view.xml
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="wedfrend.wang.privateproject.recycle.RecycleViewActivity">
<fragment
android:id="@+id/news_title_fragment"
app:layout_widthPercent="100%"
app:layout_heightPercent="100%"
android:layout_alignParentRight="true"
android:name="wedfrend.wang.privateproject.fragment.FirstFragment"
tools:layout="@layout/fragment_right">
fragment>
android.support.percent.PercentRelativeLayout>
这里使用百分比布局。我们说创建的fragment
可以类似一个控件来使用,只不过在
android:name="wedfrend.wang.privateproject.fragment.FirstFragment"
在这个属性中一定要明确的指示出来你所创建的类:包名+类名
看看效果:
各方法的执行顺序
点击返回按钮
之后我们便可以进行逻辑上的操作在各个方法中。
2 :动态的加载Fragment,我们知道Fragment相当于一个UI,要动态的加载Fragment我们需要一个容器,所以我们现在将上述的Activity的布局修改:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="wedfrend.wang.privateproject.recycle.FragmentActivity">
<FrameLayout
android:id="@+id/frame_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent">FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:padding="10dp"
android:background="@android:color/darker_gray">
<Button
android:id="@+id/btn_firstFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/holo_green_dark"
android:textSize="15sp"
android:layout_marginRight="5dp"
android:text="First"
/>
<Button
android:id="@+id/btn_secondFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/holo_green_dark"
android:textSize="15sp"
android:layout_marginLeft="5dp"
android:text="Second"
/>
LinearLayout>
RelativeLayout>
我们添加一个FrameLayout来作为Fragment的容器。
动态的加载Fragment的步骤:
/**
* 说明:
*
* 动态添加碎片5个步骤
*
* 1.创建待添加碎片的实例
*
* 2.获取FragmentManager实例,在活动中通过getSupportFragmentManager()方法获取
*
* 3.开启一个事务,通过调用 beginTransaction()方法开启
*
* 4.向容器内添加或者替换碎片,使用replace()方式
*
* 5.提交事务,使用commit()完成
*
*
*
* 对于第4点,这里进行补充,FragmentTransaction提供操作碎片的方法中有
*
* add(),hide(),show(),remove(),replace();
*
* 在实际的项目中,具体使用那些方式需要你根据实际的情况进行考虑,代码如下,可以理解为replace是remove()和add()的结合
*/
public void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_fragment,fragment);
fragmentTransaction.commit();
}
那我们现在的布局中有两个Button,代码的需求是,现在需要点击第一个button,FrameLayout显示FirstFragment,点击第二个Button,显示第二个SectondFragment.
那我们看看代码,现在有两个Button,实例化和点击方法就不说了,现在看看如何使用,创建方法
//i = 0:FirstFragment
//i = 1:SecondFragment
//目前使用的是add,hide,和show的方法进行结合使用
public void setFragment(int i){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if(firstFragment != null){
fragmentTransaction.hide(firstFragment);
}
if(secondFragment != null){
fragmentTransaction.hide(secondFragment);
}
switch (i){
case 0:
if(firstFragment == null){
firstFragment = new FirstFragment();
fragmentTransaction.add(R.id.frame_fragment,firstFragment);
}else{
fragmentTransaction.show(firstFragment);
}
break;
case 1:
if(secondFragment == null){
secondFragment = new SecondFragment();
fragmentTransaction.add(R.id.frame_fragment,secondFragment);
}else{
fragmentTransaction.show(secondFragment);
}
break;
}
// fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
SecondFragment的方法和FirstFragment一致,只是布局显示不同。(略)
Button1的点击方法:
setFragment(0);
button2的点击方法
setFragment(1);
看看结果:
点击Button1:
点击Button2
因为我现在使用的add(),hide()和show()方法进行Fragment的显示与隐藏。
所以我么点击Button2的时候FirstFragment只是进行了隐藏,当我们在点击Button1的时候对于FirstFragment没有什么方法的调用。
点击手机的back按钮,结果
现在换一种显示方式,我们将add(),hide(),show()方法替换为replace()方法:修改容易,将
Button1的点击方法改为
replaceFragment(new FirstFragment());
Button2的点击方法改为
replaceFragment(new SecondFragment());
看看执行效果
点击Button1
点击Button2
调用replace()方法,对于FirstFragment就进行了销毁,当你在点击Button1的时候,SecondFragment同样也是被销毁的。
点击返回按钮
对于上述的显示方法进行实践后,可以发现两种加载Fragment的方式对于程序的书写方法需要 注意的地方还是有区别的。
但是 我们点击back按钮的时候,加载Fragment的Activity是跟着一起退出的,那么我们有什么办法使得Fragment的加载像栈一致,点击一次back按钮判断如果Activity中有两个Fragment,那么也像Activity一样,点击一次退出一个Fragment。
那就要看看下面的这个方法:调用FragmentTransaction中addToBackStack()方法。注意一定要在commit()方法之前进行调用
如下:
public void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_fragment,fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
现在我们再看看执行调用的方法:
点击Button1
点击Button2:
仔细的观察:添加了fragmentTransaction.addToBackStack(null);
方法之后,在点击Button2的时候,FirstFragment的onDetach()
方法并没有执行,所以此时的FirstFragment与Activity并没有解除绑定关系。再次点击Button1看看
所以,此时还是要执行onAttach()方法的。
现在我们的Fragment有三个了,分别是:
FirstFragment—SecondFragment—FirstFirgment
第一次back:
第二次back
第三次back
此时每点击一次back,退出一次Fragment,是否跟Activity的standard的启动模式类似?
Activity和Fragment两者之间的关系已经整理完成,有没有感觉对两者之间的理解更加了然于胸?
Activity和Fragment之间的通信
有时候我们需要在Fragment与Activity之间进行通信
1:静态的绑定情况下:
Activity获取Fragment的方法:
FragnentManager提供一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例
SecondFragment sf = (SecondFragment)getFragmentManager().findFragmentById(R,id.xxxx);
2.动态绑定的情况下,你需要实例化Fragment,这样的话就和普通类的调用方法一致了
Fragment获取Activity的方法:
在每个碎片中都可以通过调用getActicity()方法来得到当前碎片相关联的活动实例:
FragmentActivity activity = (FragmentActivity) getActivity();
使用限定符
一般情况下,android系统会根据屏幕的实际情况获取你的手机或者平板来加载相应的布局。
所以你只需要在res目录下创建一个layout-large的文件夹,将你的布局写入就好。
另外有一个最小限定符:
larger有多大?有时候我们就想当设备的宽度大于600的时候就加载类似平板的布局怎么办?
只需要创建 res/layout-sw600dp文件夹就好,将相应的布局放入,这样系统在宽度大于600的时候会自动加载相应的布局。
android中一些常见的限定符:
注:图片来源:第一行代码第二版(郭霖著)p158-p159
实例:一个简易的列表实践,适配平板,手机