用户场景
设想这样一种用户场景:
在个人信息展示界面, 用户点击编辑按钮, 跳转到编辑界面, 完成编辑后, 回退到个人信息展示界面, 此时界面上应该展示最新的数据.
这里涉及到编辑后的数据如何及时更新到个人信息界面, 也即Activity&Fragment之间如何通信的问题.
分析
由于Fragment需要依托于Activity存在, 故有如下几种通信场景:
image乍看上去可能不太好理解, 来依次分析下这几种场景.
1.Activity与Activity通信
此场景最为常见, 用户从Activity A跳转到Activity B, 通过Activity A的startActivity(Intent intent)
方法, 可以在Intent中附加信息, 在打开Activity B的同时将这些信息传递给Activity B.
那么Activity B如何传递信息回Activity A呢? 使用setResult()
方法即可. 很基础的用法, 下面列出一些需要注意的点:
a
调用setResult()
后Activity A并不会立刻调用onActivityResult()
回调, 而是当Activity B finish后才会调用.
2.Activity与其中的Fragment通信
Activity -> Fragment
Activity A中包含了Fragment B. 如何传递数据给Fragment B呢?
建议使用Fragment.setArguments()
.
我们也可以通过Fragment的构造方法传递参数, 但这是不推荐的. 当配置发生变化(语言发生变化, 屏幕发生旋转等), Activity会发生重建, 依附于其上的Fragment也会重建, 系统会调用Fragment的默认无参构造方法(如果我们实现了有参的构造方法, 会导致系统在重建Fragment时找不到无参构造方法, 会崩溃.) 而我们通过setArgument设置的参数在重建时会自动恢复, 这样就不会丢失数据了. 我们看一个使用setArgument的实例:
SomeFragment fragment = new SomeFragment();
Bundle args = new Bundle();
args.putString(SOME_EXTRA, "some_args");
fragment.setArguments(args);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
通过这种方式创建的Fragment在屏幕旋转等情况下还能保持通过setArgument设置的参数.
上面说的是创建Fragment时Activity如何传递参数给Fragment, 当Fragment创建后, Activity如何向Fragment发送消息呢? 我们要用到下面两个方法:
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentByTag("some_tag");
这两个方法可以获取到想要的Fragment实例, 进而可以调用其方法向其传递消息.
Fragment -> Activity
那么Fragment如何与其宿主Activity通信呢? 诚然, 我们可以通过在Fragment中调用getActivity()
获取其宿主Activity, 并将其强制转换为宿主Activity的类型, 并调用其中的方法, 达到Fragment向其Activity传递参数的目的, 如下:
SomeActivity activity = (SomeActivity) getActivity();
activity.someMethod();
Fragment被设计为可复用的模块化Activity组件, 为了保证其可复用性, 我们应该避免在Fragment中直接调用其他Fragment或Activity的方法, 因为这样会导致Fragment与特定的Activity/Fragment耦合, 无法实现其可复用性.
我们应该在Fragment中定义一个接口, 需要与之通信的Activity实现其接口:
class SomeFragment extends Fragment {
...
interface ICommunication {
void communicate(...);
}
void sendMessageToActivity() {
((ICommunication)getActivity()).communicate(...);
}
}
class ParentActivity extend FragmentActivity implements ICommunication {
@Override
void communicate(...) {
//TODO do some thing
}
}
这样做, 宿主Activity需要实现Fragment定义好的通信接口, 保证了Fragment的可复用性.
PS: 是否有更好的方式?
3.同Activity中的两个平级Fragment通信
我们为了保证Fragment的可复用性, 绝对不能在一个Fragment中直接调用另一个Fragment的方法. 那两个平级的Fragment如何通信呢, 很简单, 通过父Activity中中转一下就可以了, 用上面列出的两种方法.
image4.子Activity中的Fragment和父Activity如何通信?
2 --> 1
Fragment先与子Activity通信, 子Activity再通过setResult将消息传递给父Activity, done.
image5.子Fragment与父Fragment间通信
我们先看简单的情况, 父Fragment如何与子Fragment通信?
创建时
老生常谈了, 用setArguments()
向子Fragment传递参数, 这样在配置变化重建Fragment时, 参数也能恢复.
运行时
因为子Fragment是直接在父Fragment里new出来的, 父Fragment是保存有子Fragment的引用的, 既然有引用, 就可以直接调用子Fragment的成员方法了. 因为父Fragment就是要new特定类型的子Fragment, 不存在可复用性的影响, 类似宿主Activity持有Fragment引用.
再来看复杂一点的情况, 子Fragment如何与父Fragment通信? 不要跟我说用:
getActivity().getSupportFragmentManager().findFragmentById(R.id.fragment_container);
子Fragment设计出来是为了复用的, 这样做, 子Fragment就知道一定有一个特定类型的父Fragment, 还要去调用父Fragment特定的方法, 这样谈何复用?
正确的姿势在这里:
父Fragment在创建子Fragment时, 这样操作:
public static final int REQUEST_CODE = 1024;
...
ChildFragment childFragment = ChildFragment.newInstance(someArguments);
childFragment.setTargetFragment(this, REQUEST_CODE); //this是父Fragment的引用, 向上转型为Fragment
//show child fragment...
这里子Fragment将父Fragment设置为其TargetFragment, 并设置了requestCode.
当子Fragment需要传递数据回父Fragment时:
public class ChildFragment extends Fragment {
...
//需要传递数据时调用
private void setResult(SomeType someData) {
Intent intent = new Intent();
intent.putExtra(SOME_EXTRA, someData);
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent);
}
}
是不是很惊讶, 居然调用了Fragment.onActivityResult()
方法.
该方法是Fragment实现的一个通用空方法, 我们继承的Fragment都有这个方法, 由于是通用的, 也就不存在破坏复用性的问题.
getTargetRequestCode()
可以拿到子Fragment创建时传入的requestCode, Intent用来装载数据. 我们在父Fragment中复写onActivityResult()
方法, 这样就可以传回数据到父Fragment啦~
public class ParentFragment extends Fragment {
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//我们根据requestCode判断是哪个子Fragment传回的数据
//从data中拿到传回的数据
}
}
6.父Activity的Fragment与子Activity的Fragment之间通信
这种情况就很简单啦, 套用上面的5点, 相信你可以解决~
网友评论