上篇我对 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 通信

  1. 如果 Activity 中包含自己管理的 Fragment 的引用, 可以通过引用直接访问所有 Fragment 的 public 方法
  2. 如果未包含 Fragment 的引用, 每个 Fragment 都有唯一一个 TAG 或者 ID, 通过 getFrgmentManager.findFragmentByTag()findFragmentById() 获得任何 Frgment 实例, 然后进行操作
  3. 在 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 实现