上篇我对 Fragment 有了初步的回忆, 以前走马观花学的太不扎实.
###如何管理 Fragment 回退栈(back stack)
类似于 Android 系统为 Activity 维护一个任务栈, 我们可以通过 Activity 维护一个回退栈来保存每次 Fragment 事务的变化. 如果将 Fragment 加入回退栈, 当用户点击后退按钮时, 将看到上一次保存的 Fragment. 一旦 Fragment 完全从后退栈中弹出, 用户再次点击 back 键, 则退回到当前 Activity.
看下面这个例子:
Activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (a == null)
a = new AFragment();
/*这里并没有调用 FragmentTransaction.addToBackStack(String),
因为我不喜欢在当前显示时,点击 Back 键出现白板。而是正确的响应 Back 键,即退出我们的 Activity.*/
ft.add(R.id.id_content, a, "ONE");
ft.commit();
}
复制代码
Fragment A:
public class AFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_a,container,false);
Button bt_a = (Button) root.findViewById(R.id.bt_a);
bt_a.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BFragment b = new BFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.id_content,b,"TWO");
ft.addToBackStack("SAVE_B_STATE");
ft.commit();
}
});
return root;
}
}
复制代码
Fragment B:
public class BFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_b,container,false);
Button bt_b = (Button) root.findViewById(R.id.bt_b);
bt_b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CFragment c= new CFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//如果不希望视图重新绘制, 应该先隐藏再添加
ft.hide(BFragment.this);
ft.add(R.id.id_content,c,"THREE");
ft.addToBackStack("SAVE_C_STATE");
ft.commit();
}
});
return root;
}
}
复制代码
Fragment C 就是吐司提醒一下:
public class CFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_c,container,false);
Button bt_c = (Button) root.findViewById(R.id.bt_c);
bt_c.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), " i am a btn in Fragment C",
Toast.LENGTH_SHORT).show();
}
});
return root;
}
}
复制代码
完成后我们运行一下, 我依次点按钮从 Fragment A 进入 C 然后返回. 并在 editext 里面输入一些文本. 我们可以看到再返回的时候依次返回 C--B--A, B 的数据保存了, A 由于是使用 replace() 替换的, 它的实例还在但是视图被摧毁了, 是一个 onDestory 到 onCreated 的过程. 所以它的数据没有被保存下来.
###Fragment 与 Activity 通信
- 如果 Activity 中包含自己管理的 Fragment 的引用, 可以通过引用直接访问所有 Fragment 的 public 方法
- 如果未包含 Fragment 的引用, 每个 Fragment 都有唯一一个 TAG 或者 ID, 通过
getFrgmentManager.findFragmentByTag()
或findFragmentById()
获得任何 Frgment 实例, 然后进行操作 - 在 Fragment 中可以通过 getActivity 得到当前绑定的 Activity 的实例,然后进行操作。
注:如果在 Fragment 中需要 Context,可以通过调用getActivity()
, 如果该 Context 需要在 Activity 被销毁后还存在,则使用getActivity().getApplicationContext()
######最佳实践
因为要考虑 Fragment 的复用, 所以必须降低 Fragment 与 Activity 的耦合, 而且 Fragment 更不应该直接操作别的 Fragment, 毕竟 Fragment 操作应该由他的管理者 Activity 决定
对上面的代码进行重构
Fragment A:
public class AFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_a, container, false);
Button bt_a = (Button) root.findViewById(R.id.bt_a);
bt_a.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//交给 Activity 处理
if (mAClickListener != null) {
mAClickListener.onAClick();
}
}
});
return root;
}
public interface AClickListener {
void onAClick();
}
public AClickListener mAClickListener;
public void setAClickListener(AClickListener AClickListener) {
mAClickListener = AClickListener;
}
}
复制代码
Fragment B:
public class BFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_b,container,false);
Button bt_b = (Button) root.findViewById(R.id.bt_b);
bt_b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mBClickListener!=null)
mBClickListener.onBClick();
}
});
return root;
}
public interface BClickListener{
void onBClick();
}
public BClickListener mBClickListener;
public void setBClickListener(BClickListener BClickListener) {
mBClickListener = BClickListener;
}
}
复制代码
Activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (a == null)
a = new AFragment();
/*这里并没有调用 FragmentTransaction.addToBackStack(String),
因为我不喜欢在当前显示时,点击 Back 键出现白板。而是正确的响应 Back 键,即退出我们的 Activity.*/
ft.add(R.id.id_content, a, "ONE");
ft.commit();
a.setAClickListener(this);
}
/**
* A 按钮的点击事件
*/
@Override
public void onAClick() {
if (b == null){
b= new BFragment();
b.setBClickListener(this);
}
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.id_content, b, "TWO");
ft.addToBackStack(null);
ft.commit();
}
/**
* B 按钮的点击事件
*/
@Override
public void onBClick() {
if(c== null)
c= new CFragment();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(b);
ft.add(R.id.id_content,c,"C");
ft.addToBackStack(null);
ft.commit();
}
复制代码
通过给 A B 设置回调. 逻辑在 Activity 实现