Android Fragments 基础知识:为啥?这在概念上是错误的吗?

Posted

技术标签:

【中文标题】Android Fragments 基础知识:为啥?这在概念上是错误的吗?【英文标题】:Android Fragments fundamentals: why? Is this conceptually wrong?Android Fragments 基础知识:为什么?这在概念上是错误的吗? 【发布时间】:2015-08-27 00:44:07 【问题描述】:

我有一个关于 android 中“正确编程”的问题。

我目前正在使用片段开发应用程序。它涉及到动态添加片段到 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();
  


TestActivity

public class Test implements TestFragmentInterface 

  @Override
  public void actionMethod() 
    ..
  

这里一切正常。

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

反过来,Activity 可以通过持有引用并调用其公共方法与片段进行交互。这也是片段间通信的建议方式,使用 Activity 作为桥梁。

这很酷,但有时感觉为此使用界面有点“太多”。

问题 A

在这个场景中,我附加的片段有一个非常集中的角色,这意味着它们是为那个特定的活动完成的,不会被使用,忽略接口实现而只是做类似的事情在概念上是错误的

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

?

这也适用于(不是我的情况,只是将其视为“最糟糕的情况”)我的活动必须处理各种各样的这些不同片段的情况,这意味着它应该为每个片段实现一种方法它应该处理。这会使代码变成一大堆“可能不需要的行”。

进一步:仍然使用“聚焦”片段,旨在仅在某种方式下工作,嵌套片段的用途是什么?

像这样添加它们

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

【问题讨论】:

看看这是否有帮助:corner.squareup.com/2014/10/… 我已经阅读了您发布的链接,虽然这确实是一篇包含大量信息的有趣文章,但恐怕它并不能回答我的问题。还是谢谢你 【参考方案1】:

我会尽我所能在这里回答文字墙:)

问题 A:

片段被设计成可重复使用的模块,可以随任何活动即插即用。因此,与 Activity 交互的唯一正确方法是让 Activity 从 Fragment 理解的接口继承。

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();
  


然后让活动实现接口

public class MainActivity extends Activity implement MapFragmentInterface

//All methods need to be implemented here

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

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

依赖于这样一个事实,即您的片段只能在测试活动中工作,因此“破坏”了片段的规则。

问题 B 和 C:

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

【讨论】:

然后,从你写的内容来看,我知道我正在做的事情是可能的,但不建议使用,因为它的用法超出了原始范围。它不是有害的东西,只是没有按原意使用。我错了多少? :) 你的 100% 正确。与所有编程一样,有很多方法可以做事。很多时候,我什至会完全按照您的描述进行操作,因为我知道我的片段只会在这一项活动中使用。但是,如果您想“正确”,您应该通过一个界面来使您的代码广告尽可能地解耦。如果您有时间和金钱,您应该始终以正确的方式构建您的应用程序 :)【参考方案2】:

我会试着把它全部清除一下。

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

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

除此之外,为了减少代码行并获得部分代码重用,您可以在 BaseFragment 中使用泛型。所以看下一段代码sn -p:

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):

如果你只有一个片段,你需要决定:“你真的需要在这里使用片段吗?”。但是 mb 最好有一个活动的片段以从活动中提取一些视图逻辑并清除基本逻辑。但是要为您的应用程序清除基础架构逻辑,请使用侦听器。这将使其他开发人员的生活更轻松

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

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

答案 C: 我部分解释了片段之间合作的一种方法。使用一个事件调度器可以避免为所有不同的通信设置许多侦听器。有时它非常有利可图。

或者我认为使用 Activity 或其他生活在 Activity 中的类作为 Fragments 合作的中介更有用,因为在生命周期中,无论是处理还是系统,Fragment 都会发生变化。并且它将所有这些逻辑集中在一个地方,让您的代码更加清晰。

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

【讨论】:

谢谢,这是一个很好的答案,逻辑详细,很有帮助,给了我一些好主意:)

以上是关于Android Fragments 基础知识:为啥?这在概念上是错误的吗?的主要内容,如果未能解决你的问题,请参考以下文章

Fragments的初识---不知道Fragments的不是合格的android开发

Android Fragment使用 嵌套Fragments (Nested Fragments) 的使用及常见错误

在 Android 中使用 Fragments 而不是 Views 有啥好处?

Android Fragment使用 嵌套Fragments (Nested Fragments) 的使用及常见错误

使用 Fragments 进行 Android 搜索

Android11.5 创建和管理Fragments