Android之Fragment

Fragment 表示 Activity 中的行为或用户界面部分。您可以将多个片段组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(有点像您可以在不同 Activity 中重复使用的“子 Activity”)。

片段必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。 不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除它们。 当您执行此类片段事务时,您也可以将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生片段事务的记录。 返回栈让用户可以通过按返回按钮撤消片段事务(后退)。

当您将片段作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为 元素插入您的 Activity 布局中,或者通过将其添加到某个现有 ViewGroup,利用应用代码进行插入。不过,片段并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的片段用作 Activity 的不可见工作线程。

————转载于Google官方API

一、Fragment的创建与使用
Fragment的使用方式有两种,一种是在xml布局文件中直接静态使用定义好的Fragment;另一种是在Activity的Java代码中动态添加Fragment。
先来讲第一种:
首先我们要为Fragment写一个布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:background="#BEBEBE"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="fragment"/>

</LinearLayout>

在使用Fragment前我们必须创建一个继承自Fragment的类(这里有不同包下的Fragment接口,一种是android.app包下的,一种是android.support.v4.app包下的,我们一般使用功能比较全面的android.support.v4.app包下Fragment),然后重写这个Fragment的onCreatView方法。

package com.studio.fragmentdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

//注意fragment继承的Fragment类最好是v4包下的
public class FragmentTest extends Fragment
{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        //将布局fragment_layout加载到Fragment中,最后一个参数表示是否附加到布局文件的根目录
        return inflater.inflate(R.layout.fragment_layout,container,false);
    }

之后在要用到这个Fragment的Activity所绑定的布局文件中添加上这个Fragment

<?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:id="@+id/activity_demo_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.studio.fragmentdemo.DemoFragmentActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="在xml中使用Fragment"/>

    <!--
    在xml中使用Fragment,即静态添加Fragment
    在布局中添加一个已经写好的Fragmemt
    name:指定要在布局中实例化的Fragment类
    若没有加最后一句tools:layout="@layout/fragment_laYOUT"则无法在预览图中看到,只有启动模拟器才能看到
    -->
    <fragment
        android:name="com.studio.fragmentdemo.FragmentTest"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:layout="@layout/fragment_layout"></fragment>

</LinearLayout>

之后我们运行代码,就可以在对应的Activity中看到这个Fragment了。

再来说第二种方法:
还是与之前一样的Fragment的布局文件,这里不再赘述也不再贴上代码。
还是与之前一样的创建继承自Fragment的类,这里也不再赘述不再贴上代码。
然后在要用到这个Fragment的Activity中用如下代码为其添加Fragment,注意为了方便显示Fragment的效果,我在Activity的布局文件(LinearLayout)中为这个Fragment创建了一个子布局(FrameLayout,其id为fl_fragment)专门用来显示Fragment

        //要想在Activity中管理(如添加、删除、修改)Fragment,需要使用FragmentManager(即Fragment管理器)
        //由于我们在创建Fragment时使用的是support.v4包下的,因此得到FragmentManager对象的方法是getSupportFragmentManager(),若使用的是Activity内置的Fragment(即android.app包下的),那么在这里使用getFragmentManager()就好了
        FragmentManager fragmentManager=getSupportFragmentManager();
        //获取Fragment的事务对象并利用FragmentManager开启事务
        FragmentTransaction transaction=fragmentManager.beginTransaction();
        //创建自定义的Fragment对象
        FragmentTest fragmentTest=new FragmentTest();
        //第一个参数的意思是要将Fragment放到哪一个container中(在这里container就是一个布局),第二个参数就是要添加的Fragment对象,第三个参数是我们给Fragment取的Tag,可有可无,但为了方便之后的删除操作,这里为其添加一个Tag"fragment"
        transaction.add(R.id.fl_fragment,fragmentTest,"fragment");
        //提交操作,千万不能漏
        transaction.commit();

二、Fragment的删除、替换、隐藏、显示
注:之前的代码依然沿用
1、
首先说一说如何动态的删除一个Fragment,首先我们在显示这个Fragment的Activity的布局文件下为其添加一个用来隐藏Fragment的Button

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="remove"
        android:text="Remove fragment"/>

然后在Activity中完成这个Button的点击事件,注意从这里开始我已将fragmentManager设置为整个Activity的成员变量。

public void remove(View view)
    {
        FragmentTransaction transaction=fragmentManager.beginTransaction();
        //通过findFragmentByTag的方法找到要删除的Fragment并进行删除,当然还有findFragmentById的方法
        //remove方法的唯一参数就是要删除的Fragment对象
        transaction.remove(fragmentManager.findFragmentByTag("fragment"));
        transaction.commit();
    }

这时我们运行代码,点击remove按钮,就发现显示的Fragment被删除不见了。

接着说说如何用一个新的Fragment替换旧的Fragment
先为这个新Fragment新建一个布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="AnotherFragment"/>

</LinearLayout>

首先我们新建一个Fragment命名为AnotherFragmentTest(原来的叫FragmentTest)

package com.studio.fragmentdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class AnotherFragmentTest extends Fragment
{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.anotherfragment_layout,container,false);
    }
}

然后在Activity的布局文件中为替换Fragment这个操作新建一个Button命名为replace

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="replace"
        android:text="replace"/>

接着在Activity的Java代码中完成这个Button的点击事件

public void replace(View view)
    {
        FragmentTransaction transaction=fragmentManager.beginTransaction();
        AnotherFragmentTest anotherFragmentTest=new AnotherFragmentTest();
        /*将ID为fl_container的布局中的Fragment用新的Fragment替换*/
        transaction.replace(R.id.fl_container,anotherFragmentTest);
        transaction.commit();
    }

之后我们运行代码,点击了replace按钮后,就发现原来文本为Fragment的Fragment被新的文本内容为AnotherFragment的Fragment替换了。

2、
接下来是如何隐藏和显示一个Fragment
首先我们为隐藏Fragment和显示Fragment事件添加触发Button

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="hide"
        android:text="hide"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="show"
        android:text="show"/>

然后在Java代码中完成这两个Button的点击事件

public void hide(View view)
    {
        FragmentTransaction transaction=fragmentManager.beginTransaction();
        transaction.hide(fragmentManager.findFragmentByTag("fragment"));
        transaction.commit();
    }

    public void show(View view)
    {
        FragmentTransaction transaction=fragmentManager.beginTransaction();
        transaction.show(fragmentManager.findFragmentByTag("fragment"));
        transaction.commit();
    }

之后运行代码,点击hide发现文本内容为Fragment的Fragment不见了,再点击show发现Fragment再次出现。

3、
再来讲讲Fragment的数据传递。
[1]、首先说一说一个Activity中只放一个Fragment的情况。
现在在一个Activity中只有一个Fragment,现在开启App默认显示的Fragment是一个EditText和一个Button,要求是当我在EditText输入字符串后,点击Button,输入的字符串将传给另一个Fragment中的TextView,并让另一个Fragment代替这个Activity中的Fragment显示出来,下面说代码。
先在Activity的布局文件中添加一个容器布局来显示Fragment

<?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:id="@+id/activity_fragment_values"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.studio.fragmentdemo.FragmentValuesActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"></FrameLayout>

</LinearLayout>

然后为默认显示的Fragment和最终替换上来的Fragment写布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="input"/>
    <Button
        android:id="@+id/btn_sendValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Value"
        android:onClick="send"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <!--用来接受并显示其他Fragment传递过来的EditText中的文本内容-->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

接着分别创建这两个Fragment并在onCreatView方法中完成传输数据、接收数据和替换Fragment的方法

package com.studio.fragmentdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;

/**
 * Created by xwx on 2017/2/24.
 */

public class AnotherFragment_value extends Fragment
{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.fragment_value,container,false);
        //注意这不是在Activity类或者View类下,无法直接调用findViewById方法,必须通过View的对象调用findViewById方法
        final EditText editText= (EditText) view.findViewById(R.id.et);
        Button btn_sendValue= (Button) view.findViewById(R.id.btn_sendValue);
        btn_sendValue.setOnClickListener(new View.OnClickListener()
        {
            //点击SendValue按钮时,将EditText中的文本内容传递到另一个Fragment中让另一个Fragment显示出来
            @Override
            public void onClick(View v)
            {
                AnotherFragment_values2 anotherFragment_values2=new AnotherFragment_values2();
                //通过bundle传递数据
                Bundle bundle=new Bundle();
                bundle.putString("arg",editText.getText().toString());
                anotherFragment_values2.setArguments(bundle);
                getFragmentManager().beginTransaction().replace(R.id.container,anotherFragment_values2).commit();
            }
        });
        return view;
    }
}
package com.studio.fragmentdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;

/**
 * Created by xwx on 2017/2/24.
 */

public class AnotherFragment_values2 extends Fragment
{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        View view=inflater.inflate(R.layout.fragment_value2,container,false);
        TextView textView= (TextView) view.findViewById(R.id.tv);
        //通过getArguments得到传过来的Bundle对象,再通过Bundle的getString方法和字符串对应的key得到字符串
        textView.setText(getArguments().getString("arg"));
        return view;
    }
}

[2]、
继续学习一种降低耦合的Fragment之间传递数据的方法——接口回调
我们最终要达到的目的是在一个Fragment中有三个Button,文本内容分别为Button1,Button2,Button3。现在我们要做的就是当我们点击其中任何一个Button的时候,位于这个Fragment下面的另一个Fragment的TextView文本内容变成与按钮文本内容一样。
首先我们为两个Fragment写布局文件

<?xml version="1.0" encoding="utf-8"?>
<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/btn_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"/>
    <Button
        android:id="@+id/btn_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"/>
    <Button
        android:id="@+id/btn_3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"/>

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_value"
        android:text="Waiting for initing..."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

然后创建两个对应的Fragment

package com.studio.fragmentdemo;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

/**
 * Created by xwx on 2017/2/24.
 */

public class FragmentValuePass1 extends Fragment implements View.OnClickListener
{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        View view=inflater.inflate(R.layout.fragment_valuepass1,container);
        Button btn_1= (Button) view.findViewById(R.id.btn_1);
        Button btn_2= (Button) view.findViewById(R.id.btn_2);
        Button btn_3= (Button) view.findViewById(R.id.btn_3);
        //为三个按钮绑定监听器
        btn_1.setOnClickListener(this);
        btn_2.setOnClickListener(this);
        btn_3.setOnClickListener(this);
        return view;
    }
    @Override
    public void onClick(View v)
    {

    }
}
package com.studio.fragmentdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import org.w3c.dom.Text;

/**
 * Created by xwx on 2017/2/24.
 */

public class FragmentValuePass2 extends Fragment
{
    private TextView textView;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        View view=inflater.inflate(R.layout.fragment_valuepass2,container);
        textView= (TextView) view.findViewById(R.id.tv_value);
        return view;
    }

}

在这里我们换一种方法使用Fragment,我们将两个Fragment定义在Activity的xml布局文件中进行使用,注意所有的fragment都要给其附上id

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <fragment
            android:id="@+id/frg_1"
            android:name="com.studio.fragmentdemo.FragmentValuePass1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout="@layout/fragment_valuepass1"/>

        <fragment
            android:id="@+id/frg_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:name="com.studio.fragmentdemo.FragmentValuePass2"
            tools:layout="@layout/fragment_valuepass2"/>
    </LinearLayout>

然后在要显示文本的Fragment中为其添加一个单独的改变TextView内容的方法。

    //改变这个Fragment的TextView内容的方法
    public void setFragmentTextValue(String textValue)
    {
        textView.setText(textValue);
    }

之后创建一个回调接口

package com.studio.fragmentdemo;
//回调接口
public interface IButton
{
    //回调方法
    void onFragmentButtonClick(String text);
}

让Activity实现这个回调接口并重写回调函数

    //重写回调方法
    @Override
    public void onFragmentButtonClick(String text)
    {
        //当在布局中定义<fragment>标签时使用findFragmentById,否则使用new的形式。二者区别:在布局中定义<fragment>标签后Fragment是固定的,不可替换,推荐使用new的形式动态处理Fragment,使展示效果更加灵活。
        FragmentValuePass2 fragmentValuePass2= (FragmentValuePass2) getSupportFragmentManager().findFragmentById(R.id.frg_2);
        fragmentValuePass2.setFragmentTextValue(text);
    }

回到具有三个Button的Fragment,我们首先为其定义一个全局的成员接口

private IButton mIButton;

并重写onClick方法

    //重写监听事件,实现回调方法
    @Override
    public void onClick(View v)
    {
        //将Button的文本内容赋值给FragmentValuePass2中的TextView
        mIButton.onFragmentButtonClick(((Button)v).getText().toString());
    }

注意由于此时mIButton接口变量还未与任何类进行绑定,因此若就此执行代码会发生空指针异常,我们还需要在这个Fragment下重写一个Fragment生命周期中的回调方法让mIButton和这个Fragment所属的Activity绑定

    //onAttach方法,在Fragment和Activity建立关联的时候回调
    //在这里实例化mIButton
    @Override
    public void onAttach(Context context)
    {
        super.onAttach(context);
        try
        {
            mIButton = (IButton) context;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

就这样,我们完成了一个以接口回调机制为基础的Fragment之间的数据传递,这种方式可以降低Fragment之间的耦合度,增加代码的复用率。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值