Android开发之Fragment基础

Fragment(碎片)是Android3.0新增的概念,它与Activity非常相似,是一种可以嵌入在Activity当中的UI片段,它能让程序更加合理和充分的利用大屏幕的空间。当然,在小屏幕中它的用处也很大,后面会陆续讲到。更多信息可以参考官方文档(http://developer.android.com/guide/topics/fundamentals/fragments.html
我们从Fragment最基本的使用开始吧!

1、Fragment的使用

注意:如果我们使用V4包,必须将MainActivity派生自FragmentActivity,否则无法启动程序!因为系统的Activity只能用来盛装系统自带的Fragment、根本无法识别V4包中的Fragment。如果不使用V4包,使用系统自带的Fragment则不必将MainActivity派生自FragmentActivity。

1.1 Fragment创建

创建一个Fragment,必须创建一个Fragment的子类,或者继承于另一个已经存在的Fragment,并重写其onCreateView()方法。示例代码如下:

public class MyFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //从布局文件fragment_my.xml中加载布局
        View view = inflater.inflate(R.layout.fragment_my, container, true);
        return view;
   }

1.1.1 在布局xml文件中添加fragment(静态)
基本的代码片段如下:

<fragment
        android:id="@+id/bottomFragment"
        android:name="com.asus.mydemo1.Fragment.MyFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

其中name属性表明添加的fragment为MyFragment,而MyFragment已在前面创建过。如此一来我们即可以将xml文件中定义的fragment显示出来。这种方式也叫作静态使用,如同普通的view一样。

1.1.2 在代码中动态添加fragment
上面讲到了静态创建fragment,但在实际应用中很少这么用,而是直接在code中定义fragment,这样更加灵活并且逻辑也清晰。使用方法如下所示:

  1. 在activity的layout文件中定义装载fragment的container,常用的LinearLayout、FrameLayout等都可以。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="fragment1" />

        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="fragment2" />

        <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="fragment3" />
    </LinearLayout>

</RelativeLayout>

从上面的布局文件中显而易见:下方水平摆放3个按钮(我们将用它们来切换fragment);上方是一个FrameLayout,用它来装载fragment。
2. 创建3个fragment,这一步在前面讲过,不在赘述。
3. 在MainActivity中显示默认的fragment,点击button切换fragment:

public class MainActivity extends Activity {

    private Fragment fragment1;
    private Fragment fragment2;
    private Fragment fragment3;

    private Button btn1, btn2, btn3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initEvent();
    }

    private void initEvent() {
        btn1.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (fragment1 == null) {
                    fragment1 = new Fragment1();
                }
                FragmentManager manager = getFragmentManager();
                FragmentTransaction transaction = manager.beginTransaction();
                transaction.replace(R.id.container, fragment1);
                transaction.commit();
            }
        });

        btn2.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (fragment2 == null) {
                    fragment2 = new Fragment2();
                }
                FragmentManager manager = getFragmentManager();
                FragmentTransaction transaction = manager.beginTransaction();

                transaction.replace(R.id.container, fragment2);
                transaction.commit();
            }
        });

        btn3.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (fragment3 == null) {
                    fragment3 = new Fragment3();
                }
                FragmentManager manager = getFragmentManager();
                FragmentTransaction transaction = manager.beginTransaction();

                transaction.replace(R.id.container, fragment3);
                transaction.commit();
            }
        });
    }

    private void initView() {
        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);
        btn3 = (Button) findViewById(R.id.btn3);
        setDefaultFragment();
    }

    private void setDefaultFragment() {
        FragmentManager manager = getFragmentManager();
        FragmentTransaction transaction = manager.beginTransaction();

        fragment1 = new Fragment1();
        transaction.replace(R.id.container, fragment1);
        transaction.commit();
    }
}

这个界面非常简单,没有做任何美化,但是足够说明主题。
4. 其中3个Fragment的布局类似,只是一个简单的布局,里面显示一个text以示区分。如下所示:

<RelativeLayout 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">

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

</RelativeLayout>

从中我们可以看到几个使用Fragment时重要的类:

  • FragmentManager:要管理activity中的fragments,你就需要使用FragmentManager。通过getFragmentManager()或getSupportFragmentManager()获得

  • FragmentTransaction:一般用来对当前的Fragment进行管理,包括add,replace,remove.

注意:除非特殊情况,不要在一个Fragment里面直接操作另一个Fragment。

1.2 Fragment中模拟返回栈

我们在以上示例中的连续点击Button fragment1,2,3之后,再点击Back key会发现直接退出程序,而没有像Activity一样先退回至第一个fragment,那么有没有办法使用回退栈呢?答案是肯定的。
只需要在replace之后,加上

transaction.addToBackStack(null);

然后,再commit即可。具体效果大家可以自己试试。

如此一来,我们只需要根据业务和操作流程,将适当的fragment添加到返回栈中。

2、Fragment的生命周期

和Activity一样,活动也有它自己的生命周期,Fragment类中提供了一系列的回调方法,已覆盖Fragment的生命周期的每个环节。我们可从官网 (http://developer.Android.com/guide/components/fragments.html)看到如下两张图:

Fragment生命周期
图1 Fragment生命周期

Fragment&Activity
图2 Fragment与Activity生命周期对比

从以上两张图中,我们可以总出以下Fragment的生命周期:

Fragment生命周期:

1、 onAttach();//当fragment被添加到activity中时会回调这个方法;只会调用一次

2、 onCreate();//在fragment创建的时候回调这个方法;只会调用一次;

3、 onCreateView();//每次创建都会绘制Fragment的View组件时调用该方法,相当于fragment绑定一个布局,布局文件转换成view返回回来;

4、 onActivityCreated();//fragment所在的activity启动完成后调用;

5、 onStart():启动Fragment时会被回调,

6、 onResume()方法,调用onStart()方法后面一定会调用这个方法,恢复fragment时回调

7、 onPause():暂停Fragment

8、 onStop();//停止Fragment

9、 onDestroyView():销毁Fragment所包含的View组件,与onCreateView相对应

10、 onDestroy():销毁Fragment时会被回调

11、 onDetach():Fragment从Activity中删除时回调这个方法,并且只调用一次

几种常见场景下的生命周期:

  • 启动fragment:

onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()

  • 屏幕锁屏 :onPause()->onStop()

  • 屏幕解锁: onStart()->onResume()

  • 第一个fragment切换到第二个fragment:
    第一个onPause()->onStop()->onDestoryView()->onDestory()->onDetach();
    第二个和启动Fragment时一样

可以看出Fragment的生命周期比Activity的还要多,但是它和Activity的生命周期是息息相关的,也很有规律。

3、Fragment参数传递

Fragment&Activity之间的参数传递,无非分为三种:Activity和Fragment互相传参(注意:我们可以认为Activity是通信的主动一方,而Fragment是被动一方,它们之间传参的方式和复杂程度并不一样);还有就是Fragment传参到Fragment。

3.1 Activity->Fragment的传值

从Activity传值到Fragment应该算是最简单的一种了,我们要用到Fragment的setArguments()方法和getArguments()方法通过Bundle对象来传值。简单的示例如下:

<RelativeLayout 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:orientation="vertical"
    tools:context="com.lupu.demofrag.MainActivity"
    tools:ignore="MergeRootFrame" >

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

        <Button
            android:id="@+id/sendBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="点击发送“hello world" >
        </Button>
    </LinearLayout>

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="fragment1" />

        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="fragment2" />

        <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="fragment3" />
    </LinearLayout>

</RelativeLayout>

以上布局如下所示:

这里写图片描述

当我们点击上方的Button时将字符串发送到fragment2中,主要代码如下:

   //在activity中对按钮设置监听发送字符到fragment中
   sendBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "发送到fragment2",
                        Toast.LENGTH_SHORT).show();
                if (fragment2 == null) {
                    fragment2 = new Fragment2();
                }
                Bundle bundle = new Bundle();
                bundle.putString("hello", "hello world from activity");
                fragment2.setArguments(bundle);
            }
        });

接着,我们应当在fragment2中去接收它,怎么接受呢?就是用我们提到过的方法getArguments(),主要代码片段如下所示:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View fragmentView = inflater.inflate(R.layout.fragment_2, container,
                false);
        //初始化显示接收到的字符串的TextView
        tv = (TextView) fragmentView.findViewById(R.id.tvReceive);
        return fragmentView;
    }

    @Override
    public void onStart() {
        super.onStart();
        tv.setText(getArguments().getString("hello")+"!");
    }

运行之后效果如下图:

这里写图片描述

当我们切换到fragment2时可以看到有fragment发过来的数据,用这种方法我们就可以很方便的发送和接收所有Bundle支持的数据。
ps:从截图发现上方的按钮没有了,但是我们预计的是有的,因为我们在相对布局中没有做位置的限定,所以切换至fragment时消失了,知道原因即可,不多解释。

3.2 Fragment->Activity的传值

Fragment作为被Activity加载的被动一方,要想实现向Activity传值通信,要解决以下几个问题:

  1. Fragment要知道是哪个Activity加载了它通过onAttach()方法可以让Fragment获取Activity的信息

  2. 在Fragment中创建一个Activity的对象,通过这个对象去调用Activity中的方法

  3. Activity中的这个方法要满足如下条件:

    1、Fragment要传送给Activity的数据可以作为这个方法的参数
    2、这个方法是确定存在的
    3、Fragment知道Activity中有这个方法

通过接口回调可以完美的解决这几个问题。是不是有点抽象?下面我们用一个例子来理解一下就好了。

首先,我们要在fragment里面定义一个接口,并在它的宿主activity里实现它:

//fragment类中定义接口
public interface IHello {
        public void sayHello(String hello);
    }

//其宿主activity实现此接口
public class MainActivity extends Activity implements IHello {
    ...
    @Override
    public void sayHello(String hello) {
        Toast.makeText(getApplicationContext(), "hello from fragment2",
                Toast.LENGTH_SHORT).show();
        tv.setText("hello from fragment2");
    }
    ...
}

其次,在fragment的onAttach方法中,根据获取到的activity来得到接口对象,并在适当的地方调用(如:onStart方法中)

    @Override
    public void onAttach(Activity activity) {
        helloListener = (IHello) activity;
        super.onAttach(activity);
    }

    @Override
    public void onStart() {
        super.onStart();
        sendInfoBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                helloListener.sayHello("hello from fragment2");
            }
        });
    }

这里写图片描述

我们在中间fragment2中加了一个button点击之后触发回调,在上方加了一个TextView来显示传过去的值(当然也可以换为其他任何操作),界面很简单就不贴出代码了。

点击之后,出现如下界面,可以证实完成了我们想要的功能。

这里写图片描述

请注意:这里为了简单演示,我们只传入了一个固定的字符串,确认可以完成消息传递。你可以将参数换为任何你需要的内容,来实现更丰富的功能!

以上方式可以认为是动态传递数据的,有动态的当然就有静态的,静态的方式主要就是用到了自定义的getXXX()和setXXX方法(),它同样可以完成一些传递数据的要求,但是功能有限,有兴趣的可以试试。墙裂推荐使用接口回调的方法来完成数据传递。

3.3 Fragment->Fragment的传值

所有的Fragment间的通信都是通过跟关联的Activity来完成的。另个Fragment不应该直接通信。也就是说Fragment间不直接通信,通过Activity转一下,按Java常规,转一下多是使用Interface实现的。可以分为以下两步完成:

  1. Fragment->Activity:用上一节讲到的方法即可

  2. Activity->otherFragment:使用findFragmentById()方法捕获Fragment实例,宿主Activity可以把消息发送给该Fragment,然后直接调用该Fragment的公共方法。

这两步其实用到了上面讲到的东西,只是稍加变通而已,我相信如果一步步扎实的从开头看到现在应该可以完成它。

上述已经介绍完成了Fragment常用的基本方法,相信看完,大家一定清楚了Fragment的简单使用,这一部分就到此为止吧。更高级深入的知识点还有很多呢,后续会继续深入的介绍常用的东西。stay hungry, stay foolish,真的hungry了,O(∩_∩)O哈哈~。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值