Reputation: 668
I am working on a project where I have to pass data between fragments. All the data is provided from my DatabaseHandler class
(SQLiteOpenHelper). I'm writing this post by hand, so ignore syntax errors, if any.
Here's what my Activity
looks like(contains SelectionFragment and InfoFragment):
GridView
)ViewPager
with about 5-6 child fragments
)
Here's what I've done so far:
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 extends FragmentActivity implements 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 extends 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;
}
}
Here are my Layouts:
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>
The app currently passes info from selectFrag to Activity, which transfers it to infoFrag. I would like for this ID to reach All the way to the child Fragments. I was thinking of maybe implementing the MyCommunicator to InfoFrag and passing the data to children Fragments, but wouldn't that throw a NullPointerException for the childFragments that are not visible.
*Note: * The selectFrag and InfoFrag are always visible in the Activity. InfoFrag has to refresh when a new item is selected in selectFrag's GridView.
Thanks for your help!
EDIT:
I've tried using the "communication interface", but I get lost every time :( lol.. Can someone plz give me a brief way to do this? Can all my childrenFragments implement the communicator from selectFrag? If yes, is that the right way to do it? I think InfoFragment should implement the interface and pass the data to its' children. But I don't know how to do that and refresh the children. Another thing I was thinking of doing was storing lastItemClicked in sharedPreferences and then accessing the Object from my db. I'm really confused :/ Thanks once again!
Upvotes: 3
Views: 3990
Reputation: 27236
I think you are facing a different problem.
You need to create the following components:
I'll outline a super simplified version.
Interface: This is simple:
public interface DataChangeListener {
void onDataChanged();
}
List Of Listeners: This is easy too… in your Controllers/Activities/Singletons or anywhere you need to perform actions and notify listeners…
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);
}
}
Then your fragments… (for example)
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);
}
}
So far, you register when you are resumed and remove when you're no longer visible.
You also need to implement the method…
@Override
public void onDataSetChanged() {
// DO WHATEVER YOU NEED TO DO
}
And finally, have a notify method in your controller (the same one which had the list)
private void notifyListeners(){
for (DataChangeListener listener : mListeners) {
if (listener != null) {
listener.onDataSetChanged();
}
}
}
This pattern applies to anything, it's a nice and simple way to have your UI notified that you have retrieved new data, that an adapter must be notified, etc.
Rethink your problem using something like this and things will be a lot easier.
Upvotes: 6
Reputation: 668
Found a solution for now. Instead of communicating with the child fragment. I ended up reinstantiating the ViewPager every time an item was selected in the GridView (calls changeObj in infoFrag). I guess I should have used different keywords earlier to find the solution! :/ Replacing a fragment from ViewPager android
Upvotes: 1