Android 片段基础知识:为什么?这在概念上是错误的吗?

Android Fragments fundamentals: why? Is this conceptually wrong?

我对 Android 中的 "proper programming" 有疑问。

我目前正在使用片段开发应用程序。它涉及动态添加到 Activity 的片段、从 XML 膨胀的片段、来自 XML 的嵌套片段或动态添加。随便说说吧,什么都有。

本题关注的概念是片段所涉及的沟通过程。所以,我已经阅读了文档,这不是我第一次尝试使用片段。

常识(和文档)告诉我们,如果一个 Fragment 想要与它说话或交流 activity,我们应该使用一个接口。

示例:

测试片段

public class TestFragment extends Fragment {

  private TestFragmentInterface listener; 

  public interface TestFragmentInterface {

      void actionMethod();

  }


  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {

      if (getActivity() instanceof TestFragmentInterface) {
          listener = (TestFragmentInterface) getActivity();
      }

      // sending the event
      if (listener != null) listener.actionMethod();
  }

}

测试Activity

public class Test implements TestFragmentInterface {

  @Override
  public void actionMethod() {
    ..
  }
}

一切都很好。

这提高了可重用性,因为我的 TestFragment 这种方式可以与任何类型的 Activity 交互,因为 Activity 实现了我声明的接口。

相反,Activity 可以通过持有引用并调用其 public 方法与片段交互。这也是片段到片段通信的建议方式,使用 Activity 作为桥梁。

这很酷,但有时感觉为此使用一个接口有点不对劲 "too much"。

问题A

在这种情况下,我附加的片段具有非常集中的作用,这意味着它们是针对特定的 activity 完成的,不会在其他情况下使用,忽略接口实现而只是做某事在概念上是错误的喜欢

((TestActivity) getActivity().myCustomMethod();

?

这也适用于(不是我的情况,只是将其视为 "at its worst")我的 activity 必须处理各种不同片段的情况,这意味着它应该为它应该处理的每个片段实现一个方法。这使代码变得一团糟 "potentially not-needed lines".

更进一步:仍然使用 "focused" 片段,旨在仅在特定方式下工作,使用嵌套片段有什么用?

一样添加了它们
public class TestFragment extends Fragment {


  private void myTestMethod() {

    NestedFragment nested = new NestedFragment();

    getChildFragmentManager()
      .beginTransaction()
      .add(R.id.container, nested)
      .commit();
  }

}

这会将 NestedFragment 绑定到 TestFragment。我再说一遍,NestedFragment和TestFragment一样,只能这样使用,否则没有意义。

回到问题,遇到这种情况我应该怎么做?

B题

1) 我应该在 NestedFragment 中提供一个接口,并使 TestFragments 实现 NestedFragmentInterface 吗?在这种情况下,我将采取以下行动

嵌套片段

public class NestedFragment extends Fragment {

  private NestedFragmentInterface listener; 

  public interface NestedFragmentInterface {

      void actionMethodNested();

  }


  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {

      if (getParentFragment() instanceof NestedFragmentInterface) {
          listener = (NestedFragmentInterface) getParentFragment();
      }

      // sending the event
      if (listener != null) listener.actionMethodNested();
  }

}

2) 我应该(或可以)忽略接口,只调用

getParentFragment().publicParentMethod();

?

3) 我应该在 NestedFragment 中创建接口,但让 activity 实现它,以便 activity 会调用 TestFragment 吗?

C题

关于使用 Activity 作为片段之间桥梁的想法,我相信这样做是为了正确处理所有这些对象的生命周期。在尝试手动处理系统可能抛出的异常时,直接片段到片段(使用接口或直接调用 public 方法)是否仍然可行?

我会尽力回答这里的文字墙:)

问题一:

Fragments 被设计为可重复使用的模块,可以与任何 activity 即插即用。因此,与 activity 接口的唯一正确方法是让 activity 从片段理解的接口继承。

public class MapFragment extends Fragment {

  private MapFragmentInterface listener; 

  public interface MapFragmentInterface {

      //All methods to interface with an activity

  }


  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {
      // sending the event
      if (listener != null) listener.anyMethodInTheAboveInterface();
  }

}

然后让activity实现接口

public class MainActivity extends Activity implement MapFragmentInterface{

//All methods need to be implemented here
}

这允许您的片段与任何 activity 一起使用,只要 activity 实现此接口。你需要这个接口的原因是因为片段可以与任何activity一起使用。调用类似

的方法
((TestActivity) getActivity().myCustomMethod();

依赖于这样一个事实,即您的片段只能在测试中工作 activity,因此 "breaks" 片段规则。

问题 B 和 C:

假设您遵循正确的片段指南并且它们是独立的模块。那么你永远不应该出现片段需要相互了解的情况。 99% 的时间人们认为他们需要片段来直接交流,他们可以通过使用 MVC 模式或类似的东西将他们的问题重构为我在上面给出的情况。让 activity 像控制器一样工作,告诉片段何时需要更新,然后创建一个单独的数据存储。

我会尽量把它全部清除。

首先,考虑一下你为片段设置监听器的方法。在 onViewCreated 方法中设置监听器是不好的,因为它会导致过度重置监听器任何创建的片段。将其设置为 onAttach 方法就足够了。

我讲了代码行。请注意,让 BaseFragment 在您的应用程序中实现常见行为是很好的,因为设置 FragmentListener 从资源创建视图。

不仅如此,为了减少代码行数和获得部分代码重用,您还可以在 BaseFragment 中使用泛型。那么请看下一个代码片段:

public abstract BaseFragment<T extends BaseFragmentListener> extends Fragment {

  T mListener;

  public void onAttach(Activity activity) {
    super.onAttach(activity);
    if (Activity instanceof T)
      mListener = (T) activity; 
  }

  abstract int getLayoutResourceId();

  @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View layout = inflater.inflate(getLayoutResourceId(), null);
        // you can use some view injected tools here, mb ButterKnife 
        return layout;
    }
}

答案 A(针对问题 A):

如果您只有一个 activity 片段,您需要决定:"Do you really need to use Fragment here?"。但是 mb 最好有一个 activity 的片段以从 activity 中提取一些视图逻辑并清除基本逻辑。但是要清除应用程序的基本架构逻辑,请使用监听器。这将使其他开发人员的生活更轻松

答案B: 对于您需要解决的嵌套片段,他们需要使用确切的 activity 或只是片段并将其用作连接到其他系统的桥梁。如果您知道嵌套片段将一直嵌套,则需要将父片段声明为侦听器,否则您必须使用其他方法。

注意事项: 作为在 App 的不同部分之间进行通信的基本方法,您可以使用事件,例如也可以尝试查看事件总线。它为您提供了通用的通信方法,您可以提取调用侦听器自定义方法的逻辑等等,所有逻辑都将位于处理事件中,您将拥有一个协调系统进行协作。

答案C: 我部分解释了片段之间合作的一种方法。使用一个事件调度程序可以避免您拥有多个用于所有不同通信的侦听器。有的时候还是很赚钱的。

或者我觉得用Activity或者其他class住在Activity作为Fragments合作的mediator比较好用,因为Fragments变化的情况很多处理和系统的生命周期。而且它将所有这些逻辑集中在一个地方,使您的代码更加清晰。

希望我的考虑对您有所帮助。