一、碎片是什么
碎片(Fragment)是一种可以嵌入在活动当中的UI片段,能让程序更加合理和充分地利用大屏幕空间,因而在平板上应用得非常广泛。
二、碎片的使用方式
2.1碎片的简单使用
在一个活动当中添加两个碎片 ,并让这两个碎片平分活动空间。
(1)新建一个左侧碎片布局left_fragment.xml,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00ff00">
<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>
接着新建一个LeftFragment 类,并让它继承自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的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;
}
}
接下来修改activity_main.xml中的 代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/right_fragment"
android:name="RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
使用了 <fragment>标签在布局中添加碎片
2.2动态添加碎片
新建another_right_fragment.xml,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#ffff00"
android:layout_width="match_parent"
android:layout_height="match_parent">
<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>
然后新建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;
}
}
我们就准备好了另一个碎片,接下来看一下如何将它动态地添加到活动当中。修改 activity_main.xml,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="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">
</FrameLayout>
</LinearLayout>
现在将右侧碎片替换成了一个FrameLayout(帧布局)中,由于这里仅需要在布局里放入一个碎片,不需要任何定位,因此非常适合使用FrameLayout。
下面我们将在代码中向FrameLayout里添加内容,从而实现动态添加碎片的功能。修改 MainActivity中的代码,如下所示:
public class MainActivity extends AppCompatActivity implements View.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);
replaceFragment(new RightFragment());//动态添加RightFragment
}
@override
public void onClick(View v){
switch(v.getId()){
case R.id.button:
replaceFragment(new AnotherRightFragment());//按下按钮后,将右侧碎片替换成
AnotherRightFragment
break;
defaule:
break;
}
}
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager=getSupportFragmentManager();
通过getSupportFragmentManager()获取FragmentManager
FragmentTransaction transaction=fragmentManager.beginTransaction();
通过beginTransaction()开启一个事务
transaction.replace(R.id.right_layout,fragment);
向容器内添加或替换碎片,一般使用replace() 方法实现,需要传入容器的id和待添加的
碎片实例
transaction.commit();提交事务
}
}
点击按钮后,效果如图所示:
2.3在碎片中模拟返回栈
通过点击按钮添加了一个碎片之后,这时按下Back键程序就会直接退出。如果这里我们想模仿类 似于返回栈的效果,按下Back键可以回到上一个碎片,FragmentTransaction中提供了一个addToBackStack() 方法,可以用于将一个事务添加到返回栈中。
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager=getSupportFragmentManager();
通过getSupportFragmentManager()获取FragmentManager
FragmentTransaction transaction=fragmentManager.beginTransaction();
通过beginTransaction()开启一个事务
transaction.replace(R.id.right_layout,fragment);
向容器内添加或替换碎片,一般使用replace() 方法实现,需要传入容器的id和待添加的
碎片实例
transaction.addToBackStack(null);//添加到返回栈中
transaction.commit();提交事务
}
现在重新运行程序,并点击按钮将 AnotherRightFragment添加到活动中,然后按下Back键,你会发现程序并没有退出,而是回到了 RightFragment界面,继续按下Back键,RightFragment界面也会消失,再次按下Back键,程序才 会退出。
2.4碎片和活动之间进行通信
为了方便碎片和活动之间进行通信,FragmentManager提供了一个类似于findViewById() 的 方法,专门用于从活动中获取碎片的实例,代码如下所示:
RightFragment rightFragment=(RightFragment)getSupportFragmentManager().
findFragmentById(R.id.right_fragment);
在碎片中调用活动:
MainActivity activity = (MainActivity) getActivity();
当碎片中需要使 用Context 对象时,也可以使用getActivity() 方法,因为获取到的活动本身就是一 个Context 对象。
首先在一个碎片中可以得到与它相关联的活动,然后再通过这个活动去获取另外一个碎片的实例,这样也就实现了不同碎片之间的通信功能。
三、碎片的生命周期
3.1碎片的状态和回调
01. 运行状态
当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态。
02. 暂停状态
当一个活动进入暂停状态时(由于另一个未占满屏幕的活动被添加到了栈顶),与它相关联的可见碎片就会进入到暂停状态。
03. 停止状态
当一个活动进入停止状态时,与它相关联的碎片就会进入到停止状态,或者通过调用 FragmentTransaction的remove() 、replace() 方法将碎片从活动中移除,但如果在事务提交之前调用addToBackStack() 方法,这时的碎片也会进入到停止状态。总的来说, 进入停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收。
04. 销毁状态
碎片总是依附于活动而存在的,因此当活动被销毁时,与它相关联的碎片就会进入到销毁状态。或者通过调用FragmentTransaction的remove() 、replace() 方法将碎片从活动中移除,但在事务提交之前并没有调用addToBackStack() 方法,这时的碎片也会进入到销毁状态。
碎片还提供了一些附加的回调方法,那我们就重点看一下这几个回调。
onAttach() 。当碎片和活动建立关联的时候调用。
onCreateView() 。为碎片创建视图(加载布局)时调用。
onActivityCreated() 。确保与碎片相关联的活动一定已经创建完毕的时候调用。
onDestroyView() 。当与碎片关联的视图被移除的时候调用。
onDetach() 。当碎片和活动解除关联的时候调用。
四、动态加载布局的技巧
4.1使用限定符
在运行时判断程序应该是使用双页模式还是单页模式呢?这就需要借助限定符 (Qualifiers)来实现了。
修改FragmentTest项目中 的activity_main.xml文件,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
接着在res目录下新 建layout-large文件夹,在这个文件夹下新建一个布局,也叫作activity_main.xml,代码如下所 示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/right_fragment"
android:name="RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"/>
</LinearLayout>
layout/activity_main布局只包含了一个碎片,即单页模式,而layout-large/ activity_main布局包含了两个碎片,即双页模式。
其中large 就是一个限定符,那些屏幕被认为是large 的设备就会自动加载layout-large文件夹下的布局,而小屏幕的设备则还是会加载layout 文件夹下的布局。
平板:
手机:
4.2使用最小宽度限定符
最小宽度限定符允许我们对屏幕的宽度指定一个最小值(以dp为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕宽度小于这个值的设备就加载另一个布局。
在res目录下新建layout-sw600dp文件夹,然后在这个文件夹下新建activity_main.xml布局,代码 如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/right_fragment"
android:name="RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"/>
</LinearLayout>
当程序运行在屏幕宽度大于600dp的设备上时,会加载layout-sw600dp/activity_main布局,当程序运行在屏幕宽度小于600dp的设备上时,则仍然加载默认的layout/activity_main布局。