第七章:UI Fragment和Fragment管理器
这一章我们将开发一个CriminalIntent的应用。可以详细记录种种办公室陋习。(需要13章的事件把这个应用做完)
适应用户或设备的需求,activity界面可以在运行时组装,甚至重新组装。
fragment是一种控制器对象,activity可以委派它完成一些任务,这些任务通常就是管理用户界面,受管理的用户界面可以使一整屏幕或者整屏幕的一部分。
根据用户需求,可以联合使用fragment及activity来组装货重新组装用户界面,在整个生命周期过程中,activity视图在技术上保持不变,因此不用担心会违反Android系统的activity使用规则。
左边列表fragment,右边明细fragment。(这样,视图切换的过程,就不用销毁activity了)
fragment本身不具备在屏幕上显示视图的能力。因此,只有将它的视图放置在activity的视图层级结构中,fragment视图才能显示在屏幕上。
Google在API 11级中引入了fragment,以前的开发者要考虑支持的SDK最低版本是API 8级,因此,必须设法保证应用了兼容旧版本设备。幸运的是,Android提供了开发支持库。支持库提供了完整的fragment相关的类实现,最低能兼容支持API 4级。相较于Android系统内置的fragment实现,本书选择使用支持库中的fragment实现。
我们要用到两个重要的支持库类:一个是Fragment类(android.support.v4.app.Fragment),另一个是FragmentActivity(android.support.v4.app.Fragment-Activity),使用fragment的前提是activity知道如何管理fragment,FragmentActivity类知道如何管理支持版本的fragment。
7.3.3 在Android Studio中增加依赖关系。
要使用支持库,项目必须将其列入依赖关系。(每个项目有两个build.gradle,一个用户整个项目,一个用于应用模块)我们要编辑的是app/build.gradle文件。
apply plugin: ‘com.android.application’
android {
…
}
dependencies {
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
}
考虑到依赖项设置指令复杂难记,Android Studio维护了一份常用库列表。File -> Project Structure 菜单项打开项目结构对话框。选择左边的应用模板,然后右边点击dependencies选项页,可以看到应用模板的依赖项都列在这了。
单击+号按钮,选择Library dependency界面添加新的依赖项,在列表中选择中support-v4库后单击OK即可,这样,在app/build.gradle文件就会自动添加了相应的依赖库:
dependencies {
compile fileTree(dir: ‘ilbs’, include: [‘*.jar’])
compile ‘com.android.support:support-v4:22.1.1’
}
如果手动修改了app/build.gradle文件,就需要同步修改内容:Tools -> Andorid -> Sync Project with Gradel Files菜单可手动执行同步。
compile ‘com.android.support:support-v4:22.1.1’ 使用了Maven坐标模式:groupId:artifactId:version
groupId通常是类库的基础包名,如com.android.support
artifactId是包中的特定库名,我们这里指定了support-v4,com.android.suport包中有很多不同的库,如support-v7、appcompat-v7和gridlayout-v7。-vX指所支持的最低API级别,v4表示google兼容库可以应用到Android API 7及以上级别的设备
version:指类库的版本号。如22.1.1的support-v4库
1、将CrimeActivity的超类修改为FragmentActivity,
public class CrimeActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2、创建Crime类(Model层)
public class Crime {
pirvate UUID mId;
private Sting mTitle;
public Crime() {
mId = UUID.randomUUID();
}
// 生成getter和setter方法
// 右键,Generate -> Getter菜单
}
为了托管UI fragment,activity必须做到如下:
1、在布局中为fragment的视图安排位置
2、管理fragment实例的生命周期
fragment的生命周期:
可以看到,许多方法对应着activity的生命周期方法。因为fragment代表activity工作,它的状态应该反映activity的状态,显然,fragment需要相对应的生命周期方法来处理activity的工作。
fragment生命周期与activity生命周期的一个关键区别在于,fragment的生命周期方法是由托管activity而不是操作系统调用。fragment的使用是activity内部的事情。
activity托管UI fragment的两种方式:
1、在activity布局中添加fragment:简单但不够灵活,等同于视图绑定,在activity生命周期过程中,无法切换fragment
2、在activity代码中添加fragment:比较复杂,但是可以在运行时控制fragment
因此,为了追求真正的灵活性UI设计,使用第二种方式。
7.5 创建fragment:
1、通过定义布局文件中的组件,组装界面
2、创建fragment类并设置其视图为定义的布局
3、通过代码的方式,组装在布局文件中实例化的组件。
7.5.1 创建CrimeFragment的布局
fragment_crime.xml
7.5.2 创建CrimeFragment类
记得需要选择集成的类来自com.android.support.v4.app.Fragment
点击Alt+Return可以选中需要导入的库
实现fragment的生命周期方法
public class CrimeFragment extends Fragment {
private Crime mCrime;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime = new Crime();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, container, false);
return v;
// inflater用来传入资源id生成视图
// container是视图的父视图
// false表示是否将生成的视图添加给父视图
}
}
注意:Fragment.onCreate(Bundle)是公共方法,而Activity.onCreate(Bundle)是保护方法。Fragment生命周期方法必须是公共方法,因为托管fragment的activity要调用他们。
fragment的视图没有在onCreate方法中生成,创建和配置fragment视图是另一个fragment生命周期方法完成的:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
该方法实例化fragment视图的布局,然后将实例化的View返回给托管activity。
创建关联组件EditText(在onCreateView方法里面添加EditText)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, container, false);
mTitltField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// this space intentionally left blank
}
@Override
public void onTextChanged(ChaSequence s, int start, int before, int count) {
s代表用户输入
mCrime.setTitle(s.toSting());
}
@Override
public void afterTextChanged(Editable s) {
// this one too
}
});
return v;
}
Fragment.onCreateView方法中的组件几乎等同于Activity.onCreate方法的处理。
7.6 添加UI fragment到FragmentManager
FragmentManager类负责管理fragment并将他们的视图添加到activity的视图层级结构中。
具体管理:1、fragment队列。2、fragment事务回退栈
Activity -> FragmentManager -> 1、fragment队列。2、fragment回退栈
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransation().add(R.id.fragment_container, fragment).commit();
}
fragment事务被用来添加、移除、附加、分离或者替换fragment队列中的fragment。这就是使用fragment在运行时组装和重新组装用户界面的关键。FragmentManager管理着fragment事务回退栈。
重新审视Fragment的生命周期:
activity的FragmentManager负责调度队列中fragment的生命周期方法。
1、添加fragment供FragmentManager管理时,onAttach(Activity),onCreate(Bundle)以及onCreateView(…)方法被调用
2、在activity处于运行状态时,添加fragment会发生什么呢?FragmentManager会立即驱动fragment行动,执行必要方法,快速跟上activity步伐。一旦fragment的状态与activity的状态保持了同步,托管activity的FragmentManager就会边接收操作系统的调用指令,边调用其他生命周期方法,以继续保持fragment与activity的状态一致。
封装可复用组件是Google设计fragment的本意。一个良好的使用原则是:应用单瓶之多使用2-3个fragment。
使用fragment的理由:我们坚持总是使用fragment的原则。