一、碎片是什么android
碎片(Fragment)是一种能够嵌入在活动当中的UI片断,它能让程序更加合理和充分地利用大屏幕的空间,于是在平板上应用的很是普遍。虽然碎片对你来讲应该是个全新的概念,但我相信你学习起来应该绝不费力,由于它和活动实在是太像了,一样都能包含布局,一样都有本身的生命周期。你甚至能够将碎片理解成一个迷你型的活动,虽然这个迷你型的活动有可能和普通的活动是同样大的。app
那么究竟要如何使用碎片才能充分地利用平板屏幕的空间呢?想象咱们正在开发一个新闻应用,其中一个界面使用ListView展现了一组新闻的标题,当点击了其中一个标题,就打开另外一个界面显示新闻的详细内容。若是是在手机中设计,咱们能够将新闻标题列表放在一个活动中,将新闻的详细内容放在另外一个活动中,如图1所示。ide
图1布局
但是若是在平板上也这么设计,那么新闻标题列表将会被拉长至填充满整个平板的屏幕,而新闻的标题通常都不会太长,这样将会致使界面上有大量的空白区域,如图2所示。学习
图2this
所以,更好的设计方案是将新闻标题列表界面和新闻详细内容界面分别放在两个碎片中,而后在同一个活动里引入这两个碎片,这样就能够将屏幕空间充分地利用起来了,如图3所示。spa
图3设计
二、碎片的使用方式code
新建一个FragmentTest项目,而后开始咱们的碎片探索之旅吧。xml
新建一个左侧碎片布局left_fragment.xml,代码以下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Button" /> </LinearLayout>
这个布局很是简单,只放置了一个按钮,并让它水平居中显示。而后新建右侧碎片布局right_fragment.xml,代码以下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00ff00" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20sp" android:text="This is right fragment" /> </LinearLayout>
能够看到,咱们将这个布局的背景色设置成绿色,并放置了一个TextView用于显示一段文本。接着新建一个LeftFragment类,继承自Fragment。注意,这里可能会有两个不一样包下的Fragment供你选择,建议使用android.app.Fragment,由于咱们的程序是面向Android 4.0以上系统的,另外一个包下的Fragment主要是用于兼容低版本的Android系统。LeftFragment的代码以下所示:
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的onCreateView()方法,而后在这个方法中经过LayoutInflater的inflate()方法将刚才定义的left_fragment布局动态加载进来,整个方法简单明了。接着咱们用一样的方法再新建一个RightFragment,代码以下所示:
public class RightFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.right_fragment, container, false); return view; } }
新建another_right_fragment.xml,代码以下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffff00" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20sp" android:text="This is another right fragment" /> </LinearLayout>
这个布局文件的代码和right_fragment.xml中的代码基本相同,只是将背景色改为了黄色,并将显示的文字改了改。而后新建AnotherRightFragment做为另外一个右侧碎片,代码以下所示:
public class AnotherRightFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.another_right_fragment, container, false); return view; } }
代码一样很是简单,在onCreateView()方法中加载了刚刚建立的another_right_fragment布局。这样咱们就准备好了另外一个碎片,接下来看一下如何将它动态地添加到活动当中。修改activity_main.xml,代码以下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
<!-- 静态加载Fragment --> <fragment android:id="@+id/left_fragment" android:name="com.example.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/right_layout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" >
<!-- 能够在这个容器中动态加载Fragment -->
<fragment android:id="@+id/right_fragment" android:name="com.example.fragmenttest.RightFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> </LinearLayout>
能够看到,如今将右侧碎片放在了一个FrameLayout中,这是Android中最简单的一种布局,它没有任何的定位方式,全部的控件都会摆放在布局的左上角。因为这里仅须要在布局里放入一个碎片,所以很是适合使用FrameLayout。
以后咱们将在代码中替换FrameLayout里的内容,从而实现动态添加碎片的功能。修改MainActivity中的代码,以下所示:
public class MainActivity extends Activity implements OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: AnotherRightFragment fragment = new AnotherRightFragment(); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager. beginTransaction(); transaction.replace(R.id.right_layout, fragment); transaction.commit(); break; default: break; } } }
能够看到,首先咱们给左侧碎片中的按钮注册了一个点击事件,而后将动态添加碎片的逻辑都放在了点击事件里进行。结合代码能够看出,动态添加碎片主要分为5步。
这样就完成了在活动中动态添加碎片的功能,运行程序,能够看到启动界面如图4所示,而后点击一下按钮,效果如图5所示。
图4
图5
咱们成功实现了向活动中动态添加碎片的功能,不过你尝试一下就会发现,经过点击按钮添加了一个碎片以后,这时按下Back键程序就会直接退出。若是这里咱们想模仿相似于返回栈的效果,按下Back键能够回到上一个碎片,该如何实现呢?
其实很简单,FragmentTransaction中提供了一个addToBackStack()方法,能够用于将一个事务添加到返回栈中,修改MainActivity中的代码,以下所示:
public class MainActivity extends Activity implements OnClickListener { …… @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: AnotherRightFragment fragment = new AnotherRightFragment(); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager. beginTransaction(); transaction.replace(R.id.right_layout, fragment); transaction.addToBackStack(null); transaction.commit(); break; default: break; } } }
这里咱们在事务提交以前调用了FragmentTransaction的addToBackStack()方法,它能够接收一个名字用于描述返回栈的状态,通常传入null便可。如今从新运行程序,并点击按钮将AnotherRightFragment添加到活动中,而后按下Back键,你会发现程序并无退出,而是回到了RightFragment界面,再次按下Back键程序才会退出。
四、碎片和活动之间进行通讯
虽然碎片都是嵌入在活动中显示的,但是实际上它们的关系并无那么亲密。你能够看出,碎片和活动都是各自存在于一个独立的类当中的,它们之间并无那么明显的方式来直接进行通讯。若是想要在活动中调用碎片里的方法,或者在碎片中调用活动里的方法,应该如何实现呢?
为了方便碎片和活动之间进行通讯,FragmentManager提供了一个相似于findViewById()的方法,专门用于从布局文件中获取碎片的实例,代码以下所示:
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
调用FragmentManager的findFragmentById()方法,能够在活动中获得相应碎片的实例,而后就能轻松地调用碎片里的方法了。
掌握了如何在活动中调用碎片里的方法,那在碎片中又该怎样调用活动里的方法呢?其实这就更简单了,在每一个碎片中均可以经过调用getActivity()方法来获得和当前碎片相关联的活动实例,代码以下所示:
MainActivity activity = (MainActivity) getActivity();
有了活动实例以后,在碎片中调用活动里的方法就变得垂手可得了。另外当碎片中须要使用Context对象时,也可使用getActivity()方法,由于获取到的活动自己就是一个Context对象了。
这时不知道你心中会不会产生一个疑问,既然碎片和活动之间的通讯问题已经解决了,那么碎片和碎片之间可不能够进行通讯呢?
说实在的,这个问题并无看上去的复杂,它的基本思路很是简单,首先在一个碎片中能够获得与它相关联的活动,而后再经过这个活动去获取另一个碎片的实例,这样也就实现了不一样碎片之间的通讯功能,所以这里咱们的答案是确定的。