Fragment onResume() isn't called when using FragmentPagerAdapter - android

I need my fragments to always call a certain function when they are the active fragment, so I put it in onResume(), but it isn't being called.
Fragment A
#Override
public void onResume(){
super.onResume();
Log.d("clear state", " "+clear);
if(clear == true)
{
restart();
clear = false;
calculate();
}
}
I use a FragmentPagerAdapter with a ViewPager to switch fragments
public class ScoutingFragSingle extends FragmentPagerAdapter{
#Override
public Fragment getItem(int index) {
Bundle data = new Bundle();
switch(index){
case 0:
TeamsFragment teamsFragment = new TeamsFragment();
data.putInt("current_page", index+1);
teamsFragment.setArguments(data);
return teamsFragment;
case 1:
data.putInt("current_page", index+1);
data.putInt("matchId", matchNum);
aFragment.setArguments(data);
return aFragment;
So how would I make the fragments call their onResume()?

I had the same problem while ago.
Create a new interface and implement it by both of your Fragments:
public interface OnPageSelectedListener {
void onPageSelected();
}
In parent activity implement android.support.v4.view.ViewPager.OnPageChangeListener and call Fragment method like this:
#Override
public void onPageSelected(int i) {
OnPageSelectedListener fragment = (OnPageSelectedListener ((PlaceListPagerAdapter)pager.getAdapter()).getFragment(i);
fragment.onPageSelected();
}
PS: Name of the new interface and its method is a bit confusing, so be careful or change it.

Override Fragment.setUserVisibleHint. When setUserVisibleHint is true call same logic you use for onResume.
You can see when FragmentPagerAdapter calls setUserVisibleHint in instantiateItem and setPrimaryItem. Applicable to android since 4.2.1 and support-v4.

Related

ViewPager and FragmentPagerAdapter: how to notify the fragment instance that it's being switched to another fragment?

I have a ViewPager, FragmentPagerAdapter and a couple fragments. I need to notify the fragment when the user switches to a different one (so that the first one can finalize its work). I'm looking into ViewPager.OnPageChangeListener but can't see any way to get the current fragment instance when the change occurs. Even ViewPager.getCurrentItem() returns the new position when OnPageChangeListener.onPageSelected is called.
Is it guaranteed that Fragment.onPause() will be called for the previous fragment every time when a new one is selected? That would certainly simplify things.
According to documentation is seems onPause gets call when ever user leave the fragment (though it does not always mean the fragment is being destroyed).
https://developer.android.com/guide/components/fragments.html
You can keep a reference of your fragment at class level, as mCustomFragment.
Then inside your fragment setup a public method which will finalize the work, you simply call this method when you need it in the ViewPager.OnPageChangeListener.
Maybe attach some sort of gesture listener to determine which way the swipe occurs. Then on OnPageChangeListener based on the last gesture used before the call, determine whether to put +1 or -1 on the current viewpager index.
Here's my solution that actually works. It involves broadcasts for notifying the fragments.
First, I extended the Fragment class:
#SuppressLint("ValidFragment")
public class ExtendedFragment extends Fragment {
public static final String FragmentIdTag = "Fragment ID";
public static final String FragmentSwitchedEventId = "FragmentSwitchedEvent";
#SuppressLint("ValidFragment")
protected ExtendedFragment(FragmentType fragmentId) {
m_fragmentType = fragmentId;
}
public void onSwitchedAway() {
}
public BroadcastReceiver broadcastReceiver() {
return m_receiver;
}
class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
FragmentType newFragmentType = (FragmentType) intent.getSerializableExtra(FragmentIdTag);
if (newFragmentType != m_fragmentType)
onSwitchedAway();
}
}
protected final FragmentType m_fragmentType;
Receiver m_receiver = new Receiver();
}
FragmentType is enum that's different for every fragment. All the fragments extend ExtendedFragment (a terrible name, I know. Feel free to suggest a better one) and pass appropriate unique FragmentType value to its constructor.
Then I use the Viewpager.OnPageChangeListener to detect the fragment switch:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {
FragmentType newType = fragmentTypeForPosition(position);
Intent i = new Intent(ExtendedFragment.FragmentSwitchedEventId);
i.putExtra(ExtendedFragment.FragmentIdTag, newType);
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(i);
}
#Override
public void onPageScrollStateChanged(int state) {}
});
And finally, when instantiating the fragments don't forget to register them as broadcast receivers:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
FragmentType newType = fragmentTypeForPosition(position);
Intent i = new Intent(ExtendedFragment.FragmentSwitchedEventId);
i.putExtra(ExtendedFragment.FragmentIdTag, newType);
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(i);
}
});
Done. Now the fragments can override public void onSwitchedAway().

Notify RecyclerView adapter database changed

My MainActivity calls a fragment,
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, PlaceholderFragment.newInstance(position + 1),
"placeHolderFragmentTag")
.commit();
}
placeholderFragment that fills a RecyclerView with an adapter,
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dbHelper = new DatabaseHelper(getActivity().getApplicationContext());
dbHelper.getWritableDatabase();
dbHelper.openDataBase();
adapterUpcomingGames = new AdapterUpcomingGames(retrieveGames(dbHelper.getUpcomingGames()));
}
adapterUpcomingGames. The adapter gets data from a database that the user has entered, being a list of upcoming sporting events.
#Override // still placeholderFragment
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getApplicationContext()));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
switch(getArguments().getInt(ARG_SECTION_NUMBER))
{
case 1: // This will be upcoming games
mRecyclerView.setAdapter(adapterUpcomingGames);
break;
case 2: // This will be past games
AdapterPastGames adapterPastGames;
adapterPastGames = new AdapterPastGames(retrieveGames(dbHelper.getPastGames()));
mRecyclerView.setAdapter(adapterPastGames);
break;
default: // Default is, well, anything that will work.
adapterUpcomingGames1 = new AdapterUpcomingGames(retrieveGames(dbHelper.getGames()));
mRecyclerView.setAdapter(adapterUpcomingGames1);
}
newGame.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0){
Intent createGame = new Intent(getActivity(),
ActivityCreateGame.class);
startActivity(createGame);
adapterUpcomingGames.notifyDataSetChanged();
}
});
setRetainInstance(true);
}
With this view, there is an button to add games i.e. modify the database. I am achieving this by calling a new activity, activityCreateGame, that hosts a fragment,
#Override // activityCreateGame
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_game);
fragmentCreateGame = new FragmentCreateGame();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.attach(fragmentCreateGame)
.replace(R.id.newGameContainer, fragmentCreateGame)
.commit();
restoreActionBar();
}
fragmentCreateGame, that allows the user to enter in the desired information, and then append that to the database. The user saves this information using an option on the navbar within that activity.
#Override // activityCreateGame
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
finish();
break;
case R.id.action_save:
// check if an error has occured
if (fragmentCreateGame.CreateNewGame())
{
Toast toast = Toast.makeText(this, R.string.toast_success_game_create,
Toast.LENGTH_SHORT);
toast.show();
finish();
}
else{
Toast toast = Toast.makeText(this, R.string.toast_failed_game_create,
Toast.LENGTH_SHORT);
toast.show();
}
break;
}
return super.onOptionsItemSelected(item);
}
That action finishes the activityCreateGame. Now, how do I go about notify the adapter, back in placeholderFragment that the database has been updated and thus update the RecyclerView to update itself?
From looking at your code, I can think of three solutions for you. In order of (my opinion) quality of the solution from least to best.
Move your fragment fragmentCreateGame to be hosted by you first activity
Start Activity For Result
Use Otto Event Bus. I would recommend otto. Especially if you may have multiple objects listening to an event.
First add Otto to your project. In your build.gradle, add this to your dependancies.
dependencies {
....
compile 'com.squareup:otto:1.3.6'
....
}
In your activity that needs notified, add this at the beginning of your onCreate:
Bus bus = new Bus();
bus.register(this);
Then, create an "event" class. It is just an object you pass to notify subscribers. You can add info to these too, to pass more info.
public class SomeCoolEvent{
private String yourWisdom;
public SomeCoolEvent(String yourWisdom){
this.yourWisdom = yourWisdom;
}
public String getWisdom(){
return yourWisdom;
}
}
Then, in your class that is doing something to notify other classes, do this:
Bus bus = new Bus();
bus.post(new SomeCoolEvent(yourWisdomStringFromThisClass));
Now, back to your first activity, add a method that is annotated with #Subscribe
#Subscribe
public void onAnyMethodNameYouWantCauseItDoesntMatter(SomeCoolEvent event){
//this will be called when ANY class calls bus.post(SomeCoolEvent)
String boomYouAreNotified = event.getWisdom();
}
On your #Subscribe method, few things to note.
* The method name does not matter, so give it something that makes sense to you. I like to prefix it with "on" because its like a callback.
* The method must be public and return void.
* You are not explicitly calling this methods, so some IDE's may appear that it is unused. Android studio will let you fix that with "ALT + ENTER" then select "_ignore warnings for methods annotated with Subscribe".
* You should call bus.unregister(this) on destroy.
* You can have multiple classes listening to one post.
Now things can get more complicated if you start running on different threads. If you do, there are solutions to this at the bottom of the Otto page I provided.

Similar onBackPressed() for Fragments?

Hello my Android application is using fragments. I am wondering if there is a way I can use the idea of the onBackPressed() method in my app with fragments. I have previous and next buttons, but as of now I am just creating new fragments and replacing, and none of the data gets saved. Is there a way to save my data/go back once I have gone forward?
The concept of Fragment is different of Activity.
One Activity could have a many Fragments, read that:
A Fragment represents a behavior or a portion of user interface in an
Activity. You can combine multiple fragments in a single activity to
build a multi-pane UI and reuse a fragment in multiple activities. You
can think of a fragment as a modular section of an activity, which has
its own lifecycle, receives its own input events, and which you can
add or remove while the activity is running (sort of like a "sub
activity" that you can reuse in different activities).
See more here: http://developer.android.com/guide/components/fragments.html
SOLUCTION
So if you wanna handle the onBackPressed behavior in you Fragment you could do that:
package com.example.stackoverflowsandbox;
import android.app.Activity;
import android.app.Fragment;
public class MainActivity extends Activity {
public class MyFragment extends Fragment {
public void onBackPressed() {
// your code here...
}
}
private MyFragment myFragment;
#Override
public void onBackPressed() {
// super.onBackPressed(); // comment to not back
this.myFragment.onBackPressed(); // the onBackPressed method of Fragment is a custom method
}
}
Sorry, do not know if I understand your question, but if the idea and have control of direct backbutton in its fragment, and from it to perform some task of data persistence, you can add your fragment to control stack FragmentManager, the as follows.
FragmentManager fm = getSupportFragmentManager();
MyFragment mMyFragment = new MyFragment();
fm.beginTransaction()
.add(mMyFragment, "mMyFragment")
.addToBackStack( null )
.commit();
In the fragment you need to implement the interface OnBackStackChangedListener
In Fragment:
public class MyFragment extends Fragment implements OnBackStackChangedListener {
#Override
public void onBackStackChanged() {
//your code here
}
}
If you just keep the values
public class MyFragment extends Fragment {
String valeu;
#Override
public void onCreate( final Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
if ( savedInstanceState != null ) {
this.valeu = savedInstanceState.getString( "key" );
}
}
#Override
public void onSaveInstanceState( final Bundle outState ) {
super.onSaveInstanceState( outState );
outState.putString( "key", "Your content" );
}
}
while moving to front fragment, save the state of previous fragment using onSaveInstanceState()
while moving back restore the state in onCreate() or onCreateView() in the previous fragment

Data Transfer in Fragments/Activity

Hi i am new to Fragments i searched many pages but still i didn't get correct answer for my questions. I want to know how data transfer while implementing Fragments. I have three cases
1. Fragment to Fragment data transfer : By implementing a Callback Interface
2. Fragment to Activity data transfer : By using getActivity().
3. Activity to Fragment data transfer : My problem was occurred here. I referred many sites but nobody given clear explanation.Please help me.
For transfer data at the time of creation of the fragment, do this:
Fragment newFragment = MyFragment.newInstance(any argument);
In constructor of fragment, do this:
public static MyFragment newInstance(int someInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
myFragment.setArguments(args);
return myFragment;
}
Then for instantiate a fragment now, you make call newInstance() instead of new Fragment(). For recover the data of a fragment on fragment, do this:
int myData = getArguments().getInt("someInt", 0);
For transfer data from activity to fragment in a time of execution implementing a Callback Interface too.
If what you want is to have a reference in your activity to the fragment,
then make your fragment register in your activity using an interface:
public interface RegisterFragment {
public void setFragmentReference(MyFragment frag);
}
implement the interface in your activity, which defines a method to save a fragment reference:
public class MainActivity extends FragmentActivity implements RegisterFragment {
private MyFragment myFragmentReference;
public void setFragmentReference(MyFragment frag) {
this.myFragmentReference = frag;
}
public void usingFragmentReference() {
if(myFragmentReference != null)
myFragmentReference.doSomething("string data");
}
}
In your fragment subclass, save the reference using the interface method:
public class MyFragment extends Fragment {
#Override
public void onStart() {
RegisterFragment rf = (RegisterFragment)getActivity();
rf.setFragmentReference(this);
}
public void doSomething(String data) {
...
}
}
This way your activity can communicate with your fragment when required.

ViewPager Activity to notify a Fragment of a specific event

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!
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
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.
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.
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."
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();
}
Had to use event bus to make everything simple https://github.com/greenrobot/EventBus
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);

Categories

Resources