我有一个包含3个片段的活动,目前我使用的是ViewPager。我想实现MVP并在活动演示者和片段演示者之间进行通信,即:
但我不知道如何以官方方式做到这一点。我可以使用BusEvent,但我认为这不是一个好习惯。
片段和活动之间的通信或反之亦然可以通过使用
nnn's answer 来完成,或者你可以使用ViewModel
和LiveData
女巫提供一种更清洁的方式并尊重fragments
和activities
的生命周期,这可以避免编写几行代码以防止aa不可见的fragment
从背景上接收数据。
首先,你扩展ViewModel
类,初始化Livedata
和一些辅助方法。
public class MyViewModel extends ViewModel {
private MutableLiveData<String> toFragmentA, toFragmentB;
private MutableLiveData<List<String>> toAllFragments;
public MyViewModel() {
toFragmentA = new MutableLiveData<>();
toFragmentB = new MutableLiveData<>();
toAllFragments = new MutableLiveData<>();
}
public void changeFragmentAData(String value){
toFragmentA.postValue(value);
}
public void changeFragmentBData(String value){
toFragmentB.postValue(value);
}
public void changeFragmentAllData(List<String> value){
toAllFragments.postValue(value);
}
public LiveData<String> getToFragmentA() {
return toFragmentA;
}
public LiveData<List<String>> getToAllFragments() {
return toAllFragments;
}
public LiveData<String> getToFragmentB() {
return toFragmentB;
}
}
然后你在你的活动上初始化ViewModel
。
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private TabLayout tabLayout;
MyViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this)
.get(MyViewModel.class);
viewPager.setAdapter(new Adapter(getSupportFragmentManager()));
}
}
读取片段中的数据:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
mViewModel.getToAllFragments().observe(this, new Observer<List<String>>() {
@Override
public void onChanged(List<String> s) {
myList.addAll(s);
//do something like update a RecyclerView
}
});
mViewModel.getToFragmentA().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
mytext = s;
//do something like update a TextView
}
});
}
要更改任何实时数据的值,您可以使用任何片段或活动中的方法之一:
changeFragmentAData();
changeFragmentBData();
changeFragmentAllData();
幕后发生了什么:
当你使用mViewModel = ViewModelProviders.of(this).get(MyViewModel.class)
时,你正在创建一个ViewModel
的n实例并将其绑定到片段的给定活动的生命周期,因此视图模型仅在activity
或fragement
停止时才会被破坏。如果你使用mViewModel = ViewModelProviders.of(getActivity())。get(MyViewModel.class)you are bindig it to the lifecycle if the parent
activity`
当你使用mViewModel.getToFragmentA().observe()
或mViewModel.getToFragmentB().observe()
或mViewModel.getToAllFragments().observe()
你将LiveData
类中的MyViewModel
连接到给定的片段或活动时,onChange()
方法的值会在观察该方法的所有类中更新。
根据我的理解,对于你的UseCase,假设ActivityA有一个viewPager有3个片段(FragmentA,FragmentB,FragmentC)。
ActivityA有ActivityPresenterA
FragmentA有FragmentPresenterA
根据MVP,FragmentPresenterA应仅负责FragmentA的所有逻辑和业务流,并且只应与FragmentA通信。因此,FragmentPresenterA无法直接与ActivityPresenterA通信。
对于从Fragment到Activity的通信,不应该介绍演示者,这应该像我们在非MVP架构中进行通信一样,即在界面的帮助下进行。
同样适用于活动到片段通信。
对于Activity和Fragment之间的通信,请阅读here
您可以为该案例使用一个演示者。
使用Activity Presenter获取片段所需的所有数据。然后创建一个接口类并将其实现到您的片段。
例如:
为PageAFragment创建一个公共接口(此接口将从活动到片段的数据桥接)。并使用您的界面方法处理演示者要查看的结果。
这是我为接收的数据创建的接口类的示例。对于参数,你可以选择你想要的东西,这取决于你的需要,但对我来说,我选择模型。
public interface CallbackReceivedData {
void onDataReceived(YourModel model);
}
在MainActivity类中,检查附加到您的活动中的片段的实例。在提交片段后放置检查实例。
public class MainActivity extends AppCompatActivity{
private CallbackReceivedData callbackReceivedData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
}
我假设receivedDataFromPresenter是我们视图的接收方法,并将数据提供给演示者。
现在我们将数据从演示者传递给callbackReceivedData
在PageAFragment中实现CallbackReceivedData并覆盖onDataReceived方法。现在,您可以将活动中的数据传递给片段。
public class PageAFragment extends Fragment implements CallbackReceivedData{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onDataReceived(YourModel model) {
}
}
注意:替代方法,您可以使用Bundle并使用setArguments传递数据。
如果您想将片段中的事件发送到活动,您可以遵循此想法。
创建一个Interface类并将其实现到MainActivity并覆盖从接口到活动的方法,对于我的情况,我这样做。
这是我的CallbackSendData类。
public interface CallbackSendData {
void sendDataEvent(String event);
}
为您的MainActivity实现CallbackSendData接口并覆盖sendDataEvent方法。
public class MainActivity extends AppCompatActivity implements CallbackSendData{
private CallbackReceivedData callbackReceivedData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
@Override
public void sendDataEvent(String event){
//You can now send the data to your presenter here.
}
}
对于你的PageAFragment,你需要使用attach方法来构建你的界面。一旦片段与其活动相关联,就调用attach方法。如果您想了解片段的生命周期,请单击此链接:https://developer.android.com/reference/android/app/Fragment.html。
public class PageAFragment extends Fragment implements CallbackReceivedData{
private CallbackSendData callbackSendData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onDataReceived(YourModel model) {
//Received the data from Activity to Fragment here.
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup
container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.PagerAFragment, container,
false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle
savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button Eventbutton;
Eventbutton = view.findViewById(R.id.event_button);
Eventbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callbackSendData.sendDataEvent("send Data sample");
}
});
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try{
callbackSendData = (CallbackSendData) context;
}catch (ClassCastException e){
e.printStackTrace();
}
}
}
现在,您可以使用CallbackSendData将活动数据发送到片段。
注意:如果您对项目使用依赖注入,则更容易,您可以使用Dagger2库。
祝好运。
要在Fragment
和Activity
之间进行通信(无论是在他们的演示者还是他们的类之间),您需要一个您的活动实现的界面(如ShoppingInteractor
)。
这样你就可以在片段中调用((ShoppingInteractor)getActivity()).doSomething()
。如果您希望活动的演示者处理任务,则需要在活动内部的doSomething
中调用演示者。
您可以使用另一个接口对片段执行相同操作,并在活动内部调用片段的交互器。
您甚至可以在这些界面中使用Presenter getPresenter()
来访问实际的演示者。 (((ShoppingInteractor)getActivity()).getPresenter().sendData(data)
)。片段也一样。
如果你想使用MVP,第一步是为每个View创建一个演示者,我的意思是,如果你有3个片段,那么将有3个演示者。我认为为4个视图(活动和3个片段)创建一个演示者是个坏主意。
动态数据:
以下是使用rxjava2,dagger2和moxy的示例。
附加条件:
该解决方案类似于EventBus,但使用具有有限生命周期的Subject。它位于活动开始时创建的组件中,并在退出时被销毁。活动和片段都具有对它的隐式访问权限,它们可以更改值并以自己的方式响应它。
示例项目:https://github.com/Anrimian/ViewPagerMvpExample
静态数据:
只需在片段中使用参数就可以了。