Android优雅地从活动中pipe理片段

我试图完成的描述:我有一个应用程序,使用一个LinearLayout的FragmentActivity作为碎片的容器。 我点击FragmentActivity UI上的不同button,在FragmentActivity中添加和删除碎片到容器中。 除了单击FragmentActivity UI上的button外,每个Fragment还具有可以单击的button,这些button将删除当前片段并在其位置添加不同的片段。

根据我的理解,Android的做事方式是:我一直在阅读如何做到这一点,据我所知,“正确”做事的方式是使用FragmentActivity作为中继站,并将每个片段做FragmentActivitycallback交stream事件和处理它们。

场景:假设FragmentActivity正在显示FragmentA,当用户点击FragmentA中的一个button时,我想停止显示FragmentA并开始显示FragmentB。 为此,我在FragmentA中创build了一个名为AListener的接口。 在FragmentA的onAttach()方法中,我使用了build议的方法来检查FragmentActivity是否实现了AListener。 当单击FragmentA中的button时,我使用AListener的一个callback方法将点击事件传递给FragmentActivity。 在FragmentActivity中,我创build了一个FragmentB实例,并将其添加到FragmentActivity中的容器中。 然后,如果在FragmentB中发生了某些事件,我使用相同的scheme将事件传递给FragmentActivity并做一些有趣的事情。

所以有什么问题? 对于我的应用程序,我发现这个scheme将Fragments调用回FragmentActivity,然后让FragmentActivity创build一个新的片段或调用前向和现有的片段非常麻烦。 我有很多片段需要通过FragmentActivity来显示,因此我正在为需要显示的每种types的片段实现一个接口(每个片段是不同的,因此它们都有自己的接口)。 这会导致冲突,当我有两个接口具有相同的方法签名,我不得不重命名其中一个方法。

例如,如果我想使用片段的onAttach()方法将侦听器附加到片段,那么我的FragmentActivity必须实现该接口。 我发现有几个实例,我有callback方法具有相同的名称(或者我被迫命名为类似,但由于名称空间冲突不同)。 一个解决scheme是使用匿名类作为callback,而不是让FragmentActivity实现接口。 这似乎工作得不错,但是违背了Android文档中关于使用onAttach()方法设置侦听器的说法。

有没有什么优雅的方法来解决这个问题? 在我看来,权衡是你要么迫使FragmentActivity为每个要显示的片段实现一个接口,并有注意方法签名冲突的乐趣问题,要么违背Android文档并使用匿名类来处理callback(不确定的影响)。

我对Java相当陌生,觉得我可能会错过一个能够解决我的问题的概念。 任何人都可以直接就如何优雅地解决这个问题?

Solutions Collecting From Web of "Android优雅地从活动中pipe理片段"

我完全理解你的问题,因为我处理了很长时间。 这里是我现在提出的解决scheme! 它可能需要根据您的需要进行一些修改,但是我工作的很好。

首先要在你的应用程序中使事件通信更容易使用EventBus! 这里是最有名的一个https://goo.gl/nAEW6事件总线允许你从任何地方发送事件到任何地方,而无需担心实现接口,广播接收器,线程等。

然后将FragmentOrganizer添加到您的应用程序。 这是您所有片段组织者的基础课程。 基本上每个活动都需要一个。 这是代码

public abstract class FragmentOrganizer { protected FragmentManager fragmentManager; public FragmentOrganizer(FragmentManager fragmentManager) { this.fragmentManager = fragmentManager; openFragment(getInitialFragment()); EventBus.getDefault().register(this); } protected abstract Fragment getInitialFragment(); protected abstract void onEvent(Object event); public abstract boolean handleBackNavigation(); public void freeUpResources(){ EventBus.getDefault().unregister(this); } protected Fragment getOpenFragment(){ String tag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1).getName(); return fragmentManager.findFragmentByTag(tag); } protected boolean isFragmentOpen(Fragment fragment){ return isFragmentOpen(fragment, true); } protected boolean isFragmentOpen(Fragment fragment, boolean useArgs){ String fragmentTag = createFragmentTag(fragment, useArgs); if (fragmentManager.getBackStackEntryCount() != 0) { String name = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName(); if(!useArgs) name = name.substring(0, name.indexOf("-")); return name.equals(fragmentTag); } return false; } private String createFragmentTag(Fragment fragment, boolean addArgs) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(fragment.getClass().getSimpleName()); if(addArgs) { stringBuilder.append("-"); if (fragment.getArguments() != null) stringBuilder.append(fragment.getArguments().toString()); } return stringBuilder.toString(); } public void openFragment(Fragment fragment) { if(isFragmentOpen(fragment)) return; String fragmentTag = createFragmentTag(fragment, true); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.replace(R.id.activity_main_fragment_container, fragment, fragmentTag); transaction.addToBackStack(fragmentTag).commit(); } } 

现在您需要创build从FragmentOrganizerinheritance的片段pipe理器,并实现3个必需的方法。 这里是示例

 public class MainFragmentOrganizer extends FragmentOrganizer { public MainFragmentOrganizer(FragmentManager fragmentManager) { super(fragmentManager); } @Override protected Fragment getInitialFragment() { return HomeFragment.newInstance(); } @Override public void onEvent(Object event){ if(event instanceof ClickedOnPhotoEvent){ String photoCode = ((ClickedOnPhotoEvent) event).photoCode; openFragment(PhotoFragment.newInstance(photoCode)); } } @Override public boolean handleBackNavigation(){ Fragment fragment = getOpenFragment(); if (fragment instanceof HomeFragment){ return false; } else { fragmentManager.popBackStack(); return true; } } } 

而在你的活动中,你只需要让你的FragmentManager变得更有魅力就行了。

 fragmentManager = getSupportFragmentManager(); fragmentOrganizer = new MainFragmentOrganizer(getSupportFragmentManager()); @Override public void onBackPressed() { //first let fragment organizer handle back. If it does not activity takes cares of it! if(!fragmentOrganizer.handleBackNavigation()){ finish(); } } @Override protected void onDestroy() { fragmentOrganizer.freeUpResources(); super.onDestroy(); } 

它可能看起来很多代码,但是当你看到大部分封装在FragmentOrganizer基类中的代码时,它会执行所有的一般工作,所以你只需要将这个文件从一个项目复制到另一个。

正如我刚开始所说的,我现在刚刚提出了这个解决scheme,所以可能并不完美。 我计划在我的下一个项目中使用这个,我希望你能做到。 如果你真的认真分享你的意见。 玩的很开心

我的一位同事提出了我认为是这个问题的一个很好的解决办法。

请记住,我们试图实现的是一个片段callback到父活动的方式,而不需要活动实现接口。 另外,如果活动被销毁并重新创build,我们需要能够自动设置监听器。

活动有一个称为onAttachFragment(片段片段)的生命周期callback,每当一个片段被附加到活动时被调用。 所以,例如,当一个新的片段被创build在活动中,这被调用。 如果之前被销毁的活动被重新创build,它也会被调用。 你可以做的是使用一个接口或匿名类来设置onAttachFragment中的新片段上的侦听器,如下所示:

 @Override public void onAttachFragment(Fragment fragment) { super.onAttachFragment(fragment); //Determine which fragment this is by checking its tag if(fragment.getTag().contains(TextFrag.FRAG_TAG)){ //set a listener on this fragment using an anonymous class or interface ((TextFrag)fragment).setListener(new TextFragButtonListener() { @Override public void onButtonClicked() { count++; counterTV.setText(String.valueOf(count)); } }); } } 

使用这种技术,我们可以避免活动必须实现callback接口,因此我们避免了与callback方法的命名冲突。 另外,如果活动被破坏,一旦重新创build,侦听器将自动重置,所以我们的callback将仍然有效。

也许有很多其他的方法可以做到这一点,我很想在这里任何人对这种技术的批评和任何其他技术的build议。