目录
1.Fragment是什么?
2.Fragment的生命周期?
3.Fragment添加方式?
3.1在 Activity 的布局文件内声明片段
3.2通过编程方式将片段添加到某个现有 ViewGroup
4.编程方式动态添加,替换或者修改Fragment
5.实现Fragment回退
5.1定义Fragment返回监听接口,Fragment需要实现此接口
5.2定义BackHandlerHelper帮助类,进行返回事件分发,用于实现Fragment和FragmentActivity返回操作,主要实现将FragmentActivity监听到的返回事件进行分发给Fragment
5.3定义Fragment基类,其他需要监听返回事件类需要继承BackHandledFragment基类
5.4FragmentActivity调用工具方法BackHandlerHelper.handleBackPress(this),将返回事件进行分发
6.Fragment之间的数据交换
6.1Fragment通知数据给Activity
6.2将消息传递给Activity
Fragment相当于FragmentActivity界面的一部分,Fragment生命周期直接受宿主 Activity 生命周期的影响;Activity会维护Fragment返回栈,方便通过返回按钮关闭当前显示的Fragment进行回退;您可以通过在 Activity 的布局文件中声明片段,将其作为
Fragment 表示 FragmentActivity 中的行为或界面的一部分。您可以在一个 Activity 中组合多个片段,从而构建多窗格界面,并在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(这有点像可以在不同 Activity 中重复使用的“子 Activity”)。
片段必须始终托管在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。例如,当 Activity 暂停时,Activity 的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除片段。当执行此类片段事务时,您也可将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生片段事务的记录。借助返回栈,用户可以通过按返回按钮撤消片段事务(后退)。
当您将片段作为 Activity 布局的一部分添加时,其位于 Activity 视图层次结构的某个 ViewGroup 中,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
//1.创建一个Fragment和transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//2.添加添加add(),替换replace()Fragment到指定ViewGroup上,或者移除remove()Fragment
transaction.replace(R.id.fragment_container, newFragment);
//3.add()或者replace()以后调用addToBackStack()将事务添加到片段事务返回栈
//非必须调用,
transaction.addToBackStack(null);
//提交事务
transaction.commit();
Fragment并没有onBackPressed()方法,假如需要在Fragment监听返回操作需要自己实现返回的回调监听;
/**
* 返回接口
*/
public interface FragmentBackHandler {
boolean onBackPressed();
}
public class BackHandlerHelper {
/**
* 将back事件分发给FragmentManager中管理的子Fragment,如果FragmentManager中的子Fragment没有处理
* back事件,则调用fragmentManager.popBackStack();执行fragment返回
*
* true表示处理返回事件
* false表示未处理返回事件
* @param fragmentManager
* @return
*/
public static boolean handleBackPress(FragmentManager fragmentManager){
List fragments = fragmentManager.getFragments();
if(fragments == null) return false;
for(int i=fragments.size()-1; i>=0; i--)
{
Fragment child = fragments.get(i);
if(isFragmentBackHandled(child)){
return true;
}
}
if(fragmentManager.getBackStackEntryCount() > 0){
fragmentManager.popBackStack();
return true;
}
return false;
}
public static boolean handleBackPress(Fragment fragment){
return handleBackPress(fragment.getChildFragmentManager());
}
public static boolean handleBackPress(FragmentActivity fragmentActivity){
return handleBackPress(fragmentActivity.getSupportFragmentManager());
}
/**
* 判断Fragment是否处理了Back键,处理了返回true
* @param fragment
* @return
*/
public static boolean isFragmentBackHandled(Fragment fragment){
return fragment !=null
&& fragment.isVisible()
&& fragment.getUserVisibleHint() //for viewpager
&& fragment instanceof FragmentBackHandler
&& ((FragmentBackHandler) fragment).onBackPressed();//调用Fragment返回方法
}
}
/**
* Fragment基类,自己实现返回事件
*/
public abstract class BackHandledFragment extends Fragment implements FragmentBackHandler {
@Override
public boolean onBackPressed() {
return BackHandlerHelper.handleBackPress(this);
}
}
Fragment具体实现类继承BackHandledFragment基类,若需要监听返回事件,可以重写onBackPressed方法;
public class FragmentOne extends BackHandledFragment implements View.OnClickListener{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item,container,false);
TextView mTextView = (TextView) view.findViewById(R.id.tv_fragment_content);
mTextView.setText("FragmentOne");
Button mButton = (Button) view.findViewById(R.id.btn_toggle_fragment);
mButton.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_toggle_fragment:
FragmentTwo ftwo = new FragmentTwo();
FragmentManager mManager = getFragmentManager();
FragmentTransaction ft = mManager.beginTransaction();
ft.addToBackStack(null); //这里将我们的Fragment加入到返回栈
ft.replace(R.id.fl_fragment_container, ftwo);
ft.commit();
break;
}
}
}
public class FragmentManagerAcitivty extends AppCompatActivity {
private Fragment fragmentone;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
fragmentone = new FragmentOne();
getSupportFragmentManager().beginTransaction().add(R.id.fl_fragment_container,fragmentone, "one").commit();
}
@Override
public void onBackPressed() {
if (!BackHandlerHelper.handleBackPress(this)) {
super.onBackPressed();
}
}
}
返回事件分发
图中红色部分为BackHandledFragment 或其它实现了 FragmentBackHandler的Fragment。
back事件由下往上传递,当中间有未实现FragmentBackHandler的Fragment作为其它Fragment的容器时,或该Fragment拦截了事件时,其子Fragment无法处理back事件。
有没有一种似曾相识的感觉?其实这和View的事件分发机制是一个道理。
注意:这种方式支持多个Fragment返回,及Fragment嵌套,仅需要在Activity或者Fragment调用onBackPressed()实现返回事件监听和分发;
原理
1)不管是Activity也好,Fragment也好,其中内部包含的Fragment都是通过FragmentManager来管理的。
2)FragmentManager.getFragments()可以获取当前Fragment/Activity中处于活动状态的所有Fragment
3)事件由Activity交给当前Fragment处理,如果Fragment有子Fragment的情况同样可以处理。
要允许 Fragment 与其 Activity 进行通信,可以在 Fragment 类中定义接口并在 Activity 中实现该接口。Fragment 在其 onAttach() 生命周期方法中捕获接口实现,然后可以调用接口方法,以便与 Activity 通信。
以下是 Fragment 到 Activity 通信的示例:
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener callback;
public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener callback) {
this.callback = callback;
}
//这个接口将被Activity或者父Fragment实现
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// 发送事件给主Activity
callback.onArticleSelected(position);
}
}
Activity实现Fragment定义OnHeadlineSelectedListener接口,Fragment将获取主Activity通信接口实现,Fragment中callback可以通知数据给Activity ;
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
// ...
@Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof HeadlinesFragment) {
HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
headlinesFragment.setOnHeadlineSelectedListener(this);
}
}
}
托管 Activity 可通过使用 findFragmentById() 捕获 Fragment 实例,将消息传递到 Fragment,然后直接调用 Fragment 的公共方法。
例如,假设上方所示的 Activity 可能包含另一个 Fragment,该 Fragment 用于显示由上述回调方法中返回的数据指定的项。在这种情况下,Activity 可以将回调方法中收到的信息传递给显示该项的另一个 Fragment:
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
//在HeadlinesFragment调用onArticleSelected()方法传递数据给Activity
public void onArticleSelected(int position) {
//例如用户在列表选择了一篇文章,需要将选择的位置发送给ArticleFragment处理
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
//调用ArticleFragment方法显示文件
articleFrag.updateArticleView(position);
} else {
//新建ArticleFragment,并将参数传递给ArticleFragment
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
参考:
https://developer.android.google.cn/training/basics/fragments/communicating?hl=zh_cn
https://www.jianshu.com/p/fff1ef649fc0