Fragment基础知识

基础知识

Fragment,简称碎片,是Android 3.0(API 11)提出的,为了兼容低版本,support-v4库中也开发了一套Fragment API,最低兼容Android 1.6。

过去support-v4库是一个jar包,24.2.0版本开始,将support-v4库模块化为多个jar包,包含:support-fragment, support-ui, support-media-compat等,这么做是为了减少APK包大小,你需要用哪个模块就引入哪个模块。

如果想引入整个support-v4库,则compile ‘com.android.support:support-v4:24.2.1’,如果只想引入support-fragment库,则com.android.support:support-fragment:24.2.1。

不管是v4支持包还是v7支持包都是为了将一些较为先进的app设计理念向后兼容,一般来说,v4支持包的版本和v7支持包的版本与编译sdk或者目标sdk一致,比如我的编译版本(即API level)是27,那么最好使用版本号以27开头的v4或v7包.

推荐使用支持包中的fragment,兼容性更强.

fragment的特点与优势

特点

  • Fragment是依赖于Activity的,不能独立存在的。
  • 一个Activity里可以有多个Fragment。
  • 一个Fragment可以被多个Activity重用。
  • Fragment有自己的生命周期,并能接收输入事件。
  • 我们能在Activity运行时动态地添加或删除Fragment。

优势

  • 模块化(Modularity):我们不必把所有代码全部写在Activity中,而是把代码写在各自的Fragment中。

  • 可重用(Reusability):多个Activity可以重用一个Fragment。

  • 可适配(Adaptability):根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,这样用户体验更好

fragment核心的API

  • Fragment:Fragment的基类,任何创建的Fragment都需要继承该类。
  • FragmentManager:管理和维护Fragment。他是抽象类,具体的实现类是- FragmentManagerImpl。
  • FragmentTransaction:对Fragment的添加、删除等操作都需要通过事务方式进行。他是抽象类,具体的实现类是BackStackRecord。
  • Nested Fragment(Fragment内部嵌套Fragment的能力)是Android 4.2提出的,support-fragment库可以兼容到1.6。通过getChildFragmentManager()能够获得管理子Fragment的FragmentManager,在子Fragment中可以通过getParentFragment()获得父Fragment。

fragment的生命周期

Fragment基础知识

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用。
  • onCreateView():创建Fragment的布局。 onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

fragment的基本使用

设置参数和解析参数

activity可以通过fragment的setArguments()对关联的fragment设置参数,可以在onAttach()中获得参数,即getArgument()获得参数,getArgument()返回一个Bundle,解析这个Bundle即可.

It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated.

如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据

fragment的容器

Fragment有很多可以复写的方法,其中最常用的就是onCreateView(),该方法返回Fragment的UI布局,需要注意的是inflate()的第三个参数是false,因为在Fragment内部实现中,会把该布局添加到container中,如果设为true,那么就会重复做两次添加,则会抛如下异常:

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

fragment有两种添加方式

  • 静态添加:通过xml的方式添加,缺点是一旦添加就不能在运行时删除。
  • 动态添加:运行时添加,这种方式比较灵活,因此建议使用这种方式。 虽然Fragment能在XML中添加,但是这只是一个语法糖而已,Fragment并不是一个View,而是和Activity同一层次的。

运行时添加需要一个容器,这个容器一般是framelayout,至于为什么用framelayout我就不知道了.

代码

fragment

package me.tinuv.fragmenttest;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyFragment extends Fragment {
    private MainActivity mActivity;


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mActivity = (MainActivity) context;
        Log.e("Tinuv", getArguments().getString("key"));
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_my, container, false);
    }

    public static MyFragment newInstance(Bundle param) {
        MyFragment fragment = new MyFragment();
        fragment.setArguments(param);
        return fragment;
    }
}

activity

package me.tinuv.fragmenttest;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Bundle args = new Bundle();
        args.putString("key", "hello");
        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, MyFragment.newInstance(args), "f")
                .commit();
    }
}

FragmentManager的常用API

  • getFragments():可以获取所有创建时候add进去的所有Fragment;通常可以通过这个api来获取需要指定操作的fragment对象

  • manager.findFragmentByTag(String tag): 通过TAG获取指定的Fragment;这个TAG,是在创建Fragment时,调用addToBackStack(String tag)进行绑定关系的

我也不知道为什么,总是没法获得Fragment,总是空指针

  • popBackStack(): 弹出栈顶fragment

  • popBackStack(String tag,int flags):

tag可以为null或者相对应的tag,flags只有0和1(POP_BACK_STACK_INCLUSIVE)两种情况 如果tag为null,flags为0时,弹出回退栈中最上层的那个fragment。 如果tag为null ,flags为1时,弹出回退栈中所有fragment。 如果tag不为null,那就会找到这个tag所对应的fragment,flags为0时,弹出该 fragment以上的Fragment,如果是1,弹出该fragment(包括该fragment)以 上的fragment。 popBackStackImmediate相关的方法与上面逻辑是一样的与上面不同的是,在调用的时候会立即执行弹出。

FragmentTransaction的常用API

  • add():将一个Fragment实例对象添加到集合列表的尾部,当展示的时候会在activity的最上层
  • remove():将一个Fragment实例对象从存储的集合列表中移除,并且将其从UI界面中销毁
  • replace():将上一个Fragmnt的实例对象从存储的集合列表中移除,将当前的Fragment实例对象添加到存储的链表尾部,当展示的时候会在activity的最上层
  • hide():将一个fragment,从展示状态隐藏起来,实例对象不被销毁
  • show():将一个fragment实例对象,展示出来
  • addToBackStack():将fragment添加到回退栈中

add() 和 replace() 运用总结:

在项目的使用中,通常习惯使用add()加载,add方式视图不会重建,会被保存起来,而replace()每次都会remove掉前面的视图,而replace方式的回退,旧的视图每一次都会重建,在用户体验上不好。

add()和replace()的使用,不能够混合使用,在混合使用的情况下,会导致回退栈混乱,导致的原因是在回退过程中记录的角标存在问题

FragmentTransaction遇到的一个错位.

java.lang.IllegalStateException: commit already called

这个错误是将FragmentTransaction作为了一个变量
如:

    FragmenTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(fragment)
      .commit();
      
    ft.hide(fragment)
      .commit();

就会报这个错误

只要每次commit()之前都beginTransaction()就好了

    getSupportFragmentManager().beginTransaction()
        .add(fragment)
        .commit();
      
    getSupportFragmentManager().beginTransaction()
        .hide(fragment)
        .commit();

就不会报错了

????????正文结束????????