这篇文章专门用于讲述LevineUtils中的FragmentFactory,需要集成LevineUtils

1.FragmentFactory简介

  • FragmentFactory是利用apt技术,即通过注解的方式来管理整个应用中的自定义的Fragment,通过FragmentFactory对象的showFragment(String tag)方法来控制fragment的显示和隐藏,从而实现了fragment的切换.
  • FragmentFactory同时也对fragment的重影问题给出了解决方案,通过使用saveCurrentFragmentInfo(Bundle bundle)restoreCurrentFragmentInfo(Bundle bundle)方法保存状态和恢复状态.

2.使用FragmentFactory

初始化FragmentFactory

在activity中的onCreate方法中初始化FragmentFactory对象,但是需要注意的是需要继承自FragmentActicity 或者AppCompatActicity,因为在FragmentFactory中使用的是getSupportFragmentManager,所以你的activity必须继承自FragmentActivity或

AppCompatActivity:

public class MainActivity extends AppCompatActivity {    private FragmentFactory mFactory;    @Override    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         //使用单例模式创建`FragmentFactory`对象,R.id.mContentnF1是要显示的Fragment的布局容器的id         mFactory = FragmentFactory.getInstance()                .init(this, R.id.mContentFl);        ....    }     @Override    protected void onResume() {        super.onResume();        //很关键,在activity切换时起作用,如果APP使用单activity,则不需要这句代码        mFactory.onResume(this,R.id.mContentFl);    }}

自定义Fragment

例如我的主界面是这样的布局,里面含有两个tab,用于两个Fragment的切换:

<?xml version="1.0" encoding="utf-8"?><LinearLayout 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"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:orientation="vertical">    <FrameLayout        android:id="@+id/mContentFl"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="0.9" />    <com.google.android.material.tabs.TabLayout        android:layout_gravity="bottom"        android:id="@+id/mTabLayout"        android:layout_weight="0.1"        android:layout_width="match_parent"        android:layout_height="0dp">        <com.google.android.material.tabs.TabItem            android:id="@+id/tab1"            android:text="page1"            android:layout_weight="1"            android:layout_width="0dp"            android:layout_height="match_parent"/>        <com.google.android.material.tabs.TabItem            android:id="@+id/tab2"            android:text="page2"            android:layout_weight="1"            android:layout_width="0dp"            android:layout_height="match_parent"/>    com.google.android.material.tabs.TabLayout>LinearLayout>

自定义两个Fragment用于tab点击时,切换使用(此时要在Fragment的外部使用@TargetFragmentTag(String tag)注解,里面需要接收一个string类型的tag,用于和当前的fragment 一 一对应):

@TargetFragmentTag("fragment1")public class Fragment1 extends Fragment {    .....}@TargetFragmentTag("fragment2")public class Fragment2 extends Fragment {    .....}

注: 相当于Fragment1的tag就是“ fragment1”,Fragment2的tag就是“ fragment2”,但是鉴于方便管理整个应用中所有Fragment的标签,所以建议创建一个类来存贮所有fragment的标签,比如,建立一个接口类FragmentTag:

public interface FragmentTag { String FRAGMENT1="fragment1"; String FRAGMENT2="fragment2"; }

那么我们的代码就可以这样写:

@TargetFragmentTag(FragmentTag.FRAGMENT1)public class Fragment1 extends Fragment { .....}@TargetFragmentTag(FragmentTag.FRAGMENT2)public class Fragment2 extends Fragment { .....}

然后,这样就能统一调度fragment了。

使用FragmentFactory来显示指定的Fragment

在activity中调用showFragment(String tag)来显示fragment:

public class MainActivity extends AppCompatActivity {    private FragmentFactory mFactory;    @Override    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         //使用单例模式创建`FragmentFactory`对象,R.id.mContentnF1是要显示的Fragment的布局容器的id         mFactory = FragmentFactory.getInstance()                .init(this, R.id.mContentFl);        //默认显示fragment1         mFactory.showFragment(FragmentTag.FRAGMENT1)        ....    }}

另外在tab切换时,同时切换Fragment的显示:

 @Override    public void onTabSelected(TabLayout.Tab tab) {        switch (tab.getPosition()) {            case 0:                mFactory.showFragment(FragmentTag.FRAGMENT1);                break;            case 1:                mFactory.showFragment(FragmentTag.FRAGMENT2);                break;        }    }

Fragment在切换时,会自动将当前的Fragment隐藏,但并非销毁掉,下次重新切换回来时,会再将这个Fragment显示出来.其实FragmetnFactory还能实现回退栈的功能:回退到上一个Fragment,同样能使用showFragment方法,只要传入上一个Fragment的tag即可.

跨Activity使用FragmentFactory

在APP中可能有多个Activity,每个Activity中有多个Fragment,所以经常会在全局中使用FragmentFactory(跨Activity),那也是可以的,因为FragmentFactory在初始化时,传入了一个Activity的对象,所以在跨Activity使用时,只需要重新初始化一下即可,各个Activity中使用FragmentFactory是相互独立的.

例如:在另一个Activity中有一个有Fragment3和Fragment4,同样地:

@TargetFragmentTag(FragmentTag.FRAGMENT3)public class Fragment3 extends Fragment {    .....}@TargetFragmentTag(FragmentTag.FRAGMENT4)public class Fragment4 extends Fragment {    .....}public class AnotherActivity extends AppCompatActivity {    private FragmentFactory mFactory;    @Override    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_another);         //使用单例模式创建`FragmentFactory`对象,R.id.mContentnF2是要显示的Fragment的布局容器的id         mFactory = FragmentFactory.getInstance()                .init(this, R.id.mContentF2);        //默认显示fragment3         mFactory.showFragment(FragmentTag.FRAGMENT3)        ....    }         @Override    public void onTabSelected(TabLayout.Tab tab) {        switch (tab.getPosition()) {            case 0:                mFactory.showFragment(FragmentTag.FRAGMENT3);                break;            case 1:                mFactory.showFragment(FragmentTag.FRAGMENT4);                break;        }    }}

3.延伸

Fragment确实能减少Activity的数量,使用灵活方便,但是也会引发一些问题,例如Fragment重影问题.重影问题主要是由于Activity会保存当前视图层级引起的,在某些事件发生后,Activity会调用onSaveInstance方法进行保存状态,在重新打开activity后,Activity会调用onRestoreInstance方法恢复之前保存的状态,再切换到另外的Fragment,就会出现重影.

关于onSaveInstance和onRestoreInstance方法的时机大致是:当activity可能会被回收时,调用onSaveInstance,当activity被回收后,又重新创建一个实例后,会执行onRestoreInstance方法

重影问题主要有几种方式引发:

  • 横竖屏切换

  • 当任务在后台被回收,重新打开页面时

  • 在开发者模式下设置 不保存活动

第一个问题解决方案:

在AndroidManifest.xml中的Activity中添加android:configChanges="keyboardHidden|orientation|screenSize",则可以让Activity在横竖屏切换时,不调用这些生命周期的方法即可.

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.levine.ucall">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true">               <activity            android:name=".ui.activity.MainActivity"            android:configChanges="keyboardHidden|orientation|screenSize">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            intent-filter>        activity>    application>manifest>

另外,其他情况下的解决方案,在FragmentFactory中也给出了解决方案:

  • 1.首先重写Activity的OnSaveInstance和OnRestoreInstance方法

     @SuppressLint("MissingSuperCall") @Override protected void onSaveInstanceState(@NonNull Bundle outState) {//去掉super.onSaveInstanceState(outState),这是重影问题的根源        mFactory.saveCurrentFragmentInfo(outState);    } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) {        if (savedInstanceState != null) {            mFactory.restoreCurrentFragmentInfo(savedInstanceState);        }    }
  • 2.在onCreate中也调用restoreCurrentFragmentInfo方法

      private FragmentFactory mFactory;    @Override    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_another);         //使用单例模式创建`FragmentFactory`对象,R.id.mContentnF2是要显示的Fragment的布局容器的id         mFactory = FragmentFactory.getInstance()                .init(this, R.id.mContentF2);                if (savedInstanceState != null) {            mFactory.restoreCurrentFragmentInfo(savedInstanceState);        } else {           //默认显示fragment1         mFactory.showFragment(FragmentTag.FRAGMENT1)        }        ....    }

这样就解决了重影问题。

补充:虽然重影问题解决了,但是如果想保存Fragment中某些控件状态该怎么办呢?

在Fragment重写onSaveInstanceState和onViewStateRestored方法即可,比如我的Fragment中有一个两个Tab,需要保存tab的位置:

@Override    public void onSaveInstanceState(@NonNull Bundle outState) {        // 保存fragment的状态        super.onSaveInstanceState(outState);        outState.putInt("tabPosition",tabLayout.getSelectedTabPosition());    }    @Override    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {        super.onViewStateRestored(savedInstanceState);        if(savedInstanceState!=null){            //恢复fragment的状态            int position=savedInstanceState.getInt("tabPosition");            tabLayout.getTabAt(position).select();        }    }

更多相关文章

  1. android hal层 c 堆栈打印方法
  2. Android应用程序开发之requestWindowFeature()
  3. Android(安卓)设备 USB 通用调试驱动的安装方法
  4. Android(安卓)EventBus3.1.1从使用到源码解析
  5. Android面试题总结加强版(二)
  6. Android(安卓)图片压缩并保存的方法
  7. Android开发--WebView, WebChromeClient和WebViewClient
  8. Android学习笔记——常用的基本UI组件及其常用属性值和相关方法
  9. Android(安卓)Fragment 没有onRestoreInstanceState

随机推荐

  1. android v7包
  2. drawable—hdpi、drawable—mdpi、drawab
  3. 【进阶】从linux到android,进程的方方面面
  4. Android智能指针SP WP使用方法介绍
  5. Android上运行Http Server
  6. android播放rtsp文件
  7. Android之Intent原理
  8. Android(安卓)如何建立你的菜单
  9. Android中Context用法详解
  10. Android传感器、语音识别、定位系统、Goo