关于Fragment,你可能不知道的一切

省略Fragment的生命周期

省略Fragment的静态加载

1.动态添加Fragment的方法
  • 添加有UI的Fragment

    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
    
  • 添加没有UI的Fragment

    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(fragment,"ExampleFragment");
    fragmentTransaction.commit();
    

    由于它并不与 Activity 布局中的视图关联,因此不会收到对 onCreateView() 的调用。因此,不需要实现该方法。对应的可以通过 findFragmentByTag("ExampleFragment")获取Fragment实例。

2.两个Fragment与Activity之间的通信(官方建议)

前提:

  • 在Activity中有两个Fragment,分别时LeftFragment和RightFragment
  • RightFragment会随着LeftFragment的变化而改变(eg:点击左侧Fragment后右侧Fragment加载详情)

实现:

  • 1.在LeftFragment中定义一个回调接口,Activity必须实现这个接口

    public static class LeftFragment extends ListFragment {
        ...
        // Container Activity must implement this interface
        public interface OnArticleSelectedListener {
            public void onArticleSelected(Uri articleUri);
        }
        ...
    }
    
  • 2.宿主Activity必须实现该接口

    public class MainActivity extends Activity implements OnArticleSelectedListener{
        //...省略若干代码
        @Override
        public void onArticleSelected(Uri articleUri){
            
        }
      
    }
    
  • 3.在LeftFragment中获取Activity实现接口的实例

    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");
            }
        }
        ...
    }
    
3.当Activity重启时,使用Fragment保存数据

当Activity的配置发生变化导致重启时,为了提高用户体验,一般需要在Activity销毁的时候保存界面上的一些数据,再次创建的时候恢复数据。

通常的做法是在 onSaveInstanceState()回调时保存有关应用状态数据,然后,可以在 onCreate()onRestoreInstanceState() 期间恢复 Activity 状态。

通过系统回调onSaveInstanceState()方法将数据保存在Boundle,但当数据量较大时,可能无法完全恢复 Activity 状态,因为它并非设计用于携带大型对象(例如位图Bitmap),而且其中的数据必须先序列化,再进行反序列化,这可能会消耗大量内存并使得配置变更速度缓慢。

在这种情况下,如果 Activity 因配置变更而重启,则可通过保留Fragment 来减轻重新初始化 Activity 的负担。

步骤如下:

  • 1.扩展 Fragment 类并声明对有状态对象(即我们要保存的对象)的引用。
  • 2.在创建片段后调用 setRetainInstance(boolean)
  • 3.将片段添加到 Activity。
  • 4.重启 Activity 后,使用 FragmentManager 检索片段。

例如:

定义一个RetainedFragment用来保存数据

public class RetainedFragment extends Fragment {

    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

注意:尽管可以存储任何对象,但是切勿传递与 Activity 绑定的对象,例如,DrawableAdapterView 或其他任何与 Context 关联的对象。否则,它将泄漏原始 Activity 实例的所有视图和资源。 (泄漏资源意味着应用将继续持有这些资源,但是无法对其进行垃圾回收,因此可能会丢失大量内存。)

然后,使用 FragmentManager 将片段添加到 Activity。在运行时配置变更期间再次启动 Activity 时,就可以获得片段中的数据对象。

public class MyActivity extends Activity {

    private RetainedFragment dataFragment;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);

        // create the fragment and data the first time
        if (dataFragment == null) {
            // add the fragment
            dataFragment = new DataFragment();
            fm.beginTransaction().add(dataFragment, “data”).commit();
            // load the data from the web
            dataFragment.setData(loadMyData());
        }

        // the data is available in dataFragment.getData()
        ...
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // store the data in the fragment
        dataFragment.setData(collectMyLoadedData());
    }
}
4.向应用栏添加项目

Fragment可以通过实现 onCreateOptionsMenu() 向 Activity 的选项菜单(并因此向应用栏)贡献菜单项。不过,为了使此方法能够收到调用,必须在 onCreate() 期间调用 setHasOptionsMenu(),以指示Fragment想要向选项菜单添加菜单项(否则,片段将不会收到对 onCreateOptionsMenu() 的调用)。

从Fragment添加到选项菜单的任何菜单项都将追加到现有菜单项之后。 选定菜单项时,Fragment还会收到对 onOptionsItemSelected() 的回调。

Fragment向应用栏添加项目应用十分广泛,例如下面的效果

我们自己也可以动手实现这样的效果,先看效果:

当切换Fragment的时候,应用栏不同的状态,so easy!

  • step1.在Fragment的onCreate回调时,调用Fragment的setHasOptionsMenu(true)方法;

    @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setHasOptionsMenu(true);
        }
    
  • step2.覆盖onCreateOptionsMenu()方法根据不同位置加载不同的应用栏菜单;

    @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            super.onCreateOptionsMenu(menu, inflater);
            switch (mFragmentType) {
                case 0:
                    break;
                case 1:
                    inflater.inflate(R.menu.action, menu);
                    break;
                case 2:
                default:
                    inflater.inflate(R.menu.action0, menu);
                    break;
            }
        }
    
  • step3.如果需要监听应用栏的点击,只需要覆盖onOptionsItemSelected()方法。

    @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_home:
                    Toast.makeText(getContext(), "home", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.navigation_dashboard:
                    Toast.makeText(getContext(), "dashboard",                 Toast.LENGTH_SHORT).show();
                    break;
                case R.id.navigation_notifications:
                    Toast.makeText(getContext(), "notifications",          Toast.LENGTH_SHORT).show();
                    break;
            }
            return super.onOptionsItemSelected(item);
        }
    

另外,还可以通过调用 registerForContextMenu(),在片段布局中注册一个视图来提供上下文菜单。用户打开上下文菜单时,片段会收到对 onCreateContextMenu() 的调用。当用户选择某个菜单项时,片段会收到对 onContextItemSelected() 的调用。

:尽管Fragment会收到与其添加的每个菜单项对应的菜单项选定回调,但当用户选择菜单项时,Activity 会首先收到相应的回调。 如果 Activity 对菜单项选定回调的实现不会处理选定的菜单项,则系统会将事件传递到片段的回调。 这适用于选项菜单和上下文菜单。

你可能感兴趣的:(关于Fragment,你可能不知道的一切)