实验目的
掌握Fragment 与Activity 组件的交互方式;
实验要求
实验内容
Fragment 中文名为片段 或碎片 ,它可视为Activity UI 页面中的部分或轻量级的Activity ,其具备与Activity 相似的生命周期。通过Fragment 可将原有的Activity 的UI 页面进行划分,提升UI 页面的可重用性以及适配性。本实验通过Fragment 对第七个项目 中新闻列表页面进行重构,介绍Fragment 的基本使用方法。
使用Fragment 时,需要从自定义Fragment 子类,并至少需要重写下列三个方法:
onCreate() ,与Activity 中的onCreate() 方法相似,该方法在创建Fragment 实例时被回调,
通常进行Fragment 中组件的初始化;
onCreateView() ,该方法在当需要Fragment 绘制UI时被调用,方法需要返回Fragment 的UI布局根视图;
onPause() ,该方法在用户离开Fragment UI页面时被调用,通常进行数据持久化或组件销毁;
除了上述三个方法必须进行重写外,Fragment 还包含了与生命周期相关的其他几个回调函数,详见官方开发文档。
步骤一,打开第七个项目
打开第七个项目 ,在该项目的基础上完成本次实验。
步骤二,新建NewsListFragment 类
在Project 窗口中选择app/java 文件夹,右击选择【New】->【Fragment】->【Fragment(Blank)】,在弹出的 New Android Component 对话框中Fragment Name 文本框中输入NewsListFragment , 勾选Create layout XML? 、取消勾选Include fragment factory methods? 及 Include interface callbacks 复选框,点击【Finish】完成新建Fragment 的创建, 具体如图1. 新建NewsListFragment 所示。
完成新建操作后,在项目中将生成NewsListFragment.java 以及fragment_news_list.xml 两个文件,分别对应 Fragment 的源文件以及布局文件。
使用Fragment 重构第七个项目 ,主要是将原在MainActivity 中各回调函数的业务代码相应的迁移到NewsListFragment 相应的回调函数中,而原有activity_main.xml 布局元素(除根视图元素外)也可一并迁移至fragment_news_list.xml 文件中。
Copy /**
* A simple {@link Fragment} subclass.
*/
public class NewsListFragment extends Fragment {
public NewsListFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_news_list,
container, false);
}
}
步骤三,更改fragment_news_list.xml布局文件
将第七个项目 中activity_main.xml 布局文件中的ListView 标签剪切并复制到fragment_news_list.xml 文件中,具体代码如下所示。
Copy <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv_news_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scrollbars="none"
android:layout_margin="8dp"
android:divider="@android:color/transparent"
android:dividerHeight="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
步骤四,修改NewsListFragment 类定义
在NewsListFragment 中实现新闻列表加载功能,主要思路是将原MainActivity 中的业务代码逐一剪切复制到NewsListFragment 当中。
1. 将titles、newsAdapter等对象声明复制到NewsListFragment中
将titles 、newsList 、newsAdapter 等对象申明从MainActivity 剪切复制到NewsListFragment 中。
2. 将initData()方法复制到NewsListFragment中
将initData() 方法定义从MainActivity 剪切复制到NewsListFragment 中。
3. 重写NewsListFragment的onCreate()方法
在NewsListFragment 类中重写onCreate() 方法,在其中调用initData() 方法进行新闻列表数据初始化。
4. 重写NewsListFragment的onCreateView()方法
onCreateView() 方法在Fragment 绘制UI 时被回调,该方法返回值为完成布局渲染的根视图元素 。 为方便加载布局,该方法包含了三个参数:
LayoutInflater inflater ,用于加载布局的LayoutInflater 对象;
ViewGroup container ,Fragment 加载的布局应嵌入进的ViewGroup 对象;
Bundle savedInstanceState ,初始化所需的Bundle 对象;
在NewsListFragment 的onCreateView() 方法中,首先,通过getActivity() 获取到与绑定当前Fragment 的Activity 对象,并将其作为Context 上下文对象进行保存,便于后续使用;其次,通过LayoutInflater 对象加载了fragment_news_list 布局;最后,通过加载好的布局绑定ListView 控件对象,并设置该对象所需的NewsAdapter 适配器。
Copy public class NewsListFragment extends Fragment {
private Context context = null;
private String[] titles = null;
private String[] authors = null;
private String[] contents = null;
private TypedArray images;
private List<News> newsList = new ArrayList<>();
private NewsAdapter newsAdapter = null;
private ListView lvNewsList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
context = getActivity();
View rootView = inflater.inflate(R.layout.fragment_news_list,
container, false);
lvNewsList = rootView.findViewById(R.id.lv_news_list);
newsAdapter = new NewsAdapter(context,
R.layout.list_item,
newsList);
lvNewsList.setAdapter(newsAdapter);
return rootView;
}
private void initData() {
int length;
titles = getResources().getStringArray(R.array.titles);
authors = getResources().getStringArray(R.array.authors);
images = getResources().obtainTypedArray(R.array.images);
if (titles.length > authors.length) {
length = authors.length;
} else {
length = titles.length;
}
for (int i = 0; i < length; i++) {
News news = new News();
news.setTitle(titles[i]);
news.setAuthor(authors[i]);
news.setImageId(images.getResourceId(i, 0));
newsList.add(news);
}
}
}
完成将MainActivity 的代码复制到NewsListFragment 后,MainActivity 类定义代码如下所示。
Copy public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
步骤五,加载Fragment
在Activity 中加载Fragment 有2种方式:
静态加载 ,在Activity 对应的布局中使用标签进行静态加载;
动态加载 ,在Activity 中使用FragmentManager 、FragmentTransaction 动态对Fragment 进行数据加载及绑定;
本实验中使用静态加载 的方式加载Fragment ,打开activity_main.xml 布局文件,由于在步骤三 中已经将原ListView 标签从activity_main.xml 中剪切复制到fragment_news_list.xml 中,因此直接在 activity_main.xml 根视图元素中加入标签,并通过android:name 属性指定 所需加载的Fragment 类名(包含包名),最终的布局文件代码如下所示。
Copy <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:name="com.glriverside.xgqin.listviewdemo.NewsListFragment"
android:id="@+id/news_list"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</android.support.constraint.ConstraintLayout>
步骤六,编译并运行项目
编译并运行项目,其执行效果与第七个项目 相同。
附:Fragment动态加载
动态加载Fragment 需要使用FragmentManager 、FragmentTransaction 两个组件。动态加载Fragment 需要指定对应的ViewGroup 视图容器,并通过FragmentTransaction 完成事务操作。
1. 首先调整activity_main.xml布局文件
在activity_main.xml 布局文件中,需要定义ViewGroup 视图容器作为动态加载Fragment 的根视图容器, 在本实验中,使用ConstraintLayout 作为根视图容器,并指定其id 属性为fragment_container , 具体代码如下所示。
Copy <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.constraint.ConstraintLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
2. 修改MainActivity的onCreate()方法
在Fragment 静态加载 方式中,通过在布局文件中指定,当Activity 被实例化时, 指定的Fragment 将被实例化,且该Fragment 对象对应的onCreate() 、onCreateView() 方法会被回调执行。
动态加载Fragment 则需要通过FragmentTransaction 碎片事务组件完成。 首先,需要获取FragmentManager 对象; 其次,调用FragmentManager 对象的beginTransaction() 方法获得FragmentTransaction 对象;在此基础上 调用FragmentTransaction 对象的相应方法添加、替换Fragment 至指定的ViewGroup 视图容器;最后, 调用FragmentTransaction 对象的commit() 方法提交碎片事务,执行Fragment 加载操作。
在该过程中,以下几个方法尤为重要:
getSupportFragmentManager() ,获取FragmentManager 对象;
FragmentManager.beginTransaction() ,开始碎片事务,获得FragmentTransaction 对象;
FragmentTransaction.add() 或FragmentTransaction.replace() ,
添加或替换Fragment 至指定ViewGroup 视图容器;
FragmentTransaction.addToBackStack() ,添加Fragment 至返回栈;
FragmentTransaction.commit() ,提交碎片事务;
如下代码所示,首先获取了FragmentManager 对象,并开始FragmentTransaction 碎片事务,再将实例化的NewsListFragment 对象通过FragmentTransaction.add() 方法添加到R.id.fragment_container 视图容器中,最后通过FragmentTransaction.commit() 提交碎片事务,完成Fragment 动态加载操作。
Copy public class MainActivity extends AppCompatActivity {
FragmentManager fragmentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
NewsListFragment newsListFragment = new NewsListFragment();
ft.add(R.id.fragment_container, newsListFragment);
ft.commit();
}
}
实验小结
通过本次实验,你应该掌握了如下知识内容:
使用FragmentManager 、FragmentTransaction 进行Fragment 动态加载;