【问题标题】:Android MVP - How to communicate between activity presenter and fragment presenterAndroid MVP - 如何在活动演示者和片段演示者之间进行通信
【发布时间】:2016-04-19 03:00:33
【问题描述】:

我有一个包含 3 个片段的活动,目前我使用 ViewPager。我想实现 MVP 并在活动演示者和片段演示者之间进行通信,即:

  • 将数据从活动展示器传递到片段展示器
  • 将事件从片段演示者发送到活动演示者
  • ...

但我不知道如何以官方方式进行。我可以使用 BusEvent,但我认为这不是一个好习惯。

【问题讨论】:

  • 您能否提供有关您的用例的更多详细信息?因为在不同的用例中有一些可能的答案,例如:创建通用业务规则、使用依赖注入、使用事件总线、将捆绑包传递给您的片段等。
  • 您能否提及您希望在演示者之间传递的数据/事件的示例?例如,它们是通用数据源吗?
  • 这回答了几乎相同的问题:stackoverflow.com/a/49955449/4514796

标签: android mvp


【解决方案1】:

片段和活动之间的通信或反之亦然可以通过使用 nnn's answer 或者您可以使用ViewModel 和LiveData 巫婆提供更清洁的方式并尊重fragments 和activities 的生命周期,这可以省去编写几行代码,以防止不可见的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 parentactivity`

当您使用mViewModel.getToFragmentA().observe() 或mViewModel.getToFragmentB().observe() 或mViewModel.getToAllFragments().observe() 时,您将MyViewModel 类中的LiveData 连接到给定的片段或活动,onChange() 方法的值在所有类中更新正在观察的方法。

我建议个人经验对Livedata 和ViewModel 进行一些研究,您可以在youtube 或this link 上进行研究

【讨论】:

    【解决方案2】:

    据我了解,对于您的 UseCase,假设 ActivityA 有一个 viewPager,其中包含 3 个 Fragment(FragmentA、FragmentB、FragmentC)。

    ActivityA 有 ActivityPresenterA

    FragmentA 有 FragmentPresenterA

    根据 MVP,FragmentPresenterA 应该只负责 FragmentA 的所有逻辑和业务流,并且应该只与 FragmentA 通信。因此,FragmentPresenterA 不能直接与 ActivityPresenterA 通信。

    对于从 Fragment 到 Activity 的通信,不应涉及 Presenter,这应该像我们在非 MVP 架构中进行通信一样完成,即借助接口。

    同样适用于 Activity 到 Fragment 的通信。

    Activity 和 Fragment 之间的通信请阅读here

    【讨论】:

    • 这种方法最干净吗?如果这些片段只与数据相关怎么办?在这种情况下不应该采取不同的做法吗?
    • 如果 ViewPager 的所有 Fragment 都有数据相互关联或共享公共数据,在这种情况下,Fragments 不需要有不同的 Presenter,而 ActivityPresenter 应该处理这些 Fragment 的所有业务逻辑并通过在接口的帮助下将数据转移到相应的片段。
    • 我喜欢这个想法只是因为我有同样的看法。现在想象一下 viewpager 中的嵌套片段或片段,每个片段都具有完全不相关的功能,并且每个片段中都有相当多的操作。将所有不相关的业务逻辑部分组合成一个巨大的 Presenter 有多可行?
    • @Farid:在这种情况下,您可以为每个嵌套片段设置单独的演示者来执行该嵌套片段的业务逻辑。并且这些嵌套片段的片段事务应该由父片段的呈现者来处理。
    【解决方案3】:

    您可以为这种情况使用一名演示者。

    使用您的 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 是我们视图的接收方法并将数据获取到演示者。

    现在我们将把数据从presenter传递给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 传递数据。

    如果你想将事件从Fragment发送到Activity,你可以按照这个想法。

    创建一个接口类并将其实现到您的 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 方法。如果您想了解 Fragment 的生命周期,请点击此链接: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 库。

    祝你好运。

    【讨论】:

      【解决方案4】:

      要在Fragment 和Activity 之间进行通信(无论是在他们的演示者之间还是在他们的班级之间),您需要一个您的活动实现的接口(例如ShoppingInteractor)。

      这样你就可以在片段中调用((ShoppingInteractor)getActivity()).doSomething()。如果你想让你的activity的presenter来处理任务,你需要调用activity内部doSomething中的presenter。

      您可以对带有另一个接口的片段执行相同的操作,并在 Activity 中调用片段的交互器。

      您甚至可以在这些界面中使用Presenter getPresenter() 来访问实际的演示者。 (((ShoppingInteractor)getActivity()).getPresenter().sendData(data))。片段也是如此。

      【讨论】:

        【解决方案5】:

        如果你想使用 MVP,第一步是为每个 View 创建一个 Presenter,我的意思是,如果你有 3 个 Fragment,那么就会有 3 个 Presenter。我认为为 4 个视图(活动和 3 个片段)创建一个演示者是个坏主意。

        【讨论】:

          【解决方案6】:

          动态数据:

          这是一个使用 rxjava2、dagger2 和 moxy 的示例。

          条件:

          • 演示者不依赖于视图的生命周期
          • 一个演示者 - 一个视图。视图之间不共享演示者,一个视图只有一个演示者。

          该解决方案类似于 EventBus,但使用的是具有有限生命周期的 Subject。它在活动启动时创建并在退出时销毁的组件中。 Activity 和 Fragment 都可以隐式访问它,它们可以更改值并以自己的方式响应它。

          示例项目:https://github.com/Anrimian/ViewPagerMvpExample

          静态数据:

          只需在片段中使用参数即可。

          【讨论】:

          • 我们如何从一个模型中的所有片段中一一收集信息?
          • 我没有完全理解你的意思,但是在普通的Interactor中你可以收集所有信息并观察它的变化。
          猜你喜欢
          • 2021-12-31
          • 2016-09-10
          • 2017-07-02
          • 2019-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-03
          • 2011-07-13
          相关资源
          最近更新 更多