在ViewPager中多个Fragment之间的通信对象

17

我在ViewPager中有5个片段,用来逐步填充业务对象的多个字段,每一步都会设置其中一些字段。我阅读了许多关于片段之间通信的文章,但并不喜欢其他人提倡的方式,所以在思考如何在我的情况下处理这个问题后,我最终开始考虑使用单例模式对象,在特定步骤中,所有片段都可以轻松访问其字段并将它们填入。

由于我是Android新手,我想听听专家对使用单例模式而非传递数据之间的建议,例如实现接口(听说很复杂且难以维护)。任何建议都将是有帮助的。


我不知道你的确切问题是什么,但你考虑过在片段之间使用接口(并在OnPageChangeListener中使用它们)吗? - Valentino
是的,这个通用接口实现起来既昂贵又太复杂了,涉及到activity、fragments、viewpager等等。想象一下,你必须改变步骤的优先级或在步骤之间添加更多步骤。 - Saber Amani
我要发布一些东西,可能不符合你的要求。请尽快查看。 - Valentino
@AnuragSingh 在ViewPager中创建第一个Fragment,在将业务数据发送到服务器后销毁它,所有在ViewPager中的Fragment都可以访问业务模型对象,直到对象填充完成。适配器不需要扩展FragmentStatePagerActivity。 - Saber Amani
1
你考虑过使用“SharedPreferences”吗?还是它太复杂了? - Valentino
显示剩余6条评论
7个回答

6

虽然单例模式看起来易于实现和理解,但这并不是实现所需功能的最佳方式。其中一个原因是您的模型对象或业务对象存在于活动上下文之外,这可能会导致难以找到的错误。例如,在系统创建多个活动类实例且都保留对您的单例的引用的情况下。看看您如何追踪对象?

我会做的是

  1. 将我的模型对象实现Parcelable,开始时你会讨厌它,但一旦习惯了,它就会成为你模型的好帮手。
  2. 由于你的模型现在是可传递的,你可以轻松地在不同的片段、活动之间传递它,甚至可以将其保存在共享首选项中。需要注意的一点是当你在片段或活动之间传递你的可传递数据时,它就像是按值传递,即每次都会创建新的实例。
  3. 设置你的片段参数,如果已经实例化则获取参数并添加你的模型。这里有一个示例:

    如果片段还未激活:

    Bundle args = new Bundle(); args.putParcable("businessObject", yourBusinessObjectThatIsParcable); yourFragment.setArguments(args);

    否则: yourFragment.getArguments().putParcelable("businessObject", yourBusinessObjectThatIsParcable);

  4. 在你的片段中,例如在onCreateView方法中,通过如下方式获取你的模型对象:MyParcableObject mpo = (MyParcableObject)getArguments().getParcelable("businessObject"),然后使用它来设置任何你想要的数据。

  5. 当你在按钮单击或onPause方法中完成编辑对象时,更新你的片段参数同样通过getArguments().putParcelable("businessObject", mpo);

  6. 在你的最后一页或最后一个片段中,你可以将你的对象传递给你的活动,这里是如何实现的。

尽管看起来有些繁琐,但作为 Android 开发人员,这是你需要习惯的一种实践。当你的模型实现了 "parcelable" 后,你将获得更多的控制权。
另一种实现你所需的方法是通过 委托模式,但它主要用于回调,即使你也可以传递对象。

6
我不建议使用全局单例模式。原因如下:
  1. 按照定义,单例模式限制了应用程序只能有一个主要业务对象的实例。如果您(或设计师、上司的上司的上司)决定同时拥有多个这些ViewPagers,您将不得不改变架构。
  2. “Android方式”的思考是期望用户在将您的应用程序放在后台并使用其他应用程序之前。如果系统决定在后台杀死您的应用程序,则您的单例内存对象将被销毁,用户将失去所有进度。正确的Android保存状态方式是在Activity或Fragment中保持状态,适当地在onSaveInstanceState()中保存它,并在onCreate()中恢复它。

ViewPager中的所有片段都可以通过调用getActivity()获取对父Activity的引用。或者,如果您的ViewPager在一个Fragment中,则所有片段都可以通过调用getParentFragment()来访问父Fragment。然后,您可以将结果强制转换为适当的类(最好是接口),并进行方法调用以传递数据。在父Activity/Fragment中跟踪业务数据。这样,您就不需要全局单例了。

例如,
public class MyParentFragment extends Fragment {

  private String mPageOneData;
  private int mPageTwoData;
  private List<Date> mPageThreeData;

  public void setPageOneData(String data) {
    mPageOneData = data;
  }

  ...
}

public class PageOneFragment extends Fragment {

  private void sendDataToParent(String data) {
    Fragment f = getParentFragment();
    if (f != null && f instanceof MyParentFragment) {
      MyParentFragment parent = (MyParentFragment) f;
      f.setPageOneData(data);
    }
  }

}

2
您IP地址为146.190.162.183,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。

1

你看过EventBus了吗?

我不确定它是否是最好的方法,特别是当你的问题太广泛时,但只需要5个片段就很酷。

希望这有所帮助。


0
在选择任何选项之前,请记住用户可以导航或打开任何其他应用程序,因此您会丢失数据。
您可以使用onSaveInstanceState,但是它将有些难以维护(正如您所说,您是Android的新手)。您可以使用单例来进行操作
  • 数据库 - 当您想要存储多个记录但必须创建数据库getter / setter或使用任何ORM(例如RushOrm)时使用。
  • SharefPreference(最好)- 如果您想要使用单个值。
在这两种情况下,您将创建一个单例对象并访问其属性。

0

0
我猜在你的MainActivity中有一个ViewPager,而FragmentOne将是ViewPager内的一个片段。在这里,MainActivity正在与FragmentOne通信以刷新其适配器。希望清楚明白。
在你的MainActivity中添加这个接口:
public interface Updateable {
    public void update();
}

在需要更新的fragment中实现此接口,并编写代码以在update方法内通知适配器:

public class FragmentOne extends Fragment implements MainActivity.Updateable {
    ...
    @Override
    public void update() {
        // YOUR CODE TO UPDATE HERE, FOR EXAMPLE, HERE I'M UPDATING THE ADAPTER
        if ( adapter != null ) {
            adapter.notifyDataSetChanged();
        } else {
            Log.d("LOG_TAG", "null");
        }
    }
    ...
}

当片段首次加载时,从MainActivity调用update方法。您可以通过覆盖PagerAdapter中的getItemPosition方法来实现此操作,如下所示:

@Override
public int getItemPosition(Object object) {    
    if ( object != null && object instanceof FragmentOne ) {
        FragmentOne f = (FragmentOne) object;

        f.update();
    }
    return super.getItemPosition(object);
}

最后,您需要调用您的viewPager适配器的notifyDataSetChanged()方法。这将强制viewpager的适配器调用getItemPosition方法。
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        int previousState;
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

            if (previousState == ViewPager.SCROLL_STATE_SETTLING && state == ViewPager.SCROLL_STATE_IDLE) {

                if ( viewPagerAdapter.getItem(viewpager.getCurrentItem()) instanceof Pictures ) {
                    Log.d("LOG_TAG", "New Position=" + viewpager.getCurrentItem());

                    viewPagerAdapter.notifyDataSetChanged();
                }

            }
            previousState = state;
        }
    });

感谢您的努力,但很遗憾这不符合我的要求。我有一个 Activity 和其中的一个 Fragment,Activity 中的 Fragment 有一个视图页(viewpager),而视图页中的每个页面都有自己的 Fragment。我认为我应该使用单例模式,这样更清晰易于维护。 - Saber Amani
对于你的情况(包括视图页面中的片段和片段内的视图页面),我可以使用与活动和片段相同的方法。然而,你要知道它是否适用或不适用。 - Valentino

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