Android学习之Fragment

fragment是activity中用户界面的一个行为或者是一部分。我们可以在一个单独的activity上把多个fragment组合成为一个多区域的UI,并且可以在多个activity中再使用。我们可以认为fragment是activity的一个模块零件,它有自己的生命周期,接受它自己的输入事件,并且可以在activity运行的时候添加或者删除。

Fragment必须总是被嵌入一个activity中,并且fragment的生命周期直接受寄主activity的影响。例如,一旦activity被暂停,它里面所有的fragment也被暂停,一旦activity被销毁,它里面的所有的fragment也被销毁。然而,当activity正在运行的时候,我们可以单独操作每个fragment,比如添加或者删除。当我们执行这样一项事物的时候,可以将它添加到后台的一个栈中,这个栈由activity管理着—–activity里面的每个后台栈内容实体是fragment发生过的一条事务记录。这个后台栈允许用户通过BACK键回退一项fragment事物。

当我们添加一个fragment座位某个activity布局的一部分的时候,它就存在于这个activity视图体系内部的ViewGroup之中,并且定义了它自己的视图布局。我们可以通过activity布局文件中声明fragment,用
<fragment>元素把fragment插入到activity的布局中,或者是用应用程序源码将它添加到一个存在的ViewGroup中。然而,fragment并不是一个定要作为activity布局的一部分,fragment也可以为activity隐身工作。

创建Fragment

这里写图片描述

要创建一个fragment,必须创建一个fragment的子类(或者是继承它的子类)。fragment类的代码看起来很像activity。它与activity一样都有回调函数。事实上,如果我们正在讲一个现成的Android应用转而使用fragment来实现,可以简单的将代码从activity的回调函数移植到各自的fragment回调函数中。

一般情况下,我们至少需要实现以下几个生命周期的方法:
onCreate():
在创建fragment时系统会调用此方法。在实现代码中,我们可以初始化想要在fragment中保持的那些必要组件,当fragment处于暂停或停止状态之后可重新启用它们。

onCreateView():
在第一次为fragment绘制用户界面的时候系统会调用此方法。为fragment绘制用户界面,这个函数必须要返回所绘制出的fragment的根view。如果没有用户界面可以返回空

onPause():
系统回调用该函数作为用户离开fragment的第一个预兆(尽管这并不总意味着fragment被销毁)。在当前用户会话结束之前,通常要在这里提交任何应该持久化的变化。


除了基类fragment,还有:
DialogFragment:
显示一个浮动的对话框。使用这个类创建对话框是使用activity类对话框帮助方法之外的另一个选择。
因为我们可以把fragment对话框并入到由activity管理的fragments后台栈中,允许用户返回一个已经摒弃的fragment

ListFragment:
显示一个由适配器管理的条目列表,类似于ListActivity。并且提供了许多管理列表视图的函数

PreferenceFragment:
显示一个preference对象的体系结构列表,类似于preferenceActivity。这在为应用程序创建“设置”activity时是很实用的

添加用户界面

fragment常被用作activity用户界面的一部分,并且将本身的布局构建到activity中去。
为了给fragment提供一个布局,你必须实现onCreateView()回调函数,在绘制fragment布局时Android系统会调用它。实现这个函数时需要返回fragment所属的根View。

注意:如果你的fragment时ListFragment的子类,默认实现从onCreateView()返回一个ListView,所以你不需要实现它。

为了从onCreateView()返回一个布局,我们可以从layout resource定义的XML文件inflate它

public static class ExampleFragment extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.example_fragment, container, false);
        }
    }

传入onCreateView()的参数container是fragment布局要被插入的父ViewGroup(来自activity的布局)。如果fragment处于resumed状态,参数saveInstanceState是属于Bundle类,它提供了fragment之前实例的相关数据
inflate()函数需要以下三个参数:

  • 要inflate的布局的资源ID
  • 要inflate的布局父ViewGroup。传入container很重要,这是为了让系统将布局参数应用到被inflate的布局的根view中去,将其将要嵌入的父view指定
  • 一个布尔值,表明在inflate期间的布局是否应该附上ViewGroup(第二个参数)。(在这个例子中传入的是false,因为系统已经将被inflate的布局插入到容器中(container)——传入true会在最终的布局里创建一个多余的ViewGroup。)
将fragment添加到activity中

通常,fragment构建了其宿主activity的部分界面,它被作为activity全部视图体系的一部分被嵌入进去。
在activity布局中添加fragment有两种方法:

  • 在activity的布局文件里声明fragment
<?xml version="1.0" encoding="utf-8"?>
    <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:name="com.example.news.ArticleListFragment"
                android:id="@+id/list"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
        <fragment android:name="com.example.news.ArticleReaderFragment"
                android:id="@+id/viewer"
                android:layout_weight="2"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
    </LinearLayout>

当系统创建activity布局的时候,它实例化了布局文件中指定的每一个fragment,并为它们调用onCreateView(),以获取每一个fragment的布局。系统直接在<fragment>元素的位置插入fragment返回的View。

注意:每个fragment都需要一个唯一的标识,如果重启activity,系统可用来恢复fragment(并且可用来捕捉fragment的事物处理,例如移除)。为fragment提供ID有三种方法:
用android:id属性提供一个唯一的标识
用android:tag属性提供一个唯一的字符串
如果上述两个属性都没有,系统会使用其容器视图(view)的ID

  • 或者,通过编码将fragment添加到已存在的ViewGroup中
    在activity运行的任何时候,我们都可以将fragment添加到activity布局中,我们仅需要简单指定用来放置fragment的ViewGroup。
    我们应当使用fragmenttransaction的API来对activity中的fragment进行操作:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

可以用replace()方法添加fragment,并指定要添加的fragment以及要将其插入到哪个视图(view)中:

ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.replace(R.id.fragment_container, fragment);
    fragmentTransaction.commit();

传入replace()函数的第一个参数是fragment被放置的ViewGroup,它由资源ID(resource ID)指定,第二个参数就是要添加的fragment。

一旦通过FragmentTransaction 做了更改,都应当使用commit()使变化生效。

添加无界面的fragment

想要添加没有界面的fragment ,可以使用add(Fragment, String)(为fragment提供一个唯一的字符串“tag”,而不是视图(view)ID)。这样添加了fragment,但是,因为还没有关联到activity布局中的视图(view) ,收不到onCreateView()的调用。所以不需要实现这个方法。

为无界面fragment提供字符串标签并不是专门针对无界面fragment的——也可以为有界面fragment提供字符串标签——但是对于无界面fragment,字符串标签是唯一识别它的方法。如果之后想从activity中取到fragment,需要使用findFragmentByTag()。

管理Fragments

想要管理activity中的fragment,可以使用FragmentManager。可以通过在activity中调用getSupportFragmentManager()获得
使用fragmentmanager可以做如下的事情:

  • 使用findFragmentById()用于在activity布局中提供有界面的fragment,或者findFragmentByag()获取activity中存在的fragment(用于有界面或者没有界面的fragment)
  • 使用popBackStack()(模仿用户的BACK命令)从后台弹出fragment
  • 使用addOnBackStackChangedListener()注册一个监听后台栈变化的监听器
处理fragment事务

在activity中使用fragment的一大特点是具有添加,删除,替换,和执行其他动作的能力,以响应用户的互动。提交给activity的每一系列的变化成为事务,并且可以用fragmenttransaction中的APIs处理。你也可以将每一个事务保存在由activity管理的后台栈中,并且允许用户导航返回fragment变更。

每项事务是在同一时间内要执行的一系列的变更。我们可以为一个给定的事务用相关方法设置想要执行的所有变化。例如add(),remove(),和replace()。然后用commit将事务提交给activity。

然而,在调用commit()之前,为了将事务添加到fragment事务后台栈中,我们会调用addToBackStatck()。这个后台栈由activity管理,并且允许用户用过BACK键回退到前一个fragment状态

 // Create new fragment and transaction
 Fragment newFragment = new ExampleFragment();
 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

 // Replace whatever is in the fragment_container view with this fragment,
 // and add the transaction to the back stack
 transaction.replace(R.id.fragment_container, newFragment);
 transaction.addToBackStack(null);

 // Commit the transaction
 transaction.commit();

在这个例子中,newFragment替换了当前在布局容器中用R.Id.fragment_container标识的所有的fragment,替代的事务被保存在后台栈总,因此用户可以回退该事务,可通过按BACK键还原之前的fragment。

将变更添加到fragmenttransaction中的顺序注意以下两点:

  • 必须要在最后调用commit
  • 如果我们将多个fragment添加到同一个容器中,那么添加顺序决定了它们在视图层次里显示的顺序。

在执行删除fragment事务时,如果没有调用addToBackStack(),那么事务一提交fragment就会被销毁,而且用户也无法回退它。然而,当移除一个fragment时,如果调用了addToBackStack(),那么之后fragment会被停止,如果用户回退,它将恢复过来。

提示:对于每一个fragment事务,在提交之前通过调用setTransaction()来应用一系列事务动作

调用commit()并不立刻执行事务,相反,而是采取预约方式,一旦activity的界面线程准备好便可以运行起来。然而,如果有必要的话,我们可以从界面调用executePendingTransaction()立即执行由commit()提交的事务。

注意,一个transaction只能执行一次commit,如果想第二次提交,就需要重新new一个transaction

与Activity交互

尽管Fragment被视为一个对象,它独立于activity并且可以在多个activity中使用,一个给定的fragment实例直接被捆绑在包含它的activity中
特别是,fragment可以通过getActivity()函数访问activity,并且很容易的执行类似于查找activity布局中的视图的任务:

 View listView = getActivity().findViewById(R.id.list);

同样的,activity能够调用fragment的函数findFragmentById()或者findFragmentByTag(),从FragmentManager中获取Fragment的索引

 ExampleFragment fragment = (ExampleFragment) getSupportFragmentManager().findFragmentById(R.id.example_fragment);
创建activity事件回调函数

在一些情况下,我们可能需要fragment与activity共享事件。这样做的一个好方法是在fragment内部定义一个回调接口,并需要宿主activity实现它。当activity通过接口接受回调的时候,可以在必要的时候与布局中的其他fragment共享信息。
举个例子:
如果新闻应用的activity中有两个fragment:一个显示文章列表(A),一个显示一篇文章(B),然后A必须告诉activity列表何时被选中,这样,activity可以通知B去显示这篇文章。这种情况下,在fragment A内部声明接口OnArticleSelectedListener:

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

然后fragment的宿主activity实现了OnArticleSelectedListener接口,并且重写OnArticleSelected()以通知fragment B来自于fragment A的事件。为了确保宿主activity实现了这个接口,fragment A的onAttach()回调函数(当添加fragment到activity中时系统会调用它)通过作为参数传入onAttach()的activity的类型转换来实例化一个OnArticleSelectedListener实例

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

如果activity没事实现这个接口,那么fragment会抛出一个ClassCaseException异常。一旦成功,mListener成员会保留一个activity的OnArticleSelectedListener实例的引用,由此fragment A可以通过调用由OnArticleSelectedListener接口定义的方法与activity共享事件。例如,如果fragment A是listFragment的子类,每次用户点击列表项的时候,系统都会调用fragment的onListenerClick()事件,然后fragment调用onArticleSelected()来与activity共享事件。

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}
添加items到Action Bar

fragment可以通过实现onCreateOptionsMenu()来构建菜单项到activity的Options Mune,为了使用这个方法接受到调用,不管怎么,我们必须在onCreate()期间调用setHasOptionsMenu(),来指明想要添加项目到Options Menu的那个fragment(否则,fragment将接收不到onCreateOptionsMenu()的调用)。

任何想要在fragment中的Options Menu添加的项目都追加到已有的菜单项后面。当菜单被选中的时候,fragment也会接受到对onOptionsItemSelected()的回调。

我们也可以通过调用registerForContextMenu()在fragment布局中注册一个view以提供一个context menu。当用户打开context menu的时候,fragment接受到对onCreateContextMenu()的回调。当用户选中一个项目的时,fragment接收到onContextItemSelected()的回调

注意:尽管你的fragment会接收到为添加到每个菜单项被选择菜单项的回调,但当用户选择一个菜单项时,activity会首先接收到对应的回调。如果activity的选择菜单项回调的实现没有处理被选中的项目,那么该事件被传递给fragment的回调。这同样适用于Options Menu和context menu。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Fragment fragment = new oneFragment();
        final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.add(R.id.container,new oneFragment());
        transaction.add(R.id.container,new oneFragment());
        transaction.add(R.id.container,new twoFragment());
        transaction.add(R.id.container,new twoFragment());
        transaction.commit();
        Button button = (Button)findViewById(R.id.change);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Fragment fragment1 = new twoFragment();
                FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
                transaction1.replace(R.id.container,fragment1);
                transaction1.addToBackStack(null);
                transaction1.commit();
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        menu.add("aaaaa");
        return super.onCreateOptionsMenu(menu);

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {


        return super.onOptionsItemSelected(item);
    }
}

在main中添加menu

public class oneFragment extends Fragment{
    TextView textView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.onefragment,container,false);
        textView = (TextView)view.findViewById(R.id.fragment_text);
        return view;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        textView.setText("aaaaaaaaaaaaaaa");
        return super.onOptionsItemSelected(item);
    }
}

在fragment中重写onOptionsItemSelected()方法

效果:
这里写图片描述

这里写图片描述

与activity生命周期的协调合作

fragment所生存的activity生命周期直接影响着fragment的生命周期,由此针对activity的每一个生命周期回调都会引发一个fragment类似的回调。例如,当activity接收到onPause()的时候,这个activity之中的每个fragment都会接收到onPause()。

fragment有一些额外的生命周期回调方法,然而,为了处理像是创建和销毁fragment界面,它与activity进行独特的交互。
onAttach():
当fragment被绑定到activity时调用(activity会被传入)

onCreateView():
创建与fragment相关的视图体系的时候会被调用

onActivityCreate():
当activity的onCreate()方法返回的时候调用

onDestroyView():
当于fragment关联的视图体系正在被移除的时候调用

onDetach():
当fragment正与activity解除关联的时候被调用

这里写图片描述

一旦activity处于resumed状态,则可以在activity中自由的添加或者移除fragment。因此,只有当activity处于resumed状态时,fragment的生命周期才可以独立变化。

然而,当activity离开恢复状态时,fragment再一次被activity推入它的生命周期中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值