当设计你的应用程序支持一个广泛的屏幕尺寸的时候,你能给予有用的屏幕空间,通过在不同的布局配置重用你的Fragment来优化用户体验。
例如,在一个手持设备上,它可能适合一次在一个单面板用户界面中,显示仅仅一个Fragment。相反的,你可能想在一个平板中并排设置Fragment,它拥有更宽的屏幕大小来向用户显示更多的信息。
图1.两个Fragment,针对在不同的屏幕大小同样的Activity,显示在不同的配置。在大屏幕中,Fragment并排显示,但是在手持设备中,在一个时间仅仅一个Fragment适合,所以这个Fragment必须在用户导航的时候替换另一个
FragmentManager类提供了允许你在运行时,为了创建一个动态界面,向一个Activity添加,删除和替换Fragment的方法。
在运行时向Activity中添加Fragment
—————————————————————————————————————————————————————————————
而不是在activity的布局文件中使用<fragment>元素定义Fragment—正如在上一课中所讲述的—你能在这个activity运行时向这个activity中添加一个Fragment。如果你计划在这个activity生命周期内改变fragment,这是必须的。
为了执行如添加或删除一个fragment的事务,你必须使用FragmentManager来创建一个FragmentTranstion,它提供了添加,删除,替换和执行其它fragment事务的API。
如果你的activity运行里面的fragment被移除或替代,你应该在activity的onCreate()方法中添加初始化的fragment。
处理fragment的一个重要原则—尤其你在运行时添加的—是fragment必须在布局中有一个容器View,fragment的布局将驻留在这里。
下面的布局是在上一节课程中一次仅仅显示一个fragment的布局的替代。为了使用其它fragment替换,activity的布局包含一个空的FrameLayout,扮演fragment容器。
注意这个文件名和上一节课程中的布局文件名相同,但是布局目录没有large限定符,所以当设备屏幕比large更小的时候,这个布局被使用,因为屏幕不适合同一时间两个fragment。
res/layout/news_articles.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在你的activity中,使用Support Library API调用getSupportFragmentManager()来获取一个FragmentManager。然后调用beginTransaction()来创建一个FragmentTransaction,并且调用add()方法添加一个fragment。
你可以在activity中使用同一个FragmentTransaction执行多个fragment事务。当你准备好改变,你必须调用commit()。
例如,这里是如何向前面的布局添加一个fragment:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}
因为这个fragment在运行时已经被添加到这个FrameLayout容器中—而不是在activity的布局文件中使用<fragment>元素定义—这个activity可以移除这个fragment和使用另一个替换它。
使其它Fragment替换一个Fragment
—————————————————————————————————————————————————————————————
替换一个fragment的过程和添加一个相似,仅仅使用replace()方法替换add()方法。
记住当你执行fragment事务的时候,例如替换或者移除,通常适当允许用户导航返回并“撤销”改变。为了允许用户通过fragment事务导航返回,你必须在你提交这个FragmentTransaction之前调用addToBackStack()。
注意:当你移除或替换一个fragment,并且添加这个事务到返回栈,被移除的fragment被停止(不是被销毁)。如果用户导航返回恢复这个fragment,它重新启动。如果你没有添加这个事务到返回栈,那么当被移除或者被替换的时候这个fragment被销毁。
使用其它fragment替换一个fragment的例子:
// Create fragment and give it an argument specifying the article it should show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
addToBackStack()方法使用一个可选的字符串参数,它指定这个事务唯一的名字。这个名字是不需要的,除非你计划使用FragmentManager.BackStackEntry API执行高级的fragment操作。