面试之Fragment总结

Fragment总结

Fragment用法

Fragment从 Android 3.0后引入,用来替代Activity实现模块化,可以适配不同屏幕大小的手机或者平板或电视。但是Fragment不能独立存在,必须嵌入到Activity,Fragment具有自己的生命周期,接收它自己的事件,可以在Activity运行时被添加或删除。

  • 静态使用Fragment
    这是最简单的使用方式,将Fragment当成一个控件,直接在Activity的布局文件绑定。

    首先需要一个类来继承Fragment,重写onCreateView方法来指定Fragment的布局。

    public class Fragment1 extends Fragment {
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment1, container, false);
            return view;
        }
    }
    

    在这里面可以像Activity处理UI、逻辑一样处理Fragment的UI、逻辑,接下来通过fragment来绑定Activity,在Activity的布局里添加并指定一个Fragment。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <fragment
            android:id="@+id/fragment1"
            android:name="com.example.a14512.activitydemo.Fragment1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
    

    当系统创建此 Activity 布局时,会实例化在布局中指定的每个Fragment,并为每个Fragment调用 onCreateView() 方法,以检索每个Fragment的布局。系统会直接插入Fragment返回的 View 来替代 <fragment> 元素。

  • 动态加载Fragment
    除了可以在Activity的布局指定外,还可以通过代码来实现动态添加、更新、删除等操作。

    首先Fragment的创建都不变,只是在Activity的布局有一点点改变。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <FrameLayout
            android:id="@+id/frameLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
    

    这里只是将FrameLayout代替了fragment标签,接着我们在Activity来实例化并动态操作Fragment。

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            LogUtil.d("onCreate");
            setContentView(R.layout.activity_main);
            //获取FragmentManager
            FragmentManager fragmentManager = getSupportFragmentManager();
            //获取FragmentTransaction来更改,保证一些列Fragment操作的原子性
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            Fragment1 fragment1 = new Fragment1();
            //第一个参数是ViewGroup用来放置Fragment的位置,第二个参数是要放置的fragment
            transaction.add(R.id.frameLayout, fragment1);
            //提交,让更改生效
            transaction.commit();
        }
    }
    

    这就是刚开始加载Fragment的动态加载,如果我们需要替换另一个Fragment到这个布局容器中,同样也是获取FragmentManager、FragmentTransaction进行替换、删除等操作,最后通过FragmentTransaction来提交更改使之生效。

    FragmentTransaction常用的操作有如下:

    方法名 含义
    add() 向一个Activity中添加一个Fragment
    remove() 从Activity中移除一个Fragment
    replace() 使用另一个Fragment替换当前的Fragment
    hide() 隐藏当前的Fragment,不会调用生命周期的回调方法
    show() 显示之前隐藏的Fragment,不会调用生命周期的回调方法
    detach() 会将View从UI中移除,detach之后可以调用attach恢复
    attach() 重建view试图,附加到UI上展示

    Fragment回退栈:
    可以利用Fragment的回退栈(类似于Activity栈)来保存Fragment的状态,在back后回到之前的Fragment。
    transaction.addToBackStack(String name);在commit之前,将这次的transaction添加到回退栈,这样就可以保存这次transaction。

与ViewPager合用

Fragment的使用就像上面那样简单,但是这并不能满足我们的需求。当我们想让Fragment滑动起来,这就需要和ViewPager一起使用了。

在Activity的布局中添加ViewPager。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

这就相当于一个View容器,接着在Activity实例化。

//Fragment的适配器,注意FragmentPagerAdapter 和 FragmentStatePagerAdapter 的区别
public class FragmentAdapter extends FragmentPagerAdapter {
    private ArrayList<Fragment> mFragments = new ArrayList<>();

    public FragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragments) {
        super(fm);
        this.mFragments = fragments;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }
}

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LogUtil.d("onCreate");
        setContentView(R.layout.activity_main);
        ViewPager viewPager = findViewById(R.id.viewPager);
        ArrayList<Fragment> fragments = new ArrayList<>();
        Fragment1 fragment1 = new Fragment1();
        Fragment2 fragment2 = new Fragment2();
        Fragment3 fragment3 = new Fragment3();
        fragments.add(fragment1);
        fragments.add(fragment2);
        fragments.add(fragment3);
        FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager(), fragments);
        viewPager.setAdapter(adapter);
        viewPager.setCurrentItem(0);
        //预加载
        viewPager.setOffscreenPageLimit(4);
    }
}

就这样简单就实现了Fragment和ViewPager的联合使用。

Fragment与其他组件的通信

  • Fragment和Fragment
  1. Fragment是同一层级的,Fragment都处于Activity中,没有嵌套
    (1)直接在一个Fragment中调用另一个Fragment的方法(这种方法对于Fragment有着一定的要求):
    这种就是通过获取到另一个Fragment的实例,就可以直接操作另一个Fragment的方法或属性。一般是通过getActivity().getFragmentManager()来获取FragmentManager,这样就可以通过findFragmentById或者findFragmentByTag方法来获取Fragment的实例,这就跟Activity于Fragment的通信一样,因为在Fragment中可以获取到所依赖的Activity。
    (2)接口回调,原理跟Activity和Activity的通信差不多:
    (3)广播:
  2. Fragment不是同一层级的,Fragment嵌套Fragment
    在宿主Fragment中可以通过getChildFragmentManager来获取FragmentManager类管理子Fragment
    对于子Fragment来说,可以通过getParentFragment() 来获取宿主的FragmentManager
  • Fragment和Activity
  1. 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
  2. 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
  3. 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作,直接调用Activity中的public方法
  4. Hanlder
  5. EventBus
  6. 广播
  7. getArguments获取Activity传递的参数

Fragment的生命周期

  • onAttach()
    关联Activity时调用
  • onCreate()
    创建Fragment时调用,在这里必须初始化Fragment的基础组件
  • onCreateView()
    Fragment要绘制自己的界面时调用,这个方法必须返回Fragment的layout,也可以返回null(表示没有界面)
  • onActivityCreated()
    当Activity对象完成自己的onCreate方法时调用
  • onStart()
    Fragment的UI可见时调用
  • onResume()
    Fragment的UI可交互时调用
  • onPause()
    Fragment 可见但不可交互时调用
  • onStop()
    Fragment 完全不可见时调用
  • onDestroyView()
    Fragment 移除视图时调用
  • onDestroy()
    清理View资源时调用
  • onDetach()
    失去Activity关联时调用

Fragment和Activity生命周期关系

在这里插入图片描述
首先加载Fragment,看看生命周期。
image.png
Fragment在宿主onCreate后相应的生命周期就开始执行了,跟一个View有点类似,在宿主onResume后才调用onResume。

如果是通过按钮来添加就有所区别,因为这时Activity已经完成了onResume的调用。
image.png
先是宿主Activity的onCreate、onStart、onResume调用,接着加载Fragment,onAttach、onCreate、onCreateView、onActivityCreated、onStart、onResume依次调用。

跳转到一个新的活动。
image.png
当在Fragment中启动一个新的活动的时候,先是Fragment的onPause调用,接着是宿主Activity的onPause调用,然后就是新活动的生命周期,当Fragment和宿主Activity完全不可见后,就先调用Fragment的onStop,接着是宿主Activity的onStop。

如果新启动的Activity是透明主题或者是一些其他的view不能遮挡Fragment,那么就不会执行onStop。
image.png
这个跟Activity被遮挡类似,不会有onStop调用。

接着按下back键结束活动。
image.png
当结束一个活动后,发现是宿主Activity先onRestart,然后再是Fragment的onStart,接着宿主Activity的onStart、onResume调用,最后才是Fragment的onResume调用(这里就可以看出Fragment是依赖于宿主Activity的,只有当Activity完全可见的时候,Fragment的onResume才会调用)。

再back键结束宿主活动。
image.png
结束宿主活动时,先是Fragment的onPause调用,接着是宿主Activity的onPause,Fragment的onStop,宿主的onStop,然后Fragment的onDestroyView、onDestroy、onDetach,最后是宿主活动的onDestroy。

试试replace后的生命周期。

replace对Fragment的生命周期有影响,就是Fragment结束的生命周期。

hide之前。
image.png
hide后的生命周期。
image.png
show后的生命周期。
image.png
发现,不管是hide还是show,都不会影响Fragment的生命周期。

remove后的生命周期。
image.png
当remove的时候,就相当于将Fragment从宿主Activity移除,所以就会结束Fragment。

异常情况的Fragment生命周期

例如当横竖屏切换时
image.png
Fragment依次调用onPause、onSaveInstanceState、onStop、onDestoryView、onDestroy、onDetach,销毁时候的生命周期调用Fragment总是先于宿主Activity;接着重新构建的时候,却是Fragment先调用onAttach、onCreate,然后才是宿主Activity的onCreate,再然后又是Fragment的onCreateView、onActivityCreated、onViewStateRestored、onStart,接着宿主Activity的onStart、onRestoreInstanceState、onResume,最后时Fragment的onResume。可以看出,销毁时先保存Fragment、先销毁Fragment,重建时,先onCreateFragment,然后在宿主Activity onCreate之后就先恢复Fragment,当宿主resume后,Fragment才resume直接展示了。

相关面试题

  1. fragment 各种情况下的生命周期
    切换到Fragment(第一次)

    onAttach
    onCreate
    onCreateView
    onActivityCreated
    onStart
    onResume

    屏幕熄灭

    onPause
    onSaveInstanceState
    onStop

    屏幕解锁

    onStart
    onResume

    切换到其他Fragment

    onPause
    onStop
    onDestroyView

    切换回本身

    onCreateView
    onActivityCreated
    onStart
    onResume

    回到桌面

    onPause
    onSaveInstanceState
    onStop

    回到应用

    onStart
    onResume

    退出应用

    onPause
    onStop
    onDestroyView
    onDestroy
    onDetach

  2. 遇到过哪些关于Fragment的问题,如何处理的?
    Fragment嵌套Fragment时通信的问题,还有就是一些控制操作的问题

    在宿主Fragment中可以通过getChildFragmentManager来获取FragmentManager类管理子Fragment
    对于子Fragment来说,可以通过getParentFragment() 来获取宿主的FragmentManager

  3. 如何实现Fragment的滑动
    ViewPager的联合使用,通过ViewPager来操作

  4. ViewPager使用细节,如何设置成每次只初始化当前的Fragment,其他的不初始化
    viewPager有一个setOffscreenPageLimit(int);默认值是1,这个就是每次左右Fragment的初始化个数

  5. fragment之间传递数据的方式?
    重复问题

  6. Fragment的生命周期,栈管理,和commit()类似的方法还有哪个
    Fragment笔记整理

  7. Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用,如果在Adapter中使用应该如何解耦

  8. Fragment的懒加载:
    在Fragment中有一个setUserVisibleHint这个方法,而且这个方法是优于onCreate()方法的,所以也可以作为Fragment的一个生命周期来看待,它会通过isVisibleToUser告诉我们当前Fragment我们是否可见,getUserVisibleHint(),它就是用来判断当前Fragment是否可见

  9. Fragment如何去调用Activity的方法
    getActivity、getContext

  10. 如何切换 fragement,不重新实例化
    适用Fragment适配器,和ViewPager联合适用,基本上都是提前实例化好,切换时使用

  11. Fragment与Fragment、Activity通信的方式
    Fragment与Fragment:
    (1)在宿主Fragment中可以通过getChildFragmentManager来获取FragmentManager类管理子Fragment
    (2)对于子Fragment来说,可以通过getParentFragment() 来获取宿主的FragmentManager
    (3)如果能直接获取对应的实例可以直接通信
    (4)接口,同样适用和Activity
    (5)广播,同样适用和Activity
    (6)EventBus

    Fragment与Activity:
    和前面Fragment之间有些重复
    不同的就是在Fragment中可以通过getActivity来获取Activity的实例

  12. Activity与Fragment之间生命周期比较
    如前面的图

相关参考

Fragment 用法总结(一)
Fragment 用法总结(二)
Android Fragment 真正的完全解析(下)
Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误
Fragment生命周期详解

发布了52 篇原创文章 · 获赞 10 · 访问量 7031

猜你喜欢

转载自blog.csdn.net/baidu_36959886/article/details/105484061