本章主要内容如下:
2、Fragment与Activity的交互
3、Fragment回退栈
一、动态创建Fragment与FragmentManager
上一章最后,我们已经实现了一个通过FragmentManager、FragmentTransaction动态创建Fragment的例子,关键代码如下:
myFragment = new MyFragment(); android.support.v4.app.FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(myFragment,"aaa"); ft.commit();首先创建了一个Fragment的实例,接着调用getSupportFragmentManager()(如果使用app包下的Fragment使用getFragmentManager())获取到FragmentManager对象,该对象负责维护Fragment列表及回退栈管理,然后通过FragmentManager的beginTransaction()方法获取FragmentTransaction对象,FragmentTransaction对象是对单个Fragment添加、隐藏、移除、替换以及将Fragment添加进回退栈等操作。
FragmentTransaction完成对Fragment的操作后需要通过commit()方法提交才能生效。
并且一个FragmentTransaction对象无法执行两次commit(),每次需要commit()都要调用FragmentManager的beginTransaction()方法获取FragmentTransaction对象。
二、Fragment与Activity的交互
大家都知道Activity之间一般时通过Intent携带Bundle实现数据的交互,但Activity向Fragment发送数据无法使用Intent,并且不建议使用带参数的构造方法,因为Activity重启后会重新调用Fragment无参数的构造方法,导致我们传递的数据丢失,这里我们就需要使用Android SDK为我们提供的setArguments(Bundle args)方法来解决这个问题。该方法原型如下:
public void setArguments(Bundle args) { if (mIndex >= 0 && isStateSaved()) { throw new IllegalStateException("Fragment already active and state has been saved"); } mArguments = args; }
如果在Activity中调用该方法,就可以将Activity的数据通过Bundle发送给Fragment了,但是有一点需要注意,该方法的注释上有一句This method cannot be called if the fragment is added to a FragmentManager,就是说这个方法要在FragmentTransaction.commit()之前调用。这与方法回调时机有关,fragment的创建涉及子线程,FragmentTransaction.commit()之前调用该方法能保证在Fragment的生命周期回调中能获取到传递的参数。
实例化Fragment常用的一种写法:
public static MyFragment getInstance(Bundle bundle){ MyFragment fragment = new MyFragment(); fragment.setArguments(bundle); return fragment; }
fragment.setArguments(bundle);这个方法中将bundle设为Fragment的成员变量,并且对其数据进行了保存,在Activity重建后,也能获取到该数据,避免了数据丢失的情况。
三、Fragment回退栈
Fragment回退栈由FragmentManager负责出栈及监听,FragmentTransaction调用addToBackStack方法负责Fragment入栈,测试代码如下:
activity.onCreate
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); next = findViewById(R.id.btn_next); previous = findViewById(R.id.btn_previous); mFrame = findViewById(R.id.frame); mManager = getSupportFragmentManager(); if(savedInstanceState == null) { FragmentTransaction mTransaction = mManager.beginTransaction(); Bundle b = new Bundle(); b.putString("str", "fragment"); MyFragment myFragment = MyFragment.getInstance(b); //替换 mTransaction.replace(R.id.frame,myFragment); mTransaction.commit(); Log.d("commit", "commit结束"); } next.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FragmentTransaction mTransaction = mManager.beginTransaction(); i++; Bundle b = new Bundle(); b.putString("str", "fragment" + i); MyFragment myFragment = MyFragment.getInstance(b); //替换 mTransaction.replace(R.id.frame,myFragment); //入栈 mTransaction.addToBackStack(null); mTransaction.commit(); } }); previous.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //回退栈中是否存在Fragment实例 if(mManager.getBackStackEntryCount() > 0) i--; //出栈 mManager.popBackStack(); } }); }
fragment.java
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Bundle b = getArguments(); String str = ""; if(b != null){ str = b.getString("str"); } View view = inflater.inflate(R.layout.fragment_my,container,false); TextView text = view.findViewById(R.id.tv); text.setText(str); return view; } public static MyFragment getInstance(Bundle bundle){ MyFragment fragment = new MyFragment(); fragment.setArguments(bundle); return fragment; }每次点击next按钮,计数变量都会自增1,将"fragment" + i合并后的字符串传递给新建的Fragment用于显示,并将该Fragment加入回退栈,每次点击previous按钮或back键都会执行出栈操作。
运行结果如下: