22

I have a ViewPager and it is using a FragmentAdapter in order to display several fragments of the same kind. Although these Fragments are basically instantiated from the same class, they are using a ListView to display different information. (Obviously the ListView is being poulated by an ArrayAdapter.)
A background service is also running and is constantly receiving data from the Internet. I want to be able to update a specific Fragment in the ViewPager when my background service has notified me of a specific event.
How can I do that? A code snippet would be hugely appreciated!

(By the way, I have saw this similar question but I have no idea how to use their suggestion!)

To make it all more simple: My activity with the ViewPager:
[Fragment 0] [Fragment 1] [Fragment 2]

The background service tells me (via a broadcast) to update the ListView in Fragment 1.

EDIT: Here are sample codes:

public class ChatWindowPager extends FragmentActivity
{
    private ViewPager mViewPager = null;
    private ChatFragmentAdapter mAdapter = null;
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chat_window_pager);

        this.mViewPager = (ViewPager) findViewById(R.id.chatPager);
        this.mAdapter = new ChatFragmentAdapter(getSupportFragmentManager());
        this.mViewPager.setAdapter(this.mAdapter);
        .
        .
        .
    }
    
    class ChatFragmentAdapter extends FragmentPagerAdapter implements ViewProvider
    {

        public ChatFragmentAdapter(final FragmentManager fm)
        {
            super(fm);
        }

        @Override
        public Fragment getItem(final int arg0)
        {
            String friendId = ..... // Some initializations
            ChatWindowFragment f = ChatWindowFragment.newInstance(friendId);
            return f;
        }

        @Override
        public int getCount()
        {
            ...
        }

        @Override
        public View getView(final int position)
        {
            View v = getLayoutInflater().inflate(R.layout.tab_holder, null);
            .
            .
            .
            return v;
        }
    }
}

Now the fragments is defined like this:

public class ChatWindowFragment extends Fragment
{
    public String friendId;
    private ListView lv;
    
    public static ChatWindowFragment newInstance(final String friendId)
    {
        ChatWindowFragment chatWindowFragment = new ChatWindowFragment();
        Bundle bundle = new Bundle();
        bundle.putString("friendId", friendId);
        chatWindowFragment.setArguments(bundle);
        return chatWindowFragment;
    }

    @Override
    public void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        this.friendId = getArguments().getString("friendId");
    }

    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState)
    {
        View v = inflater.inflate(R.layout.chat_window, container, false);

        this.friendId = getArguments().getString("friendId");
        .
        .
        .
        return v;
    }
    
    //The rest of the class

}

As I am using a FragmentPagerAdapter I don't see how I can set the tag of each fragment! (Obviously, I am not using transactions to add the Fragments!)

EDIT 2: I would like to know whether what I'm doing, is the correct way to handle what I want to do... Any other solution is also welcome!

Community
  • 1
  • 1
Maghoumi
  • 3,295
  • 3
  • 33
  • 49

7 Answers7

40

Try this,

Register a broadcast receiver in all your fragments... like this

create a class which extends a broadcast receiver in all the classes, for eg:

public class FragmentReceiver1 extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
        }    
}

and register this receiver in you fragment's onCreate ...

for eg. getActivity().registerReceiver(new FragmentReceiver1(), new IntentFilter("fragmentupdater"));

Now assign a unique id to each of you fragment like 1 for Fragment1, 2 for Fragment2 and likewise

now whenever you want to pass any data and update any of the fragment just send a broadcast with the data in intent and "fragmentupdater" as the intent-filter...

For eg:

Intent data = new Intent("fragmentupdater");
data.putString("key","data");
data.putInt("fragmentno",1); // Pass the unique id of fragment we talked abt earlier
activity.sendBroadcast(data);

Now each of your fragment will receive the data but you can verify if the data if for the same fragment by the unique id we passed in it in the onReceive function..., the intent which you get, is the intent we passed above

Thomas H.
  • 653
  • 7
  • 18
King RV
  • 3,760
  • 1
  • 15
  • 16
  • 13
    If you aren't using multiple processes, please consider using http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html instead of global broadcasts. Of course you can always just implement your own observer pattern to have the fragment register with the service to be told when a change it is interested in happens. – hackbod Feb 01 '12 at 07:49
  • 1
    @King RV I have awarded you the bounty and suggested an edit for your post! Thank you... What you suggested gets the job done, I wonder if there's a better/more simple solution for this problem! Thanks again – Maghoumi Feb 01 '12 at 18:00
  • This is a really complicated solution (although it works). Is there any simpler solution to reload the viewpager's content? Such as remove all child views and load them again. – Nguyen Minh Binh Dec 04 '12 at 16:25
  • As a note, I had to register the receiver inside the fragment's onAttach method. It would not fire if I registered it inside onCreate. Not sure why... – joepetrakovich Sep 28 '13 at 03:12
  • In what method should it be un-registered ? – dev Mar 26 '14 at 19:53
5

Have you tried FragmentManager.findFragmentByTag()

FragmentManager manager = getSupportedFragmentManager();
//with support package, else
//FragmentManager manager = getFragmentManager()
Fragment fragment = manager.findFragmentByTag("Tag You Created the Fragment");
if (fragment instanceof Fragment1){
    Fragment1 fr = (Fragment1)fragment
    fr.updateData(DATA)
    //or any method of your choice
} 

EDIT: I read carefully! The instanceOf will cast a Fragment into your Fragment class. It was you, who suggested Fragment1 as a name for simpicity. Also, you didn't provide any source to help us. It is true, that you cannot set a Fragment's tag, but why do you think you are able to get its tag? Usually a Fragment is added through FragmentManagers like

FragmentManager manager = getSupportedFragmnentManager()
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(int containerViewId, Fragment fragment, String tag);
// or transaction.add(Fragment fragment, String tag)
// ...other transactions
transaction.commit()

EDIT2: it's very easy though. according to your code you could just call

Fragment fragment = mAdapter.getItem(0) // 0||1||2

You should consider reading the docs (i.e about FragmenPagerAdapter) and post your source code so we don't have to guess what you need.

Paul
  • 4,160
  • 3
  • 30
  • 56
Rafael T
  • 15,401
  • 15
  • 83
  • 144
  • Did you read my question carefully? I said, all fragments are instantiated from the same class! So obviously I cannot use "instanceof". Also, as far as I know, there is no "setTag()" method available on the fragment. So I might not be able to initialize a fragment using a tag! Any help is greatly appreciated! – Maghoumi Jan 22 '12 at 07:03
  • Look at the edit part! I still don't know what to do so any suggestion is hugely appreciated! – Maghoumi Jan 22 '12 at 16:21
2

I had the same issue but fixed it with a localBroadcastReceiver like this:

Create a receiver in your activity and register it:

 /**
 * ******************************
 * Receiver to process the message
 * *******************************
 */
private BroadcastReceiver onNotice = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //You can send any extra data if you need like this
        final int type = intent.getIntExtra("fragment.data", -1);
        Log.d(tag, "main class: " + type);
        //also refresh your fragment like this
        mViewPager.getViewPager().getAdapter().notifyDataSetChanged();
    }
};    

@Override
protected void onResume() {
    super.onResume();

    //Register a localbroadCast with the your filter
    IntentFilter thinaireFilter = new IntentFilter("your.filter");
    LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, thinaireFilter);
}

Remember to remove LocalBroadCast

//remove the LocalBroadCast when no need it
@Override
protected void onPause() {
    super.onPause();
    LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);
}
@Override
protected void onDestroy() {
    super.onDestroy();
    LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);
}

Send your broadcast from anywhere you want Adapters, services, etc.

Intent sendBroadCastData = new Intent("your.filter");
sendBroadCastData.putExtra("fragment.data", myData);
LocalBroadcastManager.getInstance(context).sendBroadcast(sendBroadCastData);

Hope it helps others.

Rensodarwin
  • 286
  • 4
  • 12
0

Just look at this answer https://stackoverflow.com/a/16388650

He has used yourAdapter.notifyDataSetChanged() which is a predefined method. Check the link to see how its done

Edit: When your AsyncTask is done, you should do something like this onPostExecute method:

ResultFragment resultFrag = (ResultFragment) getSupportFragmentManager()
                                 .findFragmentByTag("FragToRefresh");
if (resultFrag != null) {
    resultFrag.refreshData(refreshedArray);
}

And in your ResultFragment you need to have refreshData method, which is something like this:

public void refreshData(ArrayList<YourObject> data) {
   yourArray = new ArrayList<YourObject>(data);
   yourAdapter.notifyDataSetChanged();
}
Community
  • 1
  • 1
  • It's always recommended to write possibly "self-contained" answers - containing solution to the problem, not merely linking to another answer. Perhaps an edit to include most relevant snippet? :) – bardzusny Jul 27 '15 at 08:28
0

Had to use event bus to make everything simple https://github.com/greenrobot/EventBus

0

I am not sure this is the right way of doing it
1. Create a public function in fragment you would call to receive the data.

public void refreshList(List<String> yourData) {
      //referesh your fragment views here
}

2. Create the fragment object global

YourFragment frag = new YourFragment();

3. Pass it to the view pager in the containing activity
4. Add on page change listener to the view pager

  viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                switch (position) {
                    case 0:
                        break;

                    case 1:
                        break;
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

Here case 0: would be invoked when first fragment is selected and case 1: when second and so on..

5. Call your function corresponding to its position in the view pager

case 0: frag.refreshList(yourList);
chris
  • 194
  • 2
  • 10
0

I don't know enough of what you are doing, but it sounds like you need to use an Observer pattern, Callbacks, or Listeners. Can't your fragment just do somthing like:

myservice.addMyEventListener(myFragInstance);

and then you can be "notified of a specific event."

user123321
  • 12,593
  • 11
  • 52
  • 63