在片段和活动之间进行通信 - 最佳实践

Posted

技术标签:

【中文标题】在片段和活动之间进行通信 - 最佳实践【英文标题】:Communicating between a fragment and an activity - best practices 【发布时间】:2012-12-24 06:02:53 【问题描述】:

这个问题主要是就处理我的应用的最佳方式征求意见。我有一个活动正在处理三个片段。片段 A 有一个可点击的照片元素,片段 B 有 4 个可点击的元素和按钮。单击照片时,另一个片段仅显示详细信息。我正在使用 ActionBarSherlock。

前进和后退按钮需要分别将照片更改为下一个或上一个姿势。我可以将照片和按钮保留在同一个片段中,但想将它们分开,以防我想在平板电脑中重新排列它们。

我需要一些建议 - 我应该将片段 A 和 B 结合起来吗?如果没有,我需要弄清楚如何为 3 个可点击项目实现接口。

我考虑过使用 Roboguice,但我已经在使用 SherlockFragmentActivity 进行扩展,所以这是不行的。我看到提到了 Otto,但是我没有看到关于如何包含在项目中的好的教程。您认为最佳设计实践应该是什么?

我还需要帮助弄清楚如何在片段和活动之间进行通信。我想在应用程序中保留一些“全局”数据,例如姿势 ID。除了股票 android 开发人员的信息之外,我还能看到一些示例代码吗?这并不是那么有帮助。

顺便说一句,我已经将有关每个姿势的所有信息存储在 SQLite 数据库中。那是容易的部分。

【问题讨论】:

其实Rogoguice可以和ActionbarSherlock一起使用,看看github.com/rtyley/roboguice-sherlock。 Otto 更简单,它被打包为一个独立的 .jar 文件,您可以将其放在应用程序的 libs/ 文件夹中。 @rubenlop88 添加一个库只是为了将一些数据从 Fragment 传递到 Activity? 【参考方案1】:

在 Activity 和 Fragment 之间进行通信的最简单方法是使用接口。这个想法基本上是在给定的片段 A 中定义一个接口,并让 Activity 实现该接口。

一旦它实现了那个接口,你就可以在它覆盖的方法中做任何你想做的事情。

接口的另一个重要部分是您必须从片段中调用抽象方法并记住将其转换为您的活动。如果没有正确完成,它应该会捕获 ClassCastException。

Simple Developer Blog 上有一个很好的教程,教你如何做这种事情。

希望对你有帮助!

【讨论】:

有谁知道为什么android开发者页面上关于使用警报对话框的示例不使用接口与活动通信? developer.android.com/reference/android/app/… @cjayem13 在某些情况下,你可能需要一个fragment来和activity共享事件,一个好办法就是定义一个回调接口,或许你可以看看这个Communicating with the Activity跨度> 接口很好。但对于更松散的耦合,请使用广播侦听器。 当然,但是当你的活动最终为一个片段提供几个不同的服务时会发生什么,因为片段本身不能做很多上下文相关的事情?我已经让我的活动实现了 4 个不同的接口来与片段进行通信,然后举手并让片段知道活动,因为在我的情况下,这些片段不会成为不同活动的父级,如果他们这样做了,我可能不得不修复其他东西以使其顺利运行。 现代方法是使用事件,它实际上将您的项目代码解耦,这个答案来自 2014 年,虽然在一定程度上有效,但不应该是这种情况的唯一解决方案!【参考方案2】:

片段之间通信的建议方法是使用由主 Activity 管理的回调\侦听器。

我认为此页面上的代码非常清晰: http://developer.android.com/training/basics/fragments/communicating.html

您还可以参考 IO 2012 Schedule 应用程序,该应用程序旨在成为事实上的参考应用程序。在这里能找到它: http://code.google.com/p/iosched/

另外,这是一个包含很好信息的 SO 问题: How to pass data between fragments

【讨论】:

【参考方案3】:

通过回调接口实现:

首先,我们要做一个界面:

public interface UpdateFrag 
     void updatefrag();

在 Activity 中执行以下代码:

UpdateFrag updatfrag ;

public void updateApi(UpdateFrag listener) 
        updatfrag = listener;

来自必须在 Activity 中触发回调的事件:

updatfrag.updatefrag();

在 Fragment 中实现CreateView 中的接口 以下代码:

 ((Home)getActivity()).updateApi(new UpdateFrag() 
        @Override
        public void updatefrag() 
              .....your stuff......
        
 );

【讨论】:

这很好用 - 感谢分享很好的解释 :) 网络上最好的答案之一。感谢分享。【参考方案4】:

ActivityFragments之间的通信有多种选择,但经过大量阅读和经验,我发现可以这样恢复:

Activity 想与孩子交流 Fragment => 只需在 Fragment 类中编写公共方法,然后让 Activity 调用它们 Fragment 想要与父级通信 Activity => 这需要更多的工作,正如官方 Android 链接 https://developer.android.com/training/basics/fragments/communicating 所建议的那样,定义一个 interface 将由Activity,它将为任何想要与Fragment 通信的Activity 建立合同。例如,如果您有FragmentA,它想与任何包含它的activity 通信,那么定义FragmentAInterface,它将定义FragmentA 可以为决定使用它的activities 调用什么方法. Fragment 想要与其他Fragment 通信 => 这是您遇到最“复杂”情况的情况。由于您可能需要将数据从 FragmentA 传递到 FragmentB,反之亦然,这可能导致我们定义 2 个接口,FragmentAInterface 将由 FragmentB 实现,FragmentAInterface 将由 FragmentA 实现。这将开始使事情变得混乱。想象一下,如果您有更多的Fragments,甚至父母activity 也想与他们交流。好吧,这个案例是为activity 建立共享ViewModel 的完美时机,它是fragments。更多信息在这里https://developer.android.com/topic/libraries/architecture/viewmodel。基本上,您需要定义一个 SharedViewModel 类,其中包含您想要在 activityfragments 之间共享的所有数据,这些数据需要在它们之间进行数据通信。

ViewModel 案例最终使事情变得非常简单,因为您不必添加额外的逻辑来使代码变得肮脏和混乱。此外,它还允许您将收集的数据(通过调用 SQLite 数据库或 API)与 Controlleractivitiesfragments)分开。

【讨论】:

【参考方案5】:

我制作了一个注释库,可以为你做演员。看一下这个。 https://github.com/zeroarst/callbackfragment/

@CallbackFragment
public class MyFragment extends Fragment 

    @Callback
    interface FragmentCallback 
       void onClickButton(MyFragment fragment);
        
    private FragmentCallback mCallback;

    @Override
    public void onClick(View v) 
        switch (v.getId()) 
            case R.id.bt1
                mCallback.onClickButton(this);
                break;
            case R.id.bt2
                // Because we give mandatory = false so this might be null if not implemented by the host.
                if (mCallbackNotForce != null)
                mCallbackNotForce.onClickButton(this);
                break;
        
    

然后它会生成片段的子类。只需将其添加到 FragmentManager。

public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager().beginTransaction()
            .add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), "MY_FRAGM")
            .commit();
    

    Toast mToast;

    @Override
    public void onClickButton(MyFragment fragment) 
        if (mToast != null)
            mToast.cancel();
        mToast = Toast.makeText(this, "Callback from " + fragment.getTag(), Toast.LENGTH_SHORT);
        mToast.show();
    

【讨论】:

【参考方案6】:

在活动、片段、服务等之间有多种通信方式。最明显的一种是使用接口进行通信。但是,这不是一种有效的沟通方式。您必须实现侦听器等。

我的建议是使用事件总线。事件总线是一种发布/订阅模式的实现。

您可以订阅活动中的事件,然后可以在片段等中发布该事件。

在这里on my blog post你可以找到关于这个模式的更多细节,也可以an example project来展示用法。

【讨论】:

【参考方案7】:

我不确定我是否真的理解您想要做什么,但建议在片段之间进行通信的方式是使用 Activity 的回调,而不是直接在片段之间。看这里http://developer.android.com/training/basics/fragments/communicating.html

【讨论】:

是的,这种方式似乎令人困惑,我有 2 个片段需要通过活动以某种方式相互通信。有没有更好的教程来说明如何做到这一点? @KristyWelsh :我想知道您是否已经对Fragments 的真正含义以及它们如何工作有了自己的概念,而不是掌握Fragment 设计目标的实际概念。使用interfacesActivity 通信的概念与使用Activity 实现View.OnClickListener 没有什么不同,因此Button 按下会导致项目被添加到ListView(例如)。您可能有一个 Activity 与 FragmentA 和 FragmentB 但不是 FragmentC - 在这种情况下,让 FragmentA“知道”如何直接与 FragmentC 对话的概念是多余的。【参考方案8】:

谷歌推荐方法

如果您查看this page,您会发现Google 建议您使用ViewModelFragmentActivity 之间共享数据。

添加这个依赖:

implementation "androidx.activity:activity-ktx:$activity_version"

首先,定义您将用于传递数据的ViewModel

class ItemViewModel : ViewModel() 
    private val mutableSelectedItem = MutableLiveData<Item>()
    val selectedItem: LiveData<Item> get() = mutableSelectedItem

    fun selectItem(item: Item) 
        mutableSelectedItem.value = item
    

其次,在Activity中实例化ViewModel

class MainActivity : AppCompatActivity() 
    // Using the viewModels() Kotlin property delegate from the activity-ktx
    // artifact to retrieve the ViewModel in the activity scope
    private val viewModel: ItemViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        viewModel.selectedItem.observe(this, Observer  item ->
            // Perform an action with the latest item data
        )
    

第三,在Fragment中实例化ViewModel

class ListFragment : Fragment() 
    // Using the activityViewModels() Kotlin property delegate from the
    // fragment-ktx artifact to retrieve the ViewModel in the activity scope
    private val viewModel: ItemViewModel by activityViewModels()

    // Called when the item is clicked
    fun onItemClicked(item: Item) 
        // Set a new item
        viewModel.selectItem(item)
    

您现在可以编辑此代码以创建新的观察者或设置方法。

【讨论】:

【参考方案9】:

您可以在片段中创建一个带有函数声明的公共接口,并在活动中实现该接口。然后您可以从片段中调用该函数。

【讨论】:

【参考方案10】:

我正在使用 Intent 将操作传达回主要活动。主要活动是通过覆盖 onNewIntent(Intent intent) 来监听这些。例如,主要活动将这些动作转换为相应的片段。

所以你可以这样做:

public class MainActivity extends Activity  

    public static final String INTENT_ACTION_SHOW_FOO = "show_foo";
    public static final String INTENT_ACTION_SHOW_BAR = "show_bar";


   @Override
   protected void onNewIntent(Intent intent) 
        routeIntent(intent);
   

  private void routeIntent(Intent intent) 
       String action = intent.getAction();
       if (action != null)                
            switch (action) 
            case INTENT_ACTION_SHOW_FOO:
                // for example show the corresponding fragment
                loadFragment(FooFragment);
                break;
            case INTENT_ACTION_SHOW_BAR:
                loadFragment(BarFragment);
                break;               
        
    

然后在任何片段内显示 foo 片段:

Intent intent = new Intent(context, MainActivity.class);
intent.setAction(INTENT_ACTION_SHOW_FOO);
// Prevent activity to be re-instantiated if it is already running.
// Instead, the onNewEvent() is triggered
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(intent);

【讨论】:

【参考方案11】:

有最新的技术可以在没有任何接口的情况下将片段与活动进行通信,请遵循以下步骤 步骤 1- 在 gradle 中添加依赖项

实现'androidx.fragment:fragment:1.3.0-rc01'

【讨论】:

请举个例子。您刚刚定义了一些依赖项。它是如何使用的?有什么不同?

以上是关于在片段和活动之间进行通信 - 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

更新片段参数的最佳实践?

在 React 组件和服务之间进行通信的最佳实践是啥?

在android中使用底部导航的最佳实践:活动与片段

在 Rust 程序和嵌入式 WebAssembly 运行时之间进行通信的最佳实践是啥?

与服务中的活动(LocalService)进行通信 - Android最佳实践

与来自服务的活动(LocalService)通信 - Android 最佳实践