在嵌套的Fragment之间传递数据

3
我正在处理一个需要在碎片之间传递数据的项目。所有的数据都来自于我的DatabaseHandler class(SQLiteOpenHelper)。我手写了这篇文章,所以请忽略任何语法错误。
以下是我的Activity的样子(包含SelectionFragment和InfoFragment):
  • Selection Fragment(包含一个GridView
  • Info Fragment(带有大约5-6个子碎片ViewPager
    • childFragment1
    • childFragment2
    • childFragment3
    • ...
到目前为止,我已经做了以下工作:

SelectFrag extends Fragment:
MyCommunicator communicator;
GridView myGrid;

onItemSelected(... int position, ...) {
    communicator.respond("The id i get from getTag()..");
}
public void setCommunicator(MyCommunicator communicator) {
    this.communicator = communicator;
}

public interface MyCommunicator {
    public void respond(String id);
}



Activity扩展FragmentActivity并实现SelectFrag.MyCommunicator:

FragmentManager manager;
SelectFrag selectFrag;
InfoFrag infoFrag;

onCreate() {
    manager = getSupportFragmentManager();
    selectFrag = (SelectFrag) manager.findFragmentById(...);
    selectFrag.setCommunicator(this);
}

@Override
public void respond(String id) {
    DatabaseHandler db = new DatabaseHandler(this);
    try {
        CustomObject obj = db.getObj(id);
    } finally {
        db.close();
    }
    infoFrag = (InfoFrag) manager.findFragmentById(...);
    if(obj != null)
        infoFrag.changeObj(id)
    setTitle(obj.getName();
}


InfoFrag扩展了Fragment:

ViewPager pager;
Context context;
MyObj obj;

public void changeObj(String id) {
    DatabaseHandler db = new DatabaseHandler(context);
    this.obj = db.getObj(id);
    db.close();
}

protect class MyPagerAdapter extends FragmentPagerAdapter{
    public Fragment getItem(int i) {
        Fragment frag = null;
        switch(i) {
            case 0:
                frag = new ChildFrag1();
            break;
            case 1:
                frag = new ChildFrag2();
            break;
            ... Goes to case 6 for now.
        }
        return frag;
    }
}

这是我的布局:

select_frag.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SelectFrag" >

    <GridView
        android:id="@+id/selectGridView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:columnWidth="56dp"
        android:background="#dd5500"
        android:horizontalSpacing="5dp"
        android:numColumns="auto_fit"
        android:paddingLeft="10dp"
        android:paddingRight="2dp"
        android:paddingTop="3dp"
        android:stretchMode="columnWidth"
        android:verticalSpacing="5dp" >
    </GridView>

</RelativeLayout>

main_activity.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MyActivity"
    android:orientation="vertical" >

    <fragment
        android:id="@+id/selectFragment"
        android:name="my.package.SelectFragment"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="3" />

    <fragment
        android:id="@+id/infoFragment"
        android:name="my.package.InfoFragment"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="7" />

</LinearLayout>

info_fragment.xml :

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/myPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v4.view.PagerTitleStrip
        android:id="@+id/my_pager_title_strip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:background="#33b5e5"
        android:paddingBottom="4dp"
        android:paddingTop="4dp"
        android:textColor="#fff" />

</android.support.v4.view.ViewPager>

该应用程序目前将信息从selectFrag传递到Activity,然后再传递到infoFrag。我希望这个ID能够一直传递到子片段。我考虑过在InfoFrag中实现MyCommunicator并将数据传递给子片段,但是未显示的子片段是否会抛出NullPointerException呢?
*注意:selectFrag和InfoFrag在Activity中始终可见。当在selectFrag的GridView中选择新项时,InfoFrag必须刷新。
谢谢您的帮助!
编辑:我尝试使用“通信接口”,但每次都会迷失方向。有人可以简要介绍一下如何做吗?所有子片段是否都可以从selectFrag实现通信器?如果是,那么这样做是正确的吗?我认为InfoFragment应该实现接口并将数据传递给其子元素。但是我不知道如何做到这一点并刷新子元素。另一件事我想做的是将lastItemClicked存储在sharedPreferences中,然后从我的数据库中访问对象。我真的很困惑:/ 再次感谢!

1
请查看此链接:https://github.com/greenrobot/EventBus。你可以使用接口作为回调,并将值传递给已在文档中提到的片段,以下是一个示例。 - Raghunandan
非常好!但是如果不使用任何库,什么才是正确的方法? - Aftab Safdar
使用接口作为回调函数 http://developer.android.com/training/basics/fragments/communicating.html - Raghunandan
2个回答

6

我认为你面临的是一个不同的问题。

您需要创建以下组件:

  1. 在片段中实现的接口。
  2. 实现该接口的侦听器列表。
  3. 从该列表中添加/删除项目。
  4. 最终通知您的侦听器发生了某些事情。

我将概述一个超级简化版本。

接口: 这很简单:

public interface DataChangeListener {
    void onDataChanged();
}

监听器列表:这也很简单...在您的控制器/活动/单例或任何需要执行操作并通知监听器的地方...

protected List<DataChangeListener> mListeners = new ArrayList<DataChangeListener>();
    public void addDataChangeListener(DataChangeListener listener) {
        if (listener != null && !mListeners.contains(listener)) {
            mListeners.add(listener);
        }
    }
    public void removeDataChangeListener(DataChangeListener listener) {
        if (listener != null) {
            mListeners.remove(listener);
        }
    }

然后你的片段...(例如)
public class YourFragment extends Fragment implements DataChangeListener {

private SomeControllerForExample mController;

    @Override
    public void onPause() {
        super.onPause();
        mController.removeDataChangeListener(this);
    }
    @Override
    public void onResume() {
        super.onResume();
        mController.addDataChangeListener(this);
        }

}

目前,当您恢复时注册,不可见时删除。

您还需要实现该方法...

@Override
public void onDataSetChanged() {
        // DO WHATEVER YOU NEED TO DO
}

最后,在您的控制器中添加一个通知方法(与列表所在的控制器相同)。
private void notifyListeners(){
    for (DataChangeListener listener : mListeners) {
        if (listener != null) {
             listener.onDataSetChanged();
        }
    }
}

这种模式适用于任何情况,它是一种很好而简单的方法,可以让您的用户界面通知您已检索到新数据,必须通知适配器等。

使用类似这样的东西来重新思考您的问题,事情会变得更加容易。


1
搞定了!是的!谢谢哈哈。只需要一个小小的解释就行了。真不敢相信我之前犯了这么愚蠢的错误(上次尝试时)。再次感谢!可惜要等19小时才能颁发赏金 :/ - Aftab Safdar

1
我已经找到了一个临时解决方案。与其与子片段通信,我最终选择在GridView中选择项目时每次重新实例化ViewPager(调用infoFrag中的changeObj)。 我想我早些时候应该使用不同的关键字来寻找解决方案!:/ 从ViewPager android替换片段

2
是的,但这是一个非常糟糕的想法。它会使用CPU、电池,强制进行整个对象垃圾回收等。为什么不只是实现一个基本的监听器/回调接口来与您的片段通信呢? - Martin Marconcini
如果是这种情况,我将取消批准我的答案!我是Android的新手,不知道如何做。我通过Activity完成了它,但不知道如何从Fragment向childFragment发送信息。 - Aftab Safdar

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