第四章 碎片

一、碎片是什么

碎片(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布局。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值