Activity 和 Fragment 交互
Posted
技术标签:
【中文标题】Activity 和 Fragment 交互【英文标题】:Activity And Fragment Interaction 【发布时间】:2013-07-01 17:58:59 【问题描述】:我有一个 Activity
和多个 Fragment
s。我想显示DialogFragment
或从Fragment
s 之一打开另一个Fragment
。我知道Activity
应该是负责打开Fragment
s 的人,所以我尝试了几件事。
第一
我尝试使用getActivity()
并对其进行强制转换,以便我可以在Activity
中调用一个方法来显示Fragment
但这会在Fragment
中创建一个与Activity
的依赖关系,我想避免添加一个尽可能依赖。第二次
接下来我尝试了一个监听器来通知Activity
它应该显示Fragment
。所以我在Activity
中创建了一个类来实现监听接口。但是我遇到了问题,因为我必须使用New MyActivity().new Listener();
,当我尝试使用getSupportFragmentManager()
时它会抛出Exception
,因为Activity
的这个实例没有被初始化。THIRD
然后我尝试让Activity
直接实现监听器,因为这样我只创建了一个与监听器而不是活动的依赖关系。但是,现在我的 Activity
将实现 2 到 4 个不同的接口,这让我犹豫不决,因为它会严重降低凝聚力。
所以我尝试过的任何方式似乎都遇到了砖墙并创建了我不确定是否需要创建的依赖项。我搞砸了,必须选择其中一种吗?如果是这样,哪个选项最好?非常感谢任何帮助或建议。
【问题讨论】:
有很多方法可以做到这一点,我更喜欢尽可能解耦的东西,为此我喜欢事件总线。参见 otto,例如:square.github.io/otto。 (让您摆脱所有接口/侦听器的麻烦。传递数据,使用强类型,以清晰简洁的方式进行。) 看起来很有希望。我得去看看。感谢您的提示。 【参考方案1】:创建界面
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);
完成!!。现在您可以从活动中调用片段方法。
【讨论】:
【参考方案2】:我个人认为片段应该被认为是可重用和模块化的组件。因此,为了提供这种可重用性,片段不应该对它们的父活动了解太多。但作为回报,活动必须知道他们持有的碎片。
因此,我认为永远不应该考虑第一个选项,因为您提到的依赖原因会导致非常高度耦合的代码。
关于第二个选项,片段可以将任何应用程序流程或 UI 相关决策(显示新片段、决定触发片段特定事件时的操作等)委托给它们的父活动。所以你的监听器/回调应该是片段特定的,因此它们应该在片段中声明。而持有这些片段的活动应该实现这些接口并决定做什么。
所以对我来说,第三个选项更有意义。我相信活动在特定回调中所持有的内容和所做的事情更具可读性。但是,是的,您是对的,您的活动可能会成为上帝的对象。
如果您不想实现多个接口,也许可以查看 Square 的 Otto 项目。它基本上是一个事件总线。
【讨论】:
感谢您和@Charlie Collins 将我指向 Otto 库。这似乎非常有希望,并且似乎它将完成我正在寻找的东西。感谢大家的帮助。【参考方案3】:您需要将数据从 Fragment X 传递到 FragmentActivity,FragmentActivity 会将这些数据传递到 Fragment Y。您可以通过片段类中定义的接口来执行此操作,并实例化 onAttach 中定义的回调()。
有关如何在此处执行此操作的更多信息 Communication With other Fragments
快速示例,考虑 Fragment A 和 Fragment B。Fragment A 是一个列表片段,无论何时选择一个项目,它都会改变 Fragment B 中显示的内容。够简单吧?
首先,这样定义Fragment A。
public class FragmentA extends ListFragment
//onCreateView blah blah blah
这是片段 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 其位置。显然,您可以使用任意数量或类型的参数来更改接口,在此示例中,我们只是传入整数位置。希望这能澄清一点,祝你好运。
【讨论】:
【参考方案4】:我认为您的第二个选择是正确的。
在您的片段中,定义监听器接口:
class MyFragment ...
public interface IMyFragmentListenerInterface
void DoSomething();
让活动实现接口:
class MyActivity
class MyListener1 implements IMyFragmentListenerInterface ...
将监听器传递给片段。我喜欢在 Fragment 的构造函数中执行此操作,但这只有在您完全自己管理 Fragment 时才有效。您可以将 setListener
方法添加到您的片段中。
【讨论】:
setListener
方法存在一个问题:如果 Fragment 被操作系统销毁并重新创建(使用默认的空白构造函数),则对侦听器的引用将丢失。实现这一点的最佳方法是将代码放入onAttach
方法中,以将片段的父活动挂接到作为侦听器,并在运行时检查它是否支持所需的接口。请参阅developer.android.com/guide/components/fragments.html:“为 Activity 创建事件回调”。【参考方案5】:
你有没有尝试过这样的事情(来自片段):
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");
告诉我,干杯。
【讨论】:
【参考方案6】:要获得最大的松散耦合,您可以使用事件总线,例如 Square 的 OTTO 或 GreenRobot 的 EventBus。 您的片段可以触发由您的活动处理的事件,反之亦然。很酷的一点是组件(活动、片段)彼此之间没有任何关系,您不需要声明任何接口或回调。
我在所有项目中都使用它,它很健壮,对性能的影响很小甚至没有影响(在正常情况下)。
【讨论】:
【参考方案7】:就follow the documentation:
Fragment
:
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
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
【讨论】:
以上是关于Activity 和 Fragment 交互的主要内容,如果未能解决你的问题,请参考以下文章
Android Fragment和Activity的交互介绍