Fragment 基础知识

Fragment 生命周期

创建Fragment

只有通过创建Fragment的子类来获取Fragment实例. Fragment的代码和Activity十分相似. 他也拥有和Activity类似的生命周期方法. 比如 onCreate(), onStart(), onPause()onStop() 等方法. 事实上, 如果将将一个使用Activity的Application转换成使用Fragment只需要Activity中的回调方法中的代码拷贝到Fragment对应的方法中即可.

通常情况下, 以下三个生命周期方法是需要重写的 :

onCreate()
系统会在创建Fragment的时候调用这个方法, 在这个方法中可以初始化Fragment需要的组件.

onCreateView()
当系统第一次准备绘制Fragment界面时会调用这个方法, 为了绘制我们需要的UI我们需要在这个方法中返回布局的根布局. 如果Fragment没有UI则可以返回null.

onPause()
当用户离开Fragment的时候, 系统首先会调用这个方法. 通常情况下并不是Fragment被销毁了. 通常需要在这个方法中保存所有需要保存的数据和状态, 用户有可能不会再返回来.

大部分的Application都需要为每一个Fragment至少重写上面的三个方法, Fragment也有一些其他的方法.

还有一些系统提供的Fragment的子类可以使用.
- DialogFragment
- ListViewFragment
- PreferenceFragment

添加UI

Fragment通常用作Activity UI的一部分. 为了为Fragment提供一个布局文件, 需要重写onCreateView() 回调方法. 在系统绘制Fragment UI 的时候会调用这个方法. 在这个方法中需要返回Fragment布局的根布局.

注意 : 如果使用ListViewFragment默认情况下会返回一个ListView, 因此不需要重写这个方法.

为了方便加载布局资源中的布局文件, onCreateVIew() 方法提供了一个LayoutInflate. 比如下面是一个Fragment的子类加载一个xml布局文件(example_fragment.xml).

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);
    }
}

其中container 参数是Fragment要插入的父布局. 当Fragment resumesavedInstanceState 是一个Bundle 存储是一个实例的数据.

注意 : inflate() 方法的最后一个参数必须是false, 因为系统已经将当前的布局加载到父布局中, 如果这里使用true就会出现重复加的问题.

将Fragment添加到Activity

有两种方式将Fragment添加到Activity的布局当中.

方式一 : 在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>

android:name 属性指定了实例化Fragment的类 . 需要使用Fragment的子类的全类名.当系统创建这个Activity的时候会自动创建布局中指定的所有的Fragment类. 并且自动调用他们的额onCreateView() 方法.

注意 : 为了在Activity重启后回复Fragment, 因此每一Fragment都有一个唯一的ID. 有三种方式提供ID.
- 使用 android:id 属性设置一个唯一的ID.
- 使用 android:tag 属性设置一个唯一的string.
- 如果上面两个都没有设置则系统会默认使用 container 的ID.

方式二 : 使用代码动态将Fragment添加到一个ViewGroup上.

在Activity运行期间(Resumed State)都可以将一个Fragment添加到指定的ViewGroup上.

使用FragmentTransaction相关的API来处理事务. 在Activity中获取方法如下.

FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

然后通过 add() 方法将一个Fragment添加到制定的ViewGroup

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

第一个参数 : 要插入的ViewGroup的资源ID.
第二个参数 : 带插入的Fragment 对象.

只要进行了修改都需要调用commit()方法进行事务的提交.

管理Fragment

为了管理Fragment, 需要在Activity中调用getFragmentManager() 方法获取到FragmentManager对象. 使用FragmentManager可以做如下操作 :

  • 使用findFragmentById() 或者 findFragmentByTag()获取Activity中已经存在的Fragment.
  • 从返回栈中弹出Fragment, 使用popBackStack()方法.
  • 使用addOnBackStackChangedListener()为返回栈注册一个变化监听者.

详细用法参考 FragmentManager 的相关文档.

使用Transactions

在Activity中使用Fragment的最大的好处就是在响应用户操作的时候他可以进行添加、删除、替换以及其他操作. 每一系列的改变都可以都是放在一个Transaction中的. 同时也可以进Translation保存到返回栈中.

可以下面的方式那样开启Translation

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每一个Translation都是一系列我们想要同时进行的操作, 我们可以使用Translation提供的方法比如add()remove()replace()等方法在Translation中建立我们想要进行的所有的操作, 最后通过调用commit()方法使设置生效.

在调用commit()之前可以通过调用addToBackStack()方法将Translation添加到返回栈中.

下面的例子中实现了替换Fragment, 并且将Translation保存到返回栈中.

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().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();

添加操作的顺序是没有关系的, 但是以下两点需要特别注意 :
- 最后必须调用 commit() 方法.
- 如果你添加了多个Fragment到同一个ViewGroup. 此时添加的顺序会对他们在视图层次结构中的位置产生影响.

当你执行一个删除Fragment的Translation的时候如果你没有调用addToBackStack(), 那么当Translation提交后Fragment就会被销毁无法找回. 但是如果你在提交前调用了addToBackStack() 方法那么Fragment是不会被销毁的, 只是出于Stop状态, 可以找回.

Tip : 在你提交Translation之前可以使用setTransition() 方法设置一个动画.

调用了commit() 方法后并不一定立即执行Translation.而是当线程有机会就会将这个Translation调度到UI 线程. 如果真的想立即执行可以调用executePendingTransactions(), 一般情况下没有必要这么做, 除非他是其他线程的一个依赖.

注意 : commit() 方法只能在Activity执行 saving its state(用户离开Activity的时候)之前执行. 否则就会抛出异常, 因为在Activity恢复的过程中commit的Translation会丢失. 在这种情况下可以使用 commitAllowingStateLoss()提交Translation.

Fragment和Activity通信

尽管一个Fragment是一个独立的对象, 可以应用到多个Activity中. 但是一个Fragment对象和他嵌入的Activity是有直接联系的. Fragment可以通过getActivity() 方法获取到Activity并且可以很方便的执行操作, 比如获取Activity 布局中的元素等.

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

同样的Activity也可以通过findFragmentById()findFragmentByTag() 方法获取到指定的Fragment.

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

通信方式 : 创建可以发送给Activity的回调事件

在某些情况下,你需要将fragment发送的事件通知给Activity. 一种不错的做法是在fragment内部定义一个回调接口, 然后让宿主Activity去实现这个接口. 这样当Activity接收到回调的信息后如果有需要可以传递给其他的fragment.

举例 : 比如有一个新闻类的APP有两个Fragment, 一个用来展示新闻列表(FragmentA), 另一个用来展示文章的详细信息(FragmentB). 此时FragmentA就需要通知Activity用户点击了哪一个Item以便Activity告诉FragmentB应该显示哪一个文章. 在这种情况下 回调接口OnArticleSelectedListener 的写法如下 :

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

然后宿主Activity必须实现OnArticleSelectedListener接口, 并且在onArticleSelected()方法中将FragmentA传递过来的URL发送给FragmentB.为了确保宿主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");
        }
    }
    ...
}

如果强制类型转换失败则会抛出 ClassCastException异常. 成功了Fragment就可以持有一个OnArticleSelectedListener实例对象的引用. 以便FragmentA可以通过调用OnArticleSelectedListener中的方法通知宿主Activity触发的事件.

例子 : 一个ListFragment的子类, 每次用户点击Fragment中的列表项的时候都会调用onListItemClick()方法, 此时就可以在这个方法中调用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);
    }
    ...
}

管理Fragment生命周期

Activity生命周期对Fragment声明周期的影响

管理Fragment的生命周期和管理Activity的声明周期非常相似. 像Activity的声明周期一样Fragment的声明周期也存在三个状态.

Resumed
在一个运行的Activity中可见的Fragment.

Paused
宿主Activity失去焦点, 但是还没有完全被隐藏.

Stop
fragment不可见了, 有可能是宿主Activity处于Stop状态, 也有可能是Fragment被从宿主Activity中移除了, 但是有一个前提是fragment被添加到了返回栈中. 处于stop状态的fragment是没有被销毁的所有的状态数据都保存完好. 但是用户不可见, 并且当Activity被销毁时他也就被销毁了.

和Activity一样, 你同样可以使用一个Bundle对象来保存状态数据, 以便在Activity重新创建后恢复Fragment. 可以在fragment的onSaveInstanceState()回调方法中保存数据.然后在回调方法onCreate(), onCreateView(), onActivityCreated()中恢复数据.

Fragment和Activity的生命周期的最大的不同之处是对于返回栈的保存处理
默认情况下Activity 被stop后由系统自动将Activity添加到返回栈中.但是Fragment需要手动调用addToBackStack()方法才可以将remove的Fragment添加到返回栈中.

注意 : 如果你在Fragment的内部需要一个Context对象, 可以通过getActivity()方法获取到. 但是需要注意的是只有在Fragment 添加到Activity中后才会有效, 如果Fragment还没有添加到Activity或者已经从Activity中解除移除则getActivity()方法会返回null.

Fragment和Activity生命周期的协调

Fragment的生命周期中和Activity生命周期中一样的回调方法都是直接联系的比如, 当Activity的onPause()方法被调用, fragment的onPause()方法也会被调用. Fragment也有一些自己独有的方法. 这些方法如下 :

onAttach()
当Fragment和Activity进行绑定时调用. Activity在此时传入Fragment.

onCreateView()
创建和Fragment关联的视图结构时调用.

onActivityCreated()
当Activity的onCreate()方法返回时调用.

onDestroyView()
当和Fragment关联的视图结构销毁时调用.

onDetach()
当fragment和Activity解除关系时调用.

Fragment的一系列的生命周期回调方法都被他的宿主Activity的生命周期回调方法影响着.

一旦Activity处于Resumed状态, 你可以进行自由的添加, 删除, 替换等操作.但是只有Activity处于Resumed的状态下Fragment的生命周期才是自由的. 否则都是受Activity影响的.

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值