什么是Fragment
- Android在3.0系统的时候,开始为电视机提供系统,电视机的屏幕尺寸要比现在的移动设备大很多,Google的开发人员希望在开发的过程中,能够将整个屏幕划分成多个小的部分,并且每一个小部分可以单独开发,实现界面以及功能,最终这些小的部分又需要组合在一起,那么,最终设计出来一个称作Fragment的内容。
- Fragment 是一个能够显示界面以及有自己独立的代码逻辑的组件,Fragment可以在布局中直接引用
Fragment的作用
- 简化重复的开发工作,将内容与工程进行整合,最终提供功能的重用性;
- 提高内聚,减少耦合程度;
- Fragment 用于进行布局界面的切分与优化,减少布局的重复,提高开发的效率。
Fragment开发流程
- Activity 必须 继承 FragmentActivity
- 创建Fragment子类: extends Fragment
- !!! 创建一个 无参的构造方法
- 重写 onCreateView 方法,设置Fragment显示的内容
- 创建 res/layout 中的布局资源,onCreateView加载布局,最终显示;
- 在不同的界面中就可以使用Fragment了。
使用Fragment 来组装Activity,可以像控件一样使用
Fragment事务
- add(int containerID, Fragment) 添加Fragment到容器
- replace(int containerId, Fragment) 将container中,原有的Fragment 自动 remove, 之后,再添加指定的Fragment.
- remove(Fragment) 删除 Fragment
- commit() 使事务的操作生效。
注意:
1. 一个事务,只能够进行一次 commit() 提交;
2. 类似于 “高考” 一年一次,通常都是一个方法中,开启事务,在这个方法,提交事务;就结束了,不要将事务建成成员变量的方式;
Fragment生命周期
- Fragment生命周期 和 Activity的生命周期相似:
- 因为Fragment依赖于Activity显示,那么当 Activity发生生命周期变化的时候,Fragment同样发生相同的变化。包括: onCreate, onStart, onResume, onPause, onStop, onDestroy, Fragment 还有一些生命周期。
- onAttach, onCreate, onCreateView, onActivityCreated, onStart, onResume, onPause, onStop, onDestroyView, onDestroy, onDetach
生命周期方法 | 描述 |
---|---|
onAttach | Activity 加载Fragment 调用 |
onCreate | Activity 继续调用 onCreate ,初始化一次的数据,在这里调用 |
onCreateView | Fragment 要显示的时候, 返回显示的内容 |
onActivityCreated | 最后可以使 Fragment 进行初始化的最后一步,之后进入显示 |
onStart | 等同于 Activity onStart |
onResume | Activity onResume : 加载数据,加载多次 |
onPause | Activity onPause |
onStop | Activity onStop |
onDestroyView | Fragment销毁界面,不再显示时调用 |
onDestroy | 销毁,等同于 Activity onDestory |
onDetach | Activity 将 Fragment 移除的时候,调用。 |
与Activity生命周期对比
Fragment的查找
每一个fragment在添加\replace的时候,都可以使用三个参数的方法,来给Fragment设置一个名称,tag;
Fragment的回退栈
Fragment的回退栈,是一种记录,记录事务的操作,如果事务被添加到回退栈,那么一般情况,当返回的时候,会对事务的操作进行一个逆转,从而恢复事务执行之前的内容。
回退栈原理
- 当事务被添加到回退栈的时候,FragmentManager 就会将事务的操作,添加到回退中,作为一个记录,存在;
- 当返回的时候,查找回退栈,如果有内容,那么对记录进行反转的操作。如 add 翻转为 remove , replace 调整为 remove 和 add;从而还原事务执行之前的状态;
- FragmentManager.popBackStack() 弹出回退栈,相当于后退键的操作
FragmentManager的Fragment管理
- findFragmentById(int id) 从XML布局中查找相应的Fragment;
- findFragmentByTag(String tag); 通常 xml中可以指定id, 也可以指定 tag ,根据tag去查找;tag常用场景大部分针对的是代码动态添加Fragment的情况,给Fragment一个Tag, 再后续查找的时候,可以找到了。
- 如果 在同一个代码中,添加Fragment并且指定Tag,这个时候事务执行可能有一定的时间,如果进行查找,可能找不到,因为还没有来得及添加到 FragmentManager中。
Fragment与Activity之间的交互
- Fragment 创建时接收参数;
- Fragment在运行的时候,Activity进行Fragment特定方法的调用;
- Fragment调用Activity特定的方法; onAttach(Activity) onDetach(Activity)
创建Fragment并且传参
在事务处理之前,使用Fragment.setArguments(Bundle args)
横竖屏
示例代码
普通使用
public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private HomeFragment mHomeFragment;
private DmFragment mDmFragment;
private SearchFragment mSearchFragment;
private PersonalFragment mPersonalFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Fragment的管理,检查Fragment是否已经存在
FragmentManager manager = getSupportFragmentManager();
Fragment f = manager.findFragmentByTag("home");
// 主流的 底部Tab切换页面的设计方式,就是 中间一个FrameLayout,
// 底部 RadioGroup,切换时,替换FrameLayout中的Fragment
RadioGroup group = (RadioGroup) findViewById(R.id.main_tab_bar);
if (group != null) {
group.setOnCheckedChangeListener(this);
}
// 1. 创建所有的Fragment对象,便于切换的时候使用
if(f == null) {
mHomeFragment = new HomeFragment();
}else{
mHomeFragment = (HomeFragment) f;
}
f = manager.findFragmentByTag("dm");
if(f == null) {
mDmFragment = new DmFragment();
}else{
mDmFragment = (DmFragment) f;
}
mSearchFragment = new SearchFragment();
mPersonalFragment = new PersonalFragment();
// 2. 每一个Fragment可以通过两种方式来添加到Activity
// 1) 使用<fragment> 标签直接添加到Activity;
// 2) 使用代码来添加Fragment,使用 FragmentManager, FragmentTransaction
System.out.println("MyLifeCycle MainActivity.onCreate start");
//FragmentManager manager = getSupportFragmentManager();
//
内部添加、删除、替换Fragment,必须要使用 FragmentManager.beginTransaction() 来开启
//FragmentTransaction tx = manager.beginTransaction();
//
事务每次操作之前必须重新开启,不允许使用成员变量的方式
//tx.add(R.id.fragment_container, mHomeFragment);
//
//tx.commit();
group.check(R.id.main_tab_item_home);
System.out.println("MyLifeCycle MainActivity.onCreate end");
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction tx = manager.beginTransaction();
// 进行代码切换Fragment
switch (checkedId) {
case R.id.main_tab_item_home:
// 关于替换操作,如果之前有Fragment,那么删除,之后在添加新的
// 如果没有,那么直接添加
//tx.replace(R.id.fragment_container, mHomeFragment);
tx.replace(R.id.fragment_container, mHomeFragment, "home");
break;
case R.id.main_tab_item_dm:
tx.replace(R.id.fragment_container, mDmFragment, "dm");
break;
case R.id.main_tab_item_search:
tx.replace(R.id.fragment_container, mSearchFragment, "search");
break;
case R.id.main_tab_item_personal:
tx.replace(R.id.fragment_container, mPersonalFragment, "personal");
break;
}
tx.commit();
}
}
Fragment
public class HomeFragment extends Fragment {
public HomeFragment() {
// Required empty public constructor
}
/**
* 当Fragment 创建之后,交给Activity的时候,自动回调
* 这个方法调用的时候Fragment还没有布局,还没有显示
* 通常这个方法,用于设置Fragment接口回调;
* @param context
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
System.out.println("MyLifeCycle HomeFragment.onAttach");
}
/**
* 创建期间初始化数据
* @param savedInstanceState
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("MyLifeCycle HomeFragment.onCreate");
}
/**
* 当Fragment 需要向Activity添加界面,或者是需要显示的时候
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println("MyLifeCycle HomeFragment.onCreateView");
// Inflate the layout for this fragment
View ret = inflater.inflate(R.layout.fragment_home, container, false);
return ret;
}
/**
* 1. 如果当前Fragment 通过 <fragment>添加到Activity的,
* 当Activity onCreate方法执行完返回之后,自动调用
* 2. 如果是代码方式添加到Activity的,那么 onCreateView 之后,会自动调用这个方法
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
System.out.println("MyLifeCycle HomeFragment.onActivityCreated");
}
/**
* Fragment 和 Activity 没有任何关系了
* 也是最后一个声明周期方法
* ! 当使用 replace() 时,旧的Fragment会进入到 Detach, 但Activity还在;
*/
@Override
public void onDetach() {
super.onDetach();
System.out.println("MyLifeCycle HomeFragment.onDetach");
}
}
横竖屏切换
MainActivity.java
public class MainActivity extends AppCompatActivity implements NewsListFragment.OnNewsSelectedListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onNewsSelected(Bundle bundle) {
Log.d("onNewsSelected", "bundle = " + bundle);
// 监测横屏还是竖屏
// 查找横屏特有的Fragment,来区分
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.fragment_detail);
if(fragment != null && fragment.isVisible()){
// 横屏
DetailFragment detailFragment = (DetailFragment) fragment;
detailFragment.setDetailTitle(bundle.toString());
}else{
// 竖屏
Intent intent = new Intent(this, DetailActivity.class);
// 把参数传递给Activity,然后Activity再传给Fragment
intent.putExtras(bundle);
startActivity(intent);
}
//if(fragment != null){
// Toast.makeText(MainActivity.this, "fragment " + fragment.isVisible(), Toast.LENGTH_SHORT).show();
//
// // 如果Fragment存在,并且显示的状态,代表横屏情况
// if(fragment.isVisible()) {
// DetailFragment detailFragment = (DetailFragment) fragment;
// detailFragment.setDetailTitle(bundle.toString());
// }
//
//}else{
// Toast.makeText(MainActivity.this, "fragment == null", Toast.LENGTH_SHORT).show();
//}
}
}
DetailActivity.java
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
// 如果 savedInstanceState == null 代表第一次创建,否则代表屏幕发生过旋转
if(savedInstanceState == null){
// 添加Fragment
Bundle extras = getIntent().getExtras();
if(extras != null){
DetailFragment detailFragment = new DetailFragment();
// Fragment内部传递参数,使用Bundle作为参数,调用 setArguments(Bundle)
detailFragment.setArguments(extras);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction tx = manager.beginTransaction();
tx.replace(R.id.detail_fragment_container, detailFragment);
// 在事务提交之后,会进入到 fragment的创建生命周期;
// 不能够在这个语句执行的附近进行UI的设置;
tx.commit();
}
}
}
}
NewsListFragment.java
public class NewsListFragment extends Fragment implements AdapterView.OnItemClickListener {
/**
* Fragment定义的接口,用于给Activity传递一个ListView点击的事件调用
*/
public interface OnNewsSelectedListener{
void onNewsSelected(Bundle bundle);
}
private OnNewsSelectedListener mOnNewsSelectedListener;
public NewsListFragment() {
// Required empty public constructor
}
public void setOnNewsSelectedListener(OnNewsSelectedListener onNewsSelectedListener) {
mOnNewsSelectedListener = onNewsSelectedListener;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// 内部设置 接口回调
if(context instanceof OnNewsSelectedListener){
mOnNewsSelectedListener = (OnNewsSelectedListener) context;
}else{
throw new IllegalArgumentException("Activity must implements OnNewsSelectedListener");
}
}
@Override
public void onDetach() {
mOnNewsSelectedListener = null;
super.onDetach();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View ret = inflater.inflate(R.layout.fragment_news_list, container, false);
ListView listView = (ListView) ret.findViewById(R.id.news_list);
if (listView != null) {
ArrayList<String> data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
data.add("News " + i);
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
getContext(),
android.R.layout.simple_list_item_1,
data
);
listView.setAdapter(adapter);
// 设置点击
listView.setOnItemClickListener(this);
}
return ret;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(mOnNewsSelectedListener != null){
Bundle bundle = new Bundle();
bundle.putInt("position", position);
bundle.putLong("id", id);
mOnNewsSelectedListener.onNewsSelected(bundle);
}
}
}
DetailFragment.java
public class DetailFragment extends Fragment {
private TextView mTextTitle;
public DetailFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View ret = inflater.inflate(R.layout.fragment_detail, container, false);
mTextTitle = (TextView) ret.findViewById(R.id.detail_title);
// 可以通过 getArguments() 来获取初始化的参数
Bundle arguments = getArguments();
if(arguments != null){
long id = arguments.getLong("id");
mTextTitle.setText("id = " + id);
}
return ret;
}
public void setDetailTitle(String title){
mTextTitle.setText(title);
}
}