I have the following situation:
I have an Activity that hosts a ViewPager, and I have 4 Fragments;
the ViewPager at the beginning contains Fragment A,
when the user swipes on the ViewPager Fragment B goes into the ViewPager, then Fragment C and Fragment D ...etc...
Now as soon as the FragmentPagerAdapter is instantiated at least 2 of the Fragments are created.
This poses 2 problem:
Every Fragment needs to perform network calls, but I do not want to do unnecessary ones (I do not want to make network calls for Fragment B, if the user never swipes to Fragment B );
similar to 1.), I need to show a ProgessDialog when a Fragment perform network calls, but I do not want to show dialogs from Fragment B if the user never goes to it...
Please what kind of pattern should I use in such a circumstance?
Activity
public class PagerActivity extends ActionBarActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager_layout);
ViewPager pager=(ViewPager)findViewById(R.id.pager);
TabPageIndicator tabs=(TabPageIndicator)findViewById(R.id.titles);
pager.setAdapter(buildAdapter());
tabs.setViewPager(pager);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
FragmentPagerAdapter
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
#Override
public int getCount() {
return (4);
}
#Override
public Fragment getItem(int position) {
if (position == 1) {
if (dashbardFragment == null)
dashbardFragment = DashBoardFragment.newInstance(position);
return dashbardFragment;
}
if (position == 0) {
if (listOfParticipantFragment == null)
listOfParticipantFragment = ListOfParicipantsFragment
.newInstance(position);
return listOfParticipantFragment;
}
}
1 Fragment
public class ListOfParicipantsFragment extends Fragment {
public static ListOfParicipantsFragment newInstance(int position) {
ListOfParicipantsFragment frag = new ListOfParicipantsFragment();
return (frag);
}
public static String getTitle(Context ctxt, int position) {
return myApplication.getContext().getResources().getString(R.string.list_of_participants_fragment_title);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View result = inflater.inflate(R.layout.guest_list_fragment_layout,
container, false);
return (result);
}
Try this, in each fragment override below method and call your function when it is visible:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisible()){
if(isVisibleToUser){
Log.d("MyTag","My Fragment is visible");
}else{
Log.d("MyTag","My Fragment is not visible");
}
}
}
EDIT
Note: This is only useful when using a FragmentPagerAdapter or FragmentStatePagerAdapter
Basically what you want to do, is, find which fragment is currently being viewed when you swipe. And then, do your network calls.
You can take advantage of the ViewPager listeners to get notified when the user swipes to a new page. Docs : http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html#onPageSelected(int)
This will give you the position of the View. But i'm assuming that what you want is the actual fragment, which is a bit more tricky.
But, this has been answered already in here : Is it possible to access the current Fragment being viewed by a ViewPager?
Hope it helps
Let me introduce my idea to you:
getCurrentItem()
Method of ViewPager
getItem(int position)
Method of FragmentPagerAdapter Return the Fragment associated with a specified position.
You can define an Interface holding the method for Network I/O like
public Interface INetworkOnFragment{
void handle(){
//...
}
}
And implement it on your fragments and handle their own business logic (Network calls).
In main Activity ,set ViewPager.OnPageChangeListener on ViewPager object like here:
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener(){
public void onPageScrollStateChanged(int state){
//donothing
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
//donothing
}
public void onPageSelected(int position){
INetworkOnFragment interface =(INetworkOnFragment) (pager.getAdapter().getItem(position));//get the current fragment and call handle method on it,dont need to care about whichever fragment it is .
interface.handle()
}
});
The most important is onPageSelected(int position),Inside the callback it get the current fragment and call handle method on it,dont need to care about whichever fragment it is .
Remember the handle method are called in Activity and not in fragments.All the Network calls are implemention of Interface,which make it easy to deal in Activity.
Check out the solution from my answer here
1) Create LifecycleManager Interface The interface will have two methods (onPauseFragment and onResumeFragment) and each ViewPager’s Fragment will implement it
2) Let each Fragment implement the interface
3) Implement interface methods in each fragment - start your AsyncTask in onResumeFragment
4) Call interface methods on ViewPager page change You can set OnPageChangeListener on ViewPager and get callback each time when ViewPager shows another page
5) Implement OnPageChangeListener to call your custom Lifecycle methods
Create a page into view method for FragmentStatePagerAdapter which calls a method on the fragment when the fragment comes into view.
Implement the OnPageIntoView interface in your fragment.
public class SomethingDifferent extends Fragment implements OnPageIntoView {
...
/*
* Called when this page comes into view
*
* #see com.gosylvester.bestrides.SettingFragmentPagerSupport.MyAdapter.
* OnPageIntoView#onPageIntoView()
*/
#Override
public void onPageIntoView() {
// this is just some random example code
// that does some heavy lifting it only runs when the fragment
// frist comes into view
if (fragmentActivity != null) {
if (lrc == null) {
lrc = new ClientServiceLocationRecorder(
new WeakReference<Context>(
fragmentActivity.getApplicationContext()),
lrcCallback);
}
// get a status message from the location recorder
lrc.sndMessageToLocationRecorder(ServiceLocationRecorder.MSG_RECORD_STATUS);
}
}
Create a custom FragmentStatePagerAdapter Override the setPrimaryItem method and if the object can be cast to the interface then call through the interface one time only.
public static class MyAdapter extends FragmentStatePagerAdapter {
public interface OnPageIntoView {
public void onPageIntoView();
}
private Fragment mCurrentFragment;
//bonus method to get the current fragment
public Fragment getCurrentFragment() {
return mCurrentFragment;
}
static int lastPosition = -1;
#Override
public void setPrimaryItem(ViewGroup container, int position,
Object object) {
//quickly determine if the primary item has changed
//and one time only call through interface
if (position != lastPosition) {
lastPosition = position;
//determine if this is fragment it should be but lets avoid
//class cast exceptions
if (Fragment.class.isAssignableFrom(object.getClass())) {
mCurrentFragment = ((Fragment) object);
//determine if the onPageIntoView interface has
//been implemented in the fragment
//if so call the onPageIntoView
if (OnPageIntoView.class.isAssignableFrom(mCurrentFragment
.getClass())) {
((OnPageIntoView) mCurrentFragment).onPageIntoView();
}
}
}
super.setPrimaryItem(container, position, object);
}
}
It seems easy to me,what you need is Fragments onResume() method. This will be called only when your fragment is VISIBLE to user.
Here you can add logic to initiate your network call. It guarantees that your fragment is in visible mode.
See this
However you can optimize your network calls logic, using LoaderManager with AsyncTaskLoader pattern.
Loaders take care of screen orientation changes & they cache data for you. So that network call is not initiated twice for same operation.
From Android documentation
Introduced in Android 3.0, loaders make it easy to asynchronously load
data in an activity or fragment. Loaders have these characteristics:
They are available to every Activity and Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results
when the content changes.
They automatically reconnect to the last loader's cursor when
being recreated after a configuration change.
Thus, they don't need to re-query their data.
You can use any Asynchronous HTTP lib for network calls like Retrofit
i found one tutorial for AsyncTaskLoader & LoaderManager # this link, below are some quotes from tutorial
Loaders aren't trivial, so why use them in the first place? Well, in
most cases, you would use them in the same scenarios where you've been
using AsyncTasks; in fact, some loader subclasses extend AsyncTask.
Just as AsyncTasks are used to perform any long-running operation that
would tie up the user thread and ultimately throw the dreaded
Application Not Responding (ANR), loaders perform in the same manner
with the same purpose. The main difference is loaders are specialized
for loading data. As such, loaders offer a number of efficiency and
convenience benefits.
#Andrew Carl provide good idea. I also use the similar approach in my projects. I think it's more generalized.
Create an interface:
public interface ViewPagerFragment {
void onSelected();
void onDeselected();
}
And this common helper:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;
public class ViewPagerHelper implements ViewPager.OnPageChangeListener {
private final FragmentManager mFragmentManager;
private final ViewPager mViewPager;
private int mSelectedPage;
public ViewPagerHelper(FragmentManager fragmentManager, ViewPager viewPager) {
mFragmentManager = fragmentManager;
mViewPager = viewPager;
mSelectedPage = -1;
}
#Override
public void onPageSelected(int position) {
Fragment previous = findViewPagerChildFragment(mFragmentManager, mViewPager, mSelectedPage);
if (previous instanceof ViewPagerFragment) {
((ViewPagerFragment) previous).onDeselected();
}
Fragment current = findViewPagerChildFragment(mFragmentManager, mViewPager, position);
if (current instanceof ViewPagerFragment) {
((ViewPagerFragment) current).onSelected();
}
mSelectedPage = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// empty
}
#Override
public void onPageScrollStateChanged(int state) {
// empty
}
public static Fragment findViewPagerChildFragment(FragmentManager manager, ViewPager pager, int position) {
if (pager == null) {
return null;
}
String tag = "android:switcher:" + pager.getId() + ":" + position;
return manager.findFragmentByTag(tag);
}
}
Now you may use them for any purpose:
Fragment:
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class MyFragment extends Fragment implements ViewPagerFragment {
private boolean mSelected;
public static MyFragment newInstance(int position) {
Bundle args = new Bundle();
args.putInt("position", position);
MyFragment result = new MyFragment();
result.setArguments(args);
return result;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView result = new TextView(inflater.getContext());
result.setText("Position: " + getPosition());
return result;
}
private int getPosition() {
return getArguments().getInt("position");
}
#Override
public void onSelected() {
mSelected = true;
start();
}
#Override
public void onDeselected() {
mSelected = false;
}
private void start() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Activity activity = getActivity();
if (activity == null) {
return;
}
if (!mSelected) {
Toast.makeText(activity, "Fragment #" + getPosition() + " stopped", Toast.LENGTH_SHORT).show();
return;
}
TextView textView = (TextView) activity.findViewById(R.id.text);
if (textView != null) {
textView.setText("Fragment #" + getPosition() + " works: " + System.nanoTime() % 10000);
}
handler.postDelayed(this, 150);
}
}, 150);
}
}
Activity:
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
viewPager.setOnPageChangeListener(new ViewPagerHelper(getSupportFragmentManager(), viewPager));
viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
}
}
Adapter:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(position);
}
#Override
public int getCount() {
return 42;
}
}
Check complete demo on github.
Related
CONTEXT:
I have one activity, 4 fragments and a viewPager (each page consists of one fragment). Each fragment contains a list of short sounds (max 5 seconds each). The expected behavior is: if the user clicks on a sound on a certain page and the sound did not finish playing, if the user swipes to another page, the media player will be released, thus the sound will stop playing.
ISSUE:
When swiping left or right, the method onPause or onStop are not called (since the viewPager by definition loads the current, the previous and the next page), thus the media player playing the sound from the last page does not stop on swipe.
RESEARCH:
I have searched a lot on multiple posts, related to onPageChangeListener, setOffScreenPageLimit, set MediaPlayer not in fragment but in the activity (not possible in my case, since I have lists of sounds with adapter). Here are some links that I researched but did not solve my problem: ViewPager swipe previous sound, Multiple video players in viewpager in android, Android MediaPlayer on ViewPager blocking UI transition and no callback for initial page loads, How to handle MediaPlayer object inside a fragment inside view pager, Using Viewpager with MediaPlayer
SUGGESTIONS?: if you have any suggestions on how to tackle this issue, it would be highly appreciated! Thanks!
You can create a Callback interface on your MainActivity with a method onPageChanged() like this:
public interface Callback {
void onPageChanged();
}
Then, you can use this OnPageChangeListener:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private int currentPage = 0;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
Fragment fragment = adapter.getItem(currentPage);
if (fragment instanceof Callback &&
currentPage != position) {
((Callback)adapter.getItem(currentPage)).onPageChanged();
}
currentPage = position;
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Each and every Fragment in your ViewPager can be created like this:
public class TestFragment extends Fragment implements MainActivity.Callback {
private void releaseMediaPlayer() {
// ...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// ...
}
#Override
public void onPageChanged() {
releaseMediaPlayer();
// You can do more things here
}
}
After doing that, every time that you change the Fragment on the ViewPager it will be calling the onPageChanged method on the previous Fragment, and in there you can release your media player and do any other operation that you require.
private int currentPos = 0;
private int prevPos = -1;
global variable
I am getting same problem. I have take two method play and stop in fragment. and call below code in onPageSelected(int position)
new Thread(new Runnable() {
#Override
public void run() {
try {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
prevPos = currentPos;
currentPos = position;
MyFragment fragment = (MainFragment) getChildFragmentManager().
findFragmentByTag("android:switcher:" + R.id.viewPagerVideo + ":" + prevPos);
if (fragment != null) {
fragment.stop();
}
fragment = (MyFragment) getChildFragmentManager().
findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + currentPos);
if (fragment != null) {
fragment.play(mListUrl.get(position));
}
}
});
} catch (Exception ignore) {
}
}
}).start();
Consider you making the fragment itself an OnPageChangeListener. Then you can set the listener when you attach the page. This will allow you to pass the page change information to the fragment. You are allowed to have more than one OnPageChangeListener. Hope that helps.
EDIT:
Consider something like this.
public class PageFragment extends Fragment implements ViewPager.OnPageChangeListener {
ViewPager pager;
public void onPageScrollStateChanged (int state){
// reset the player if needed. you can do this since player object is in this class
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
....
pager = (ViewPager) getActivity().findViewById(ViewPagerId);
pager.addOnPageChangeListener(this);
....
}
#Override
public void onDestroyView (){
....
pager.removeOnPageChangeListener(this);
....
}
}
How could some part of my code be aware of Fragment instance become visible on a screen?
The following snippet will explain my question.
public class MyApp extends Application {
public static final String TAG = MyApp.class.getSimpleName();
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
...
#Override
public void onActivityResumed(Activity activity) {
Log.d(TAG, activity.getClass().getSimpleName() + " is on screen");
}
#Override
public void onActivityStopped(Activity activity) {
Log.d(TAG, activity.getClass().getSimpleName() + " is NOT on screen");
}
...
});
}
Here i can track when any activity within my app appears on the screen. Is there any way to extend this approach on Fragments?
Something like
Activity.getFragmentManager().registerFragmentLifecycleCallbacks();
UPD. I know nothing about activities implementations, do they use fragments at all and how do they use them (injection via xml, ViewPager etc.) The only thing I have within my class is an application context. Let's assume Activity and Fragment implementations are black boxes and i am not able to make any changes.
In your fragment, override onHiddenChanged(...) method:
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) {
Log.d(TAG, ((Object) this).getClass().getSimpleName() + " is NOT on screen");
}
else
{
Log.d(TAG, ((Object) this).getClass().getSimpleName() + " is on screen");
}
}
Hope this work for you!
Without touching the Activity or Fragment code and assuming you don't know the tag or layout it is placed in, there is very little that you can do. The best that I can see is that you could get the FragmentManager in ActivityResumed and ActivityStopped callbacks (because here you have an Activity reference) and apply a BackstackChangedListener. This assumes that you use the backstack when changing between fragments.
The issue with what you are asking is that you want lifecycle callbacks for Fragments on the Application level when you have no control over the middle men, the Activities which are already starved for Fragment callbacks. They do most everything through their FragmentManager, and propagate their own lifecycle callbacks down to the Fragments so that the fragments will behave appropriately. The onResume and onPause callbacks in fragments only occur when they are first created or when the Activity experiences those callbacks. There is only one lifecycle callback for Fragments in Activities, onAttachFragment, which if you could override, would give you references to the Fragments that are attached to the Activity. But you said you can't change the Activity or the Fragment, and you want to know when the Fragments are shown.
So if you don't use the backstack, I don't think there's a way to do what you want.
For putting Fragments inside Activity i use SlidingTabLayout which Google uses. Inside it you have ViewPager and some Adapter to populate many Fragments. First of all you have to put this and this files in your project. Then here there is good tutorial for how you can implement SlidingTabLayout.
1) After you have implemented SlidingTabLayout in your Activity, you can detect when and which Fragment becomes visible from Activity:
mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Do nothing
}
#Override
public void onPageSelected(int position) {
if (position == 0) {
//Whenever first fragment is visible, do something
} else if (position == 1) {
//Whenever second fragment is visible, do something
} else if (position == 2) {
//Whenever third fragment is visible, do something
} else if (position == 3) {
//Whenever fourth fragment is visible, do something
}
}
#Override
public void onPageScrollStateChanged(int state) {
//Do nothing
}
});
2) You can detect if Fragment is visible from Fragment itself as i answered here, however this may get called before onCreateView() of Fragment, so check answer in the link:
#Override
public void setUserVisibleHint(boolean visible){
super.setUserVisibleHint(visible);
if (visible){
//when this Fragment is active, do something
}
}
3) You can change also change colors of indicators of each Tab like this from Activity:
mSlidingTabLayout.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
#Override
public int getIndicatorColor(int position) {
if (position == 0) {
return getResources().getColor(R.color.orange);
} else if (position == 1) {
return getResources().getColor(R.color.redDimmed);
} else if (position == 2) {
return getResources().getColor(R.color.yellow);
} else if (position == 3) {
return getResources().getColor(R.color.green);
} else {
return getResources().getColor(R.color.redLight);
}
}
#Override
public int getDividerColor(int position) {
return getResources().getColor(R.color.defaultActionBarBg);
}
});
Use same way as activity
set flag in application class to check visiblity of fragment, use below code in fragment
#Override
public void onStart() {
super.onStart();
Log.e( "Fragment is visible", "Fragment is visible");
Application Class.isFragmentShow = true;
}
#Override
public void onPause() {
super.onPause();
Log.e("Fragment is not visible", "Fragment is not visible");
Application Class.isFragmentShow = false;
}
to communicate with fragment you have to call that activity in which fragment added then use below code
MainFragment fragment = (MainFragment) fragmentManager.findFragmentByTag("MainFragment");
fragment.setFilter();
Don't exist a default way to do, but you can make your own Callbacks, I made this and works fine, first need have a BaseFragment class where we'll handle all fragment events.
public class BaseFragment extends Fragment {
private String fragmentName;
private FragmentLifecycleCallbacks listener;
public void registerCallBacks(String fragmentName){
// handle the listener that implement 'MyApp' class
try{
listener = (FragmentLifecycleCallbacks) getActivity().getApplication();
} catch (ClassCastException e) {
throw new ClassCastException("Application class must implement FragmentLifecycleCallbacks");
}
// set the current fragment Name for the log
this.fragmentName = fragmentName;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if(listener!=null) {
listener.onAttachFragment(fragmentName);
}
}
#Override
public void onResume() {
super.onResume();
if(listener!=null) {
listener.onResumeFragment(fragmentName);
}
}
#Override
public void onStop() {
super.onStop();
if(listener!=null) {
listener.onStopFragment(fragmentName);
}
}
// 'MyApp' class needs implement this interface to handle all the fragments events
public interface FragmentLifecycleCallbacks{
void onStopFragment(String fragmentName);
void onResumeFragment(String fragmentName);
void onAttachFragment(String fragmentName);
}}
On 'MyApp' class implement the interface of BaseFragment
public class MyApp extends Application implements BaseFragment.FragmentLifecycleCallbacks{
public static final String TAG = MyApp.class.getSimpleName();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onStopFragment(String fragmentName) {
Log.d(TAG, fragmentName + " is NOT on screen");
}
#Override
public void onResumeFragment(String fragmentName) {
Log.d(TAG, fragmentName + " is on screen");
}
#Override
public void onAttachFragment(String fragmentName) {
Log.d(TAG, fragmentName + " is attached to screen");
}}
And now each Fragment that you have need extends 'BaseFragment' and register to the global listener
public class FragmentA extends BaseFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_simple, container, false);
// here register to the global listener
registerCallBacks(FragmentA.class.getName());
return rootView;
}}
Hope this helps!
Intercept onWindowFocusChanged() in the activity and propagate that to the interested fragment.
Try this
private Boolean isFragmentVisible()
{
if(getFragmentManager().findFragmentByTag("TAG") != null && getFragmentManager().findFragmentByTag("TAG").isVisible())
{
//The fragment is visible
return true;
}
return false;
}
Alternative way
private Boolean isFragmentVisible()
{
return getFragmentManager().findFragmentByTag("TAG") != null && getFragmentManager().findFragmentByTag("TAG").isVisible();
}
You can know the following with the built in method called "onActivityCreated(Bundle)" this method tells that the fragment has been created thus you get to know that the fragment appears on the screen Click here for reference
Hope it helps
I've looked through what's available without using a base Fragment or Activity class but couldn't find any. I've made an implementation that provides basic (onAdded / onRemoved) functionality for all fragments in your application. It is certainly possible to extend it to report the current state of the fragment (onAttach, onResume, onPause, onDetach, ...).
You can find the code along with a sample here: https://github.com/Nillerr/FragmentLifecycleCallbacks
It works both for non-support library Fragments and support library Fragments through different implementations. The support library class is safer to use and should perform better, because the non-support one uses Reflection to access the fragments, while the support library FragmentManager includes a getFragments() method.
If you are setting a Fragment to your View, you probably have a container where it will be shown. Given that this container is, say, a FrameLayout with id R.id.container, you can do that:
Fragment f = fragmentManager.findFragmentById(R.id.container);
if (f instanceof YourFragment) {
// TODO something when YourFragment is ready
}
Does this interface provide anything helpful to you?
https://github.com/soarcn/AndroidLifecyle/blob/master/lifecycle/src/main/java/com/cocosw/lifecycle/FragmentLifecycleCallbacks.java
It sounds like your best bet if you can't override the Fragment's own onResume() method is to create your own interface that extends ActivityLifecycleCallbacks, then put your logging code in the onFragmentResumed(Fragment yourFragment) method.
You can get a pointer to the Fragment by doing something like this:
int yourFragmentId = 0; //assign your fragment's ID to this variable; Fragment yourFragment.getId();
FragmentManager fm = activity.getFragmentManager();
Fragment f = fm.findFragmentById(yourFragmentId);
whereever u want to check if fragment is visible or not.. just check isMenuVisible() value.
this is fragment's method which i used to check visible fragment when i have to fire some http request from viewpager selected Item.
hope this helps.
in my case i was using this method in onActivityCreated().
In you fragment override method setMenuVisibility If you are using ViewPager and are swiping from left and right, this method is called when the visivility of the fragment gets changed.
Here is a sample from my project
public abstract class DemosCommonFragment extends Fragment {
protected boolean isVisible;
public DemosCommonFragment() {
}
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
isVisible = menuVisible;
// !!! Do Something Here !!!
}
}
Animation listener
I have NOT checked all use cases and there is an unhandled exception. You can play around with it to fit your use case. Please feel free to comment your opinions or use cases it did not solve.
NOTE: You can add fragmentWillDisappear and fragmentDidDisappear by handling for enter in onCreateAnimation.
Parent Fragment:
public class BaseFragment extends Fragment {
private Animation.AnimationListener animationListener;
private void setAnimationListener(Animation.AnimationListener animationListener) {
this.animationListener = animationListener;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
fragmentWillAppear(animation);
}
#Override
public void onAnimationEnd(Animation animation) {
fragmentDidAppear(animation);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
#Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
AnimationSet animSet = new AnimationSet(true);
Animation anim = null;
try {
anim = AnimationUtils.loadAnimation(getActivity(), nextAnim);
} catch (Exception error) {
}
if (anim != null) {
anim.setAnimationListener(animationListener);
animSet.addAnimation(anim);
}
return animSet;
}
public void fragmentDidAppear(Animation animation) {
}
public void fragmentWillAppear(Animation animation) {
}
}
Child Fragment:
class ChildFragment extends BaseFragment {
#Override
public void fragmentDidAppear(Animation animation) {
super.fragmentDidAppear(animation);
}
#Override
public void fragmentWillAppear(Animation animation) {
super.fragmentWillAppear(animation);
}
}
I have a wizard generated app with navigation drawer in android studio 0.8.2
I have created a fragment and added it with newInstance() and I get this error:
com.domain.myapp E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.ClassCastException: com.domain.myapp.MainActivity#422fb8f0 must implement
OnFragmentInteractionListener
I can't find anywhere how to implement this OnFragmentInteractionListener ??
It cannot be found even in android sdk documentation!
MainActivity.java
import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
public class MainActivity extends Activity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getFragmentManager();
switch (position) {
case 0: fragmentManager.beginTransaction()
.replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
.commit(); break;
case 1: fragmentManager.beginTransaction()
.replace(R.id.container, AboutFragment.newInstance("test1", "test2"))
.commit(); break; // this crashes the app
case 2: fragmentManager.beginTransaction()
.replace(R.id.container, BrowseQuotesFragment.newInstance("test1", "test2"))
.commit(); break; // this crashes the app
}
}
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.title_section1);
break;
case 2:
mTitle = getString(R.string.title_section2);
break;
case 3:
mTitle = getString(R.string.title_section3);
break;
}
}
public void restoreActionBar() {
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.main, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
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;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity) activity).onSectionAttached(
getArguments().getInt(ARG_SECTION_NUMBER));
}
}
}
For those of you who still don't understand after reading #meda answer, here is my concise and complete explanation for this issue:
Let's say you have 2 Fragments, Fragment_A and Fragment_B which are auto-generated from the app. On the bottom part of your generated fragments, you're going to find this code:
public class Fragment_A extends Fragment {
//rest of the code is omitted
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
}
public class Fragment_B extends Fragment {
//rest of the code is omitted
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
}
To overcome the issue, you have to add onFragmentInteraction method into your activity, which in my case is named MainActivity2. After that, you need to implements all fragments in the MainActivity like this:
public class MainActivity2 extends ActionBarActivity
implements Fragment_A.OnFragmentInteractionListener,
Fragment_B.OnFragmentInteractionListener,
NavigationDrawerFragment.NavigationDrawerCallbacks {
//rest code is omitted
#Override
public void onFragmentInteraction(Uri uri){
//you can leave it empty
}
}
P.S.: In short, this method could be used for communicating between fragments. For those of you who want to know more about this method, please refer to this link.
Answers posted here did not help, but the following link did:
http://developer.android.com/training/basics/fragments/communicating.html
Define an Interface
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Implement the Interface
For example, the following activity implements the interface from the above example.
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
}
Update for API 23: 8/31/2015
Overrided method onAttach(Activity activity) is now deprecated in android.app.Fragment, code should be upgraded to onAttach(Context context)
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public void onStart() {
super.onStart();
try {
mListener = (OnFragmentInteractionListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString()
+ " must implement OnFragmentInteractionListener");
}
}
See your auto-generated Fragment created by Android Studio. When you created the new Fragment, Studio stubbed a bunch of code for you. At the bottom of the auto-generated template there is an inner interface definition called OnFragmentInteractionListener. Your Activity needs to implement this interface. This is the recommended pattern for your Fragment to notify your Activity of events so it can then take appropriate action, such as load another Fragment. See this page for details, look for the "Creating event callbacks for the Activity" section: http://developer.android.com/guide/components/fragments.html
For those of you who visit this page looking for further clarification on this error, in my case the activity making the call to the fragment needed to have 2 implements in this case, like this:
public class MyActivity extends Activity implements
MyFragment.OnFragmentInteractionListener,
NavigationDrawerFragment.NaviationDrawerCallbacks {
...// rest of the code
}
You should try removing the following code from your fragments
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
The interface/listener is a default created so that your activity and fragments can communicate easier
In addition to #user26409021 's answer, If you have added a ItemFragment, The message in the ItemFragment is;
Activities containing this fragment MUST implement the {#link OnListFragmentInteractionListener} interface.
And You should add in your activity;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, ItemFragment.OnListFragmentInteractionListener {
//the code is omitted
public void onListFragmentInteraction(DummyContent.DummyItem uri){
//you can leave it empty
}
Here the dummy item is what you have on the bottom of your ItemFragment
With me it worked delete this code:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Ending like this:
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
OnFragmentInteractionListener is the default implementation for handling fragment to activity communication. This can be implemented based on your needs. Suppose if you need a function in your activity to be executed during a particular action within your fragment, you may make use of this callback method. If you don't need to have this interaction between your hosting activity and fragment, you may remove this implementation.
In short you should implement the listener in your fragment hosting activity if you need the fragment-activity interaction like this
public class MainActivity extends Activity implements
YourFragment.OnFragmentInteractionListener {..}
and your fragment should have it defined like this
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
also provide definition for void onFragmentInteraction(Uri uri); in your activity
or else just remove the listener initialisation from your fragment's onAttach if you dont have any fragment-activity interaction
Instead of Activity use context.It works for me.
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (OnFragmentInteractionListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Just an addendum:
OnFragmentInteractionListener handle communication between Activity and Fragment using an interface (OnFragmentInteractionListener) and is created by default by Android Studio, but if you dont need to communicate with your activity, you can just get ride of it.
The goal is that you can attach your fragment to multiple activities and still reuse the same communication approach (Every activity could have its own OnFragmentInteractionListener for each fragment).
But and if im sure my fragment will be attached to only one type of activity and i want to communicate with that activity?
Then, if you dont want to use OnFragmentInteractionListener because of its verbosity, you can access your activity methods using:
((MyActivityClass) getActivity()).someMethod()
Just go to your fragment Activity and remove all method.....instead on on createview method.
your fragment has only on method oncreateview that's it.
//only this method implement other method delete
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
and make sure your layout it is demo for u.
I'd like to add the destruction of the listener when the fragment is detached from the activity or destroyed.
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
and when using the new onStart() method with Context
#Override
public void onDestroy() {
super.onDestroy();
mListener = null;
}
I'm trying to update an object from a fragment contained within a swipe view. The code I have is taken directly from the Android documentation. What I want to do is pass an object from the main CollectionDemoActivity down into the DemoObjectFragment fragment, update it using a button in that fragment and then pass it back up to the main activity. What's the best way to accomplish this?
I've tried passing the object in a bundle as a serialisable through the DemoCollectionPagerAdapter and then again down to the fragment but this seems really cumbersome. I've also tried declaring the object in the main activity and just referencing it in the fragment class but I get complaints that it can't have a non-static reference in a static context.
public class CollectionDemoActivity extends FragmentActivity {
// When requested, this adapter returns a DemoObjectFragment,
// representing an object in the collection.
DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
ViewPager mViewPager;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collection_demo);
// ViewPager and its adapters use support library
// fragments, so use getSupportFragmentManager.
mDemoCollectionPagerAdapter =
new DemoCollectionPagerAdapter(
getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mDemoCollectionPagerAdapter);
}
}
// Since this is an object collection, use a FragmentStatePagerAdapter,
// and NOT a FragmentPagerAdapter.
public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
public DemoCollectionPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new DemoObjectFragment();
Bundle args = new Bundle();
// Our object is just an integer :-P
args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return 100;
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
// Instances of this class are fragments representing a single
// object in our collection.
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// The last two arguments ensure LayoutParams are inflated
// properly.
View rootView = inflater.inflate(
R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(android.R.id.text1)).setText(
Integer.toString(args.getInt(ARG_OBJECT)));
return rootView;
}
}
So after a lot of searching and reading I found a nice solution that works for me. For those interested I created an interface in the fragment class that is implemented in the Main activity. The methods were kicked off through a button press in the fragment class. This way I was able to pass variables up to the main class without ever needing to pass the entire object down to the fragment.
So my classes were mostly the same with these bits added:
And the fragment class which contains the interface. The onAttach() method needs to be called which gets a reference to the activity that the fragment will be attached to. This activity reference is binded to an instance of the interface in the fragment.
public class DemoObjectFragment extends Fragment {
....
//Creating the interface
public interface ButtonListener {
//This method will be called in the main activity. Whatever is passed in as the parameter can be used by the main activity
public void ButtonPressed(int myInt);
}
//Getting an instance of the interface
ButtonListener updateListener;
//Getting a reference to the main activity when the fragment is attached to it.
//The activity reference is bound to the instance of the interface.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Ensures the activity implements the callback interface
try {
updateListener = (DayUpdateButtonListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString());
}
}
....
//On the button click call the method through the activity reference from the onAttach() method
//Creating an int object to pass into the method.
int myNewInt = 5;
myButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
updateListener.ButtonPressed(myNewInt);
}
});
}
Finally in the main activity simply implement the interface and add the method from it.
public class CollectionDemoActivity extends FragmentActivity implements DemoObjectFragment.ButtonListener {
....
#Override
public void ButtonPressed(int myInt) {
//Update the object with myInt
}
}
I've got a problem with my android app crashing when trying to restore my fragments. I have not added any custom variables to the bundle that I'm trying to restore, It's all default. I'm using Fragments and ViewPager. See my code snippets below:
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
public int getCurrentItemPosition(Fragment fragment){
return getItemPosition(fragment);
}
#Override
public Fragment getItem(int position) {
return ContentFragment.newInstance(position);
}
}
public class MyActivity extends FragmentActivity {
static final int NUM_ITEMS = 100000;
public int currentSelectedPage;
private int changeToFragmentIndex;
public DateTime midDate;
MyAdapter mAdapter;
ViewPager mPager;
/** Called when the activity is first created. */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.diary);
MyApplication application = (MyApplication)getApplication();
application.dataController.myActivity = this;
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
int newDay = application.daysBetween(DateTime.now(), DateTime.parse(application.day.dateToShortString()));
this.currentSelectedPage = NUM_ITEMS/2+newDay;
mPager.setCurrentItem(NUM_ITEMS/2+newDay);
mPager.setOnPageChangeListener(new SimpleOnPageChangeListener(){
public void onPageSelected(int position){
currentSelectedPage = position;
ContentFragment fragment = (ContentFragment) mAdapter.instantiateItem(mPager, currentSelectedPage);
fragment.loadData();
}
});
}
}
public class ContentFragment extends Fragment {
private View v;
static final int NUM_ITEMS = 100000;
static ContentFragment newInstance(int num) {
ContentFragment f = new ContentFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null)
return null;
v = inflater.inflate(R.layout.diarycontent, container, false);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
setUserVisibleHint(true);
}
}
I receive this stackstrace:
Caused by: java.lang.NullPointerException
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:519)
at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:1 55)
at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:522)
As I have understood this may be a known problem with the support package and that one possible solution would be to use setUserVisibleHint(true) in onSaveInstanceState. But that didn't help.
Does anyone know another solution to the problem or what I've done wrong?
I have faced same type of issue once I started using ViewPager with FragmentStatePagerAdapter inside Tab.
Then I played with all forums related to this issue and break my head for two days to find out why this issue is occurred and how to resolve it but does not found any perfect solution that fulfills as per my requirement.
I got the solution as in order to avoid the NPE crash use FragmentPagerAdapter instead of FragmentStatePagerAdapter. When I replace FragmentPagerAdapter instead of FragmentStatePagerAdapter then not faced NPE crash issue but the same page is not refreshed itself for further navigation(which is my requirement).
Finally override the saveState method on FragmentStatePagerAdapter and everything working fine as expected.
#Override
public Parcelable saveState() {
// Do Nothing
return null;
}
How I reproduce this issue easily :
I am using the higher version(4.1) device and followed the below steps:
1. Go to Settings -> Developer Options.
2. Click the option "Do not keep activities".
Now I played with my app.
Before override the saveState() method each time the app is crashing due to the NPE at android.support.v4.app.FragmentManagerImpl.getFragment(Unknown Source). But after override saveState() no crash is registered.
I hope by doing so you will not having any issue.
I initially used solution suggested by #Lalit Kumar Sahoo. However, I noticed a serious issue: every time I rotated the device, the Fragment Manager added new fragments without removing the old ones. So I searched further and I found a bug report with a workaround suggestion (post #1). I used this fix and tested with my app, the issue appears to be resolved without any side-effects:
Workaround: Create custom FragmentStatePagerAdapter in project's src/android/support/v4/app folder and use it.
package android.support.v4.app;
import android.os.Bundle;
import android.view.ViewGroup;
public abstract class FixedFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
public FixedFragmentStatePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment f = (Fragment)super.instantiateItem(container, position);
Bundle savedFragmentState = f.mSavedFragmentState;
if (savedFragmentState != null) {
savedFragmentState.setClassLoader(f.getClass().getClassLoader());
}
return f;
}
}
Why exactly are you doing:
mPager.setOnPageChangeListener(new SimpleOnPageChangeListener(){
public void onPageSelected(int position){
currentSelectedPage = position;
ContentFragment fragment = (ContentFragment) mAdapter.instantiateItem(mPager, currentSelectedPage);
fragment.loadData();
}
});
This:
#Override
public Fragment getItem(int position) {
return ContentFragment.newInstance(position);
}
Instantiates the fragment.
You can load data like this in the fragment :
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loadData();
}