该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽可能按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深刻理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其余的优质博客,在此向各位大神表示感谢,膜拜!!!android
上一篇博客咱们主要总结了以前博客的知识,那么本篇咱们来分析一下Fragment。Fragment又被称为“碎片”,可把它看作是一个轻量的Activity,它能够像Activity同样包含布局。Fragment一般是嵌套在Activity中使用的。app
Fragment设计之初是也许是为了适配平板等大屏幕设备,在这些设备上有足够的空间同时显示两个“Activity”,使用Fragment可让咱们更加充分地利用平板的屏幕空间。面咱们一块儿来探究下如何使用Fragment。ide
首先须要注意,Fragment是在3.0版本引入的,若是你使用的是3.0以前的系统,须要先加入android-support-v4支持才能使用Fragment功能。在Android Studio中这是很容易的,另请注意尽可能不要用app包中的fragment,由于这个是在3.0以后才有的,支持的版本过高,在低版本中是是用不了的。函数
[app/build.gradle]布局
compile 'com.android.support:support-v4:25.3.1'
注:后面的25.3.1是android-support-v4的版本号,25表示android-support-v4的主版本,这个主版本尽可能要与compileSdkVersion 25保持一致。性能
咱们在TestApplication中新建一个包fragment专门用来测试与Fragment相关知识。测试
新建布局文件fragment_good.xmlgradle
<FrameLayout android:background="@color/light_green" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Goods" android:textStyle="bold" android:textSize="30sp" android:layout_gravity="center"/> </FrameLayout>
这个布局文件十分简单,仅仅是在FrameLayout布局中间位置放置了一个TextView,注意这里的FrameLayout是一个布局,称为“帧布局”,,当咱们往里面添加控件的时候,会默认把他们放到这块区域的左上角,而这种布局方式却没有任何的定位方式,因此它应用的场景并很少;帧布局的大小由控件中最大的子控件决定,若是控件的大小同样大的话,那么同一时刻就只能看到最上面的那个组件!后续添加的控件会覆盖前一个!虽然默认会将控件放置在左上角,可是咱们也能够经过layout_gravity属性,指定到其余的位置!ui
再新建布局文件fragment_goodcar.xmlspa
<FrameLayout android:background="@color/light_green" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="GoodCar" android:textStyle="bold" android:textSize="30sp" android:layout_gravity="center"/> </FrameLayout>
这个布局文件与上面的几乎彻底一致。
那么咱们的布局文件有了,咱们该新建使用该布局文件的类了,这一点与Activity能够说是彻底一致
新建GoodsFragment,该类继承于Fragment
public class GoodsFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { //加载上面的布局文件 View view = inflater.inflate(R.layout.fragment_goods, null); return view; } }
能够看到Fragment的使用几乎与Activity一模一样。下面如法炮制新建GoodCarFragment
public class GoodCarFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { //加载上面的布局文件 View view = inflater.inflate(R.layout.fragment_goodcar, null); return view; } }
注:其实在AndroidStudio中有更简单的办法
Fragment有了,那么怎么使用呢,Fragment通常是嵌套在Activity中使用,那么咱们新建一个EasyFragmentActivity
在其布局文件中activity_easy_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:baselineAligned="false" > <!--在这里使用fragment 标签 并指定name为上面定义的两个Fragment的全类名--> <fragment android:id="@+id/fragment1" android:name="com.mafeibiao.testapplication.fragment.GoodCarFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" tools:layout="@layout/fragment_goodcar" /> <fragment android:id="@+id/fragment2" android:name="com.mafeibiao.testapplication.fragment.GoodsFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" tools:layout="@layout/fragment_goods" /> </LinearLayout>
EasyFragmentActivity的代码也极其简单,使用默认生成的就能够了
public class EasyFragmentActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_easy_fragment); } }
到这里咱们运行程序就可获得以下结果
到这里咱们已经会使用Fragment了,神奇不神奇?不过上面的只是最简单的,有的文章称它是静态的使用Fragment,由于咱们只是在XML中引用了一下。既然有静态,那就有动态了,下面来看动态
稍微修改一下上面的布局文件activity_easy_fragment
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:orientation="vertical"> <!--删除了上面两个fragment标签,加入了一个id为main_layout的FrameLayout布局--> <FrameLayout android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
咱们的Activity代码也作了相应改变
public class EasyFragmentActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_easy_fragment); Display display = getWindowManager().getDefaultDisplay(); if (display.getWidth() > display.getHeight()) { GoodCarFragment fragment1 = new GoodCarFragment(); getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit(); } else { GoodsFragment fragment2 = new GoodsFragment(); getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit(); } } }
首先,咱们要获取屏幕的宽度和高度,而后进行判断,若是屏幕宽度大于高度就添加fragment1,若是高度大于宽度就添加fragment2。动态添加Fragment主要分为4步:
向容器内加入Fragment,通常使用replace方法实现,须要传入容器的id和Fragment的实例。replace方法有几个重载方法,在这里使用的是
FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);
该方法第1个参数是容器ID,即容纳Fragment的容器ID,这里传入的是在activity_easy_fragment声明的id为main_layout的FrameLayout布局,第2个参数即自定义的Fragment对象。整个函数的意思就是第2个参数中指定的Fragment嵌入到第一个参数指定的布局容器中。这一点咱们能够经过Hierarchy View验证。
和Activity同样,Fragment 也有本身的生命周期,理解Fragment的生命周期很是重要,咱们经过代码的方式来瞧一瞧Fragment的生命周期是什么样的:
public class GoodsFragment extends Fragment { private static String TAG= GoodsFragment.class.getSimpleName(); @Override public void onAttach(Context context) { super.onAttach(context); Log.d(TAG,"onAttach"); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(TAG,"onCreateView"); View view = inflater.inflate(R.layout.fragment_goods, null); return view; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); Log.d(TAG,"onViewCreated"); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log.d(TAG,"onActivityCreated"); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG,"onCreate"); } @Override public void onStart() { super.onStart(); Log.d(TAG,"onStart"); } @Override public void onResume() { super.onResume(); Log.d(TAG,"onResume"); } @Override public void onPause() { super.onPause(); Log.d(TAG,"onPause"); } @Override public void onStop() { super.onStop(); Log.d(TAG,"onStop"); } @Override public void onDestroyView() { super.onDestroyView(); Log.d(TAG,"onDestroyView"); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy"); } @Override public void onDetach() { super.onDetach(); Log.d(TAG,"onDetach"); } }
个人神呀,Fragment的生命周期函数真是多呀。Fragment的生命周期函数比Activity的生命周期函数要多很多。不过要理解Fragment的生命周期并不难。由于咱们前面的文章中已经分析了Activity的生命周期。把EasyFragmentActivity也稍微修改一下,加入生命周期函数
public class EasyFragmentActivity extends AppCompatActivity { private static String TAG= EasyFragmentActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG,"onCreate"); setContentView(R.layout.activity_easy_fragment); Display display = getWindowManager().getDefaultDisplay(); if (display.getWidth() > display.getHeight()) { GoodCarFragment fragment1 = new GoodCarFragment(); getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1,Constant.GOODCAR_FRAGMENT_FLAG).commit(); } else { GoodsFragment fragment2 = new GoodsFragment(); getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2,Constant.GOODS_FRAGMENT_FLAG).commit(); } } @Override public void onStart() { super.onStart(); Log.d(TAG,"onStart"); } @Override public void onResume() { super.onResume(); Log.d(TAG,"onResume"); } @Override public void onPause() { super.onPause(); Log.d(TAG,"onPause"); } @Override public void onStop() { super.onStop(); Log.d(TAG,"onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy"); } }
运行一下
咱们在EasyFragmentActivity中onCreate函数中动态加载了Fragment,看Fragment的生命周期的回调顺序,咱们来解释一下
按下home键以后
按下back键以后
Fragment的生命周期与Activity的生命周期很相似,Fragment一般也是嵌套在Activity中使用,因此的Fragment的生命周期跟当前Activity的状态息息相关。
一个宿主Activity可能包含2个以上的Fragment,那么Fragment之间以及Fragment与宿主Activity之间的通讯问题是咱们不得不解决的问题。
总结来讲,基本有如下几种方法:
咱们能够直接在Fragment中调用Activity中的公开方法,以下:
public class GoodsFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(TAG,"onCreateView"); View view = inflater.inflate(R.layout.fragment_goods, null); //经过getActivity()获取与该Fragment相关联的Activity ((EasyFragmentActivity)getActivity()).show(); return view; } }
show方法是定义在EasyFragmentActivity中的public方法
public class EasyFragmentActivity extends AppCompatActivity { public void show(){ Log.d(TAG,"show"); } }
咱们能够直接在一个Fragment中调用另一个Fragment的公开方法,前提是要先拿到另一个Fragment的实例,咱们先来看看怎样拿到实例:
public class GoodsFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(TAG,"onCreateView"); View view = inflater.inflate(R.layout.fragment_goods, null); // ((EasyFragmentActivity)getActivity()).show(); GoodCarFragment goodCarFragment = (GoodCarFragment) getActivity().getSupportFragmentManager().findFragmentByTag("GOODCAR_FRAGMENT_FLAG"); goodCarFragment.show(); return view; } }
上面的findFragmentByTag是FragmentManager类的方法,是根据Tag找到对应的Fragment,这个Tag是在EasyFragmentActivity中添加Fragment时指定的。
先定义一个接口,咱们在接口中定义咱们须要的功能
public interface IShow { public void show(); }
而后让宿主Activity实现该接口
public class EasyFragmentActivity extends AppCompatActivity implements IShow { @Override public void show(){ Log.d(TAG,"show"); } }
而后在Fragment中定义一个该接口的成员变量,并在Fragment onAttach方法中实例化该接口
public class GoodsFragment extends Fragment { private IShow mCallback; @Override public void onAttach(Context context) { super.onAttach(context); if (context != null) { mCallback = (IShow) context; } Log.d(TAG,"onAttach"); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(TAG,"onCreateView"); View view = inflater.inflate(R.layout.fragment_goods, null); //而后在这里就可使用了 mCallback.show(); return view; } }
这种方案应该是既能达到Fragment复用,又能达到很好的可维护性,而且性能也是杠杠的,因此说推荐使用。
在上面的方案以外,还存在Hanler、广播等通用的通讯解决方案,我这里就不进行具体介绍了。由于前面咱们已经详细分析了Handler的消息机制,至于广播,我会在之后的文章中分析。
咱们本篇详细分析了Android Fragment相关知识,灵活运用Fragment可让你获得意向不到的效果,咱们使用Fragment下降耦合度,可是可能也增长了Fragment之间以及Fragment与宿主Activity之间的通讯成本,不过这点成本对于程序的易扩展性来讲是十分值得的。
参考文章:
http://blog.csdn.net/guolin_blog/article/details/8881711
下一篇文章是呢是Fragment的最佳实践,读者敬请期待哦。
此致,敬礼