参考
###简介
Android 在 Android 3.0(API 11 级)中引入了片段,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。由于平板电脑的屏幕比手机屏幕大得多,因此可用于组合和交换 UI 组件的空间更大。利用片段实现此类设计时,您无需管理对视图层次结构的复杂更改。 通过将 Activity 布局分成片段,您可以在运行时修改 Activity 的外观,并在由 Activity 管理的返回栈中保留这些更改。
例如,新闻应用可以使用一个片段在左侧显示文章列表,使用另一个片段在右侧显示文章—两个片段并排显示在一个 Activity 中,每个片段都具有自己的一套生命周期回调方法,并各自处理自己的用户输入事件。 因此,用户不需要使用一个 Activity 来选择文章,然后使用另一个 Activity 来阅读文章,而是可以在同一个 Activity 内选择文章并进行阅读,如图 1 中的平板电脑布局所示。
您应该将每个片段都设计为可重复使用的模块化 Activity 组件。也就是说,由于每个片段都会通过各自的生命周期回调来定义其自己的布局和行为,您可以将一个片段加入多个 Activity,因此,您应该采用可复用式设计,避免直接从某个片段直接操纵另一个片段。 这特别重要,因为模块化片段让您可以通过更改片段的组合方式来适应不同的屏幕尺寸。 在设计可同时支持平板电脑和手机的应用时,您可以在不同的布局配置中重复使用您的片段,以根据可用的屏幕空间优化用户体验。 例如,在手机上,如果不能在同一 Activity 内储存多个片段,可能必须利用单独片段来实现单窗格 UI。
###frgment 的生命周期除了 Actvity 的方法, Fragment 多了几个额外的生命周期回调的方法:
-
onAttach(Acivity activity)
当 Fragment 与 Acticity 关联时调用 -
onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该 Fragment 的视图 -
onActivityCreated(Bundle)
当 Activity 的 onCreate() 返回时调用 -
onDestoryView()
与 onCreateView() 对应, 当 fragment 的视图被移除时调用 -
onDetach
与 onAttach() 相对应, 当 fragment 与 Activity 关联取消时调用
###静态使用 Fragment
这种方式最简单, 直接在布局文件中定义.-
继承 fragment 重写 oncreateView(), 返回根布局.
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); } }
-
在布局文件声明 name 属性
###动态的使用 Fragment 如何动态的添加, 更新, 删除 Fragment
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { private AFragment a; private BFragment b; private CFragment c; private DFragment d; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.setDrawerListener(toggle); toggle.syncState(); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); } @Override public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { FragmentManager fm = getSupportFragmentManager(); //开启 fragment 事务 FragmentTransaction ft = fm.beginTransaction(); // Handle navigation view item clicks here. int id = item.getItemId(); if (id == R.id.nav_camera) { // Handle the camera action if (a == null){ a = new AFragment(); } ft.replace(R.id.id_content,a); } else if (id == R.id.nav_gallery) { if (b == null){ b = new BFragment(); } ft.replace(R.id.id_content,b); } else if (id == R.id.nav_slideshow) { if (c == null){ c = new CFragment(); } ft.replace(R.id.id_content,c); } else if (id == R.id.nav_manage) { if (d == null){ d = new DFragment(); } ft.replace(R.id.id_content,d); } else if (id == R.id.nav_share) { } else if (id == R.id.nav_send) { } //事务提交 ft.commit(); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; }}复制代码
这里是使用 FragmentManager 对 Fragment 进行了动态加载. 这里使用了 replace 方法.
注:如果使用Android3.0以下的版本,需要引入v4的包,然后Activity继承FragmentActivity,然后通过getSupportFragmentManager获得FragmentManager。不过还是建议版Menifest文件的uses-sdk的minSdkVersion和targetSdkVersion都改为11以上,这样就不必引入v4包了.__
###常用 api
Fragment 常用的 3 个类:-
android.app.Fragment: 主要用于定义 Fragment
-
android.app.FragmentManager: 主要用于在 Activity 中操作 Fragment
-
android.app.FragmentTransaction: 保证一系列 Fragment 操作的原子性.
a、获取FragmentManage的方式: getFragmentManager() // v4中,getSupportFragmentManager
b、主要的操作都是FragmentTransaction的方法 FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
-
transaction.add()
: 往 Activity 中添加一个 Fragment -
transaction.remove()
: 从 Activity 中移除一个 Fragment, 如果被移除的 Fragment 没有添加到回退栈, 这个 Fragment 的实例会被销毁. -
transaction.hide()
: 隐藏当前的 Fragment, 仅仅是设为不可见, 不会销毁 -
transaction.show()
: 显示之前隐藏的 Fragment -
detach()
: 会将view从UI中移除, 和remove()不同, 此时 fragment 的状态依然由 FragmentManager 维护。 -
attach()
重建 View 视图, 附加到 UI 上并显示
transatcion.commit()//提交一个事务
注意:常用 Fragment 的哥们,可能会经常遇到这样 Activity 状态不一致:State loss 这样的错误。主要是因为:commit 方法一定要在 Activity.onSaveInstance() 之前调用。
上述,基本是操作 Fragment 的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。 值得注意的是:如果你喜欢使用 Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
-
我在 FragmentA 中的 Editext 写了数据, 当切换到 Fragment B 中时, 如果我需要回到 A 时还能看到数据, 那么适合使用 hide() 和 show().
-
如果我不希望保存用户操作, 可以使用 remove(), 然后 add(), 或者使用 replace() 代替.
-
remove() 和 detach() 有一点区别: 不考虑回退栈时, remove() 会销毁整个 Fragment 实例, detach()只是销毁其视图结构, 实例不会被销毁. 如果你的当前 Activity 一直存在,那么在不希望保留用户操作的时候,你可以优先使用 detach。若考虑回栈, 则 remove() 不会销毁实例