活动和碎片的交互

15
我有一个带有多个片段的Activity。我想从其中一个Fragment中显示一个DialogFragment或打开另一个Fragment。我知道一个Activity应该负责打开Fragment,所以我尝试了几种方法。 第一种:
我尝试使用getActivity()并对其进行强制类型转换以便于调用Activity中的方法来显示Fragment。但是这会在FragmentActivity之间创建依赖关系,如果可能的话,我想避免添加依赖项。

第二种:
接下来我尝试了一个监听器来通知Activity它应该显示一个Fragment。因此,我在Activity中创建了一个类来实现监听器接口。但是我遇到了问题,因为我必须使用New MyActivity().new Listener();,并且当我尝试使用getSupportFragmentManager()时,它会抛出异常,因为此Activity实例未初始化。

第三种:
然后我尝试让Activity直接实现监听器,这是有效的,因为这样我只会创建与监听器而不是Activity的依赖关系。但现在我的Activity将实现2-4个不同的接口,这让我有些犹豫,因为它会严重降低组织性。

所以无论我尝试什么方法,似乎都遇到了瓶颈,并且创建了我不确定是否需要创建的依赖项。我是否必须选择其中一种选项?如果是这样,哪个选择最好?任何帮助或建议都将不胜感激。

有很多方法可以实现它,我更喜欢尽可能解耦的方式,因此我喜欢事件总线。例如,可以查看otto:http://square.github.io/otto/。(让您摆脱所有接口/监听器的麻烦。传递数据,使用强类型进行操作,以清晰简洁的方式完成。) - Charlie Collins
看起来很有前途。我得去看看。谢谢你的提示。 - Jason Crosby
7个回答

16

创建接口

public interface ListenFromActivity {
    void doSomethingInFragment();
}

Activity类中保留ListenFromActivity接口的引用

 public ListenFromActivity activityListener;   

公开一个方法来设置监听器

 public void setActivityListener(ListenFromActivity activityListener) {
        this.activityListener = activityListener;
    }

在Activity类中添加一些触发点,这里我使用了用户交互

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();

        if (null != activityListener) {
            activityListener.doSomethingInFragment();
        }
    }

现在在片段类

使你的片段实现接口类

public class SomeFragment extends Fragment implements ListenFromActivity

Android Studio会提示您在Fragment中实现接口方法。

 void doSomethingInFragment()
{//Add your code here 
}

最后一步,将监听器实例添加到片段中的onCreate方法中的活动中,类似于以下方式。

((ListnerActivity) getActivity()).setActivityListener(SomeFragment.this);

DONE!! 现在您可以从Activity中调用Fragment方法。


11

个人认为片段应被视为可重用和模块化的组件。因此,为了提供这种可重用性,片段不应过多地了解其父活动。但作为回报,活动必须了解它们持有的片段。

因此,在我看来,由于您提到的依赖关系原因造成了非常高度耦合的代码,第一种选项永远不应考虑。

关于第二个选项,片段可以将任何应用程序流程或UI相关的决策(显示新片段,决定在触发片段特定事件时要做什么等)委托给其父活动。因此,您的侦听器/回调应该是特定于片段的,并且应在片段中声明。持有这些片段的活动应实现这些接口并决定要做什么。

所以对我来说,第三个选项更有意义。我相信活动在特定回调上持有和执行的内容更易读。但是,您是正确的,您的活动可能会变成一个God对象。

如果您不想实现多个接口,也许您可以检查Square的Otto项目。它基本上是一个事件总线。


感谢您和@Charlie Collins指引我使用Otto库。它看起来非常有前途,似乎可以实现我所需求的功能。感谢大家的帮助。 - Jason Crosby

4
您需要将来自Fragment X的数据传递到FragmentActivity,然后再将这些数据传递给Fragment Y。您可以通过在片段类中定义一个接口并实例化onAttach()中定义的回调来实现此目的。
更多信息可参考此链接:与其他片段通信 举个快速的例子,假设有Fragment A和Fragment B。其中Fragment A是列表片段,每次选择项目后都会更改Fragment B中显示的内容。很简单吧?
首先,将Fragment A定义如下。
 public class FragmentA extends ListFragment{

   //onCreateView blah blah blah

}

这里是 Fragment B。
public class FragmentB extends Fragment{

 //onCreateView blah blah blah

}

这是我的FragmentActivity,它将管理它们两个。

public class MainActivity extends FragmentActivity{

//onCreate 
//set up your fragments

}

假设您已经有了类似的代码,现在我们来看如何更改FragmentA(即需要获取一些数据的列表片段)。

    public class FragmentA extends ListFragment implements onListItemSelectedListener, onItemClickListener{

OnListItemSelectedListener mListener;

   //onCreateView blah blah blah



 // Container Activity must implement this interface
    public interface OnListItemSelectedListener {
    public void onListItemSelected(int position);
}


}


  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);

    // This makes sure that the container activity has implemented
    // the callback interface. If not, it throws an exception
    try {
        mListener = (OnListItemSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnListItemSelectedListener");
    }
}


  @Override 
 public void onItemClick(AdapterView<?> parent, View view, int position, long id){


 //Here's where you would get the position of the item selected in the list, and  pass    that position(and whatever other data you need to) up to the activity 
//by way of the interface you defined in onAttach

  mListener.onListItemSelected(position);


}

在这里最重要的考虑因素是你的父Activity实现了这个接口,否则你将会得到一个异常。如果成功实现,每次当列表片段中的一个项目被选中,你的Activity将会收到它的位置通知。显然,你可以使用任何数量或类型的参数修改你的接口,在这个例子中,我们只传递了整数位置。希望这能稍微解释一下,祝你好运。

2
我认为你的第二个选项是正确的。
在你的片段中,定义监听器接口:
class MyFragment ...
{
    public interface IMyFragmentListenerInterface
    {
        void DoSomething();
    }
}

让活动实现这个接口:

class MyActivity
{
    class MyListener1 implements IMyFragmentListenerInterface { ... }
}

将listener传递给Fragment。我喜欢在Fragment的构造函数中执行此操作,但仅当您完全自己管理片段时才有效。您也可以向Fragment添加一个setListener方法。

3
“setListener”方法存在问题:如果Fragment被操作系统销毁并重新创建(使用默认的空构造函数),则对监听器的引用将丢失。最好的解决方式是在“onAttach”方法中编写代码,将Fragment的父Activity作为监听器连接起来,并在运行时检查它是否支持所需的接口。请参阅http://developer.android.com/guide/components/fragments.html:“将事件回调到Activity”。 - Carlos P

0
你尝试过像这样的东西吗(来自Fragment):
FragmentTransaction ft = 
    getActivity().getSupportFragmentManager().beginTransaction();
Fragment prev = 
    getActivity().getSupportFragmentManager().findFragmentByTag("some_name");
if (prev != null) {
    ft.remove(prev);
}
ft.addToBackStack(null);

DialogFragment dialogFragment = DialogFragmentClass.newInstance();
dialogFragment.show(ft, "some_name");

请告诉我,谢谢。


0
为了实现松耦合,您可以使用Square的OTTO或GreenRobot的EventBus等事件总线。您的片段可以触发事件,这些事件由您的活动处理,反之亦然。这个酷炫的事情是,组件(活动、片段)彼此不知道,您也不需要声明任何接口或回调。
我在所有项目中都使用它,它很强大,在正常情况下对性能几乎没有影响。

0

只需遵循文档

片段

public class HeadlinesFragment extends Fragment {

    // Container Activity must implement this interface
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    OnHeadlineSelectedListener mCallback;

    // "Bind" to the Activity where this Fragment lives
    public void setOnHeadlineSelectedListener(Activity activity) {
        mCallback = (OnHeadlineSelectedListener) activity;
    }

    // ...

    // This will send info to the Activity where the Fragment lives
    private void someOtherFunctionOrListener(int position) {
        mCallback.onArticleSelected(position); // pass info into Activity
    }
}

活动

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    // ...

    @Override
    public void onAttachFragment(Fragment fragment) {
        if (fragment instanceof HeadlinesFragment) {
            HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
            headlinesFragment.setOnHeadlineSelectedListener(this);
        }
    }

    @Override
    public void onArticleSelected(int position) {
        // Call received from Fragment
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接