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);
....
}
}
Related
I have Navigation Drawer in my application with several Fragments and few new Activity also apart from main application flow.
Current Functionality For navigating to each Fragment, Network is required and in case of any network error, I used to show Dialog. User needs to click on "OK" button and again go back to navigation drawer to retry.
New Approach I am trying User should be shown and error screen similar to LinkedIn android app with option to retry.
As inner Fragments and handling call back can be cumbersome, how to handle this situation efficiently? For individual Activities this can be achieved easily, but worried about the Navigation Drawer and inner Fragments.
Any suggestions?
Make this error layout hidden in this fragment. When there is any network error then change its visibility to VISIBLE. and in this hidden layout add a button to recall same method to check network connection etc.
Let say you have fragment xml like -
fragment -
Relative Layout consisting -
1. -All layouts (VISIBLE) &
2. -Hidden network error layout with a button (GONE)
When there is network error then change 1. 's visibility to - GONE
and 2.'s visibility to VISIBLE
and on retry button call -
checkNetworkConnectionCall();
I hope this will solve you problem.
You can place some ContentFragment in a FrameLayout, then replace with a NetworkDisconnectedFragment when the network disconnects. This would require the button to a call the callback, then when reconnected, replace the NetworkDisconnectedFragment with the old ContentFragment in the callback implementation.
You can include this UI in each fragment and create a BaseFragment which will be extended by every fragment in nav drawer.
Write a method in that base fragment that will do the need full logic of changing the UI.
And whenever you detect a network failure just blindly call that method from base fragment there.
It's been almost 3 years, but I think it may be helpful for somebody. This example uses MVP pattern. BaseNetContentActivity, BaseNetContentFragment and NetworkErrorFragment are encapsulate change UI logic (by fragments' swapping), in case of network error. They should be extended by other classes.
1) BaseNetContentView.java - base interface for all Views, that should show "network error" UI.
public interface BaseNetContentView {
public void showNetworkContentError();
}
2) BaseNetContentFragment.java - base for all Fragments, that should show "network error" UI. It contains listener and corresponding interface.
public abstract class BaseNetContentFragment extends Fragment implements BaseNetContentView {
#Nullable
private OnNetworkErrorListener mOnNetworkErrorListener;
protected final void tryToShowNetworkError() {
if (mOnNetworkErrorListener != null) {
mOnNetworkErrorListener.onNetworkError();
}
}
protected final boolean hasOnNetworkErrorListener() {
return mOnNetworkErrorListener != null;
}
public final void setOnNetworkErrorListener(
#Nullable OnNetworkErrorListener onNetworkErrorListener) {
mOnNetworkErrorListener = onNetworkErrorListener;
}
public interface OnNetworkErrorListener {
public void onNetworkError();
}
}
3) BaseNetContentActivity - base Activity, that handling network error by changing UI fragments
public abstract class BaseNetContentActivity<T extends BaseNetContentFragment>
extends AppCompatActivity implements BaseNetContentFragment.OnNetworkErrorListener {
private static final String TAG = "BaseNetContentActivity";
#Override
public void onNetworkError() {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = getCurrentContentFragment(fragmentManager);
// Skip if already NetworkErrorFragment
if (!(fragment instanceof NetworkErrorFragment)) {
setFragmentToActivity(fragmentManager, new NetworkErrorFragment());
}
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
Fragment fragment = getCurrentContentFragment(getSupportFragmentManager());
// NetworkErrorFragment is self-sufficient
if (fragment instanceof NetworkErrorFragment) {
return;
}
setNetworkContentFragmentToActivity(savedInstanceState);
}
#Override
public void onAttachFragment(Fragment fragment) {
// Set appropriate listener to fragment
if (fragment instanceof NetworkErrorFragment) {
((NetworkErrorFragment) fragment)
.setOnReloadContentListener(new NetworkErrorFragment.OnReloadContentListener() {
#Override
public void onReloadContent() {
setNetworkContentFragmentToActivity(null);
}
});
} else if (fragment instanceof BaseNetContentFragment) {
((BaseNetContentFragment) fragment).setOnNetworkErrorListener(this);
}
// Don't do anything with other fragment's type
}
#NonNull
protected abstract T createNetworkContentFragment();
protected abstract void setPresenter(#NonNull T fragment, #Nullable Bundle savedInstanceState);
#LayoutRes
protected int getLayoutResId() {
return R.layout.basenetworkcontent_act;
}
#IdRes
protected int getContentFrameId() {
return R.id.network_content_frame;
}
private void setNetworkContentFragmentToActivity(#Nullable Bundle savedInstanceState) {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = getCurrentContentFragment(fragmentManager);
if (fragment == null || fragment instanceof NetworkErrorFragment) {
fragment = createNetworkContentFragment();
}
try {
setPresenter((T) fragment, savedInstanceState);
} catch (ClassCastException e) {
// Unexpected fragment type
Log.d(TAG,"Can't set Presenter because of wrong View type (wrong fragment)" + e);
// Casting to T type is safe, because createNetworkFragment() returns T type
fragment = createNetworkContentFragment(); // returns type T
setPresenter((T) fragment, savedInstanceState);
}
setFragmentToActivity(fragmentManager, fragment);
}
private Fragment getCurrentContentFragment(#NonNull FragmentManager fragmentManager) {
return fragmentManager.findFragmentById(getContentFrameId());
}
private void setFragmentToActivity(#NonNull FragmentManager fragmentManager,
#NonNull Fragment fragment) {
fragmentManager.beginTransaction()
.replace(getContentFrameId(), fragment)
.commit();
}
}
4) NetworkErrorFragment
public static class NetworkErrorFragment extends Fragment implements View.OnClickListener {
#Nullable
private OnReloadContentListener mOnReloadContentListener;
private Button mReloadButton;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.networkerror_frag, container, false);
mReloadButton = (Button) root.findViewById(R.id.reload_content_button);
if (mOnReloadContentListener != null) {
mReloadButton.setOnClickListener(this);
} else {
mReloadButton.setVisibility(View.INVISIBLE);
}
return root;
}
#Override
public void onClick(View v) {
if (mOnReloadContentListener != null) {
mOnReloadContentListener.onReloadContent();
}
}
public void setOnReloadContentListener(#Nullable OnReloadContentListener onReloadContentListener) {
mOnReloadContentListener = onReloadContentListener;
}
public interface OnReloadContentListener {
public void onReloadContent();
}
}
Full example at https://github.com/relativizt/android-network-error-ui
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);
}
}
At the moment, i have a viewpager,when page scroll,i have load api for get new information.
But in the first load viewpager,progress loading display two twice because it load first page and next page.
I only want to load the first page in viewpager.
How must I do?
best way is to create one interface. and implement this interface to all fragment.
e.g.
public interface FragmentInterface {
void fragmentBecameVisible();
}
in ur viewpager activity put like this e.g
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener(){
#Override
public void onPageSelected(int position){
FragmentInterface fragment = (FragmentInterface) mAdapter.instantiateItem(viewPager, position);
if (fragment != null) {
fragment.fragmentBecameVisible();
}
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2){
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
in your fragment like
public class TestFragmet extends Fragment implements FragmentInterface {
#Override
public void fragmentBecameVisible() {
// your task to execute
}
}
Put OffScreenPageLimit to ZERO. It won't load other fragments.
viewPager.setOffscreenPageLimit(0);
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.
I must clear that I am looking for an example or answer where I can use various differnt layout designs in a viewpager and the data in all the pages would be dynamic and all pages can be interacted by the user.
My Use Case and current approach towards the problem :
So I have got 8 different types of question types and so I have created layouts for all of them. Also I the data in the views for these layouts has to be populated via java Map that has fetched data from the sqlite DB.
But a test may contain 25 questions with different layouts out of the above 8. And for all these 25 questions I want to use a Viewpager and a Fragment that will return the required layout based on the passed question type value out of my java map.
My apporach towards this :
I have created an activity and have inflated it with a viewpager layout :
R.layout.practice_pager
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/test_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
*Activity Edited code *
public class TestActivity extends FragmentData implements FragmentData{
FragmentManager manager=getSupportFragmentManager();
private ViewPager mViewPager;
private MyFragmentPagerAdapter mMyFragmentPagerAdapter;
int PAGE_COUNT = 0;
GrePracticeTestRecord p=new GrePracticeTestRecord();
private HashMap<Integer, GrePracticeResultRecord> mPracResultMap;
public static int fragmentToReturn=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.practice_pager);
mViewPager = (ViewPager) findViewById(R.id.test_container);
//This function is called to fetch the data from sqlite db and fill the map with data
LoadingTestView();
PAGE_COUNT=mPracRecordMap.size();
initPager();
}
//here I initialize the pager and set the adapter to it
public void initPager()
{
p.setQUES(mPracRecordMap.get(1).getQUES());
p.setEXPL(mPracRecordMap.get(1).getEXPL());
fragmentToReturn=Integer.parseInt(mPracRecordMap.get(1).getQTYPE());
setData(p);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),fList);
mViewPager.setAdapter(mMyFragmentPagerAdapter);
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
mMyFragmentPagerAdapter.notifyDataSetChanged();
p.setQUES(mPracRecordMap.get(mViewPager.getCurrentItem()+1).getQUES());
p.setEXPL(mPracRecordMap.get(mViewPager.getCurrentItem()+1).getEXPL());
setData(p);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public void setData(GrePracticeTestRecord p) {
// TODO Auto-generated method stub
}
#Override
public GrePracticeTestRecord getData() {
// TODO Auto-generated method stub
return p;
}
}
My Adapter Edited code
public class MyFragmentPagerAdapter extends FragmentStatePagerAdapter{
private List<Fragment> fragments;
public MyFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
/** This method will be invoked when a page is requested to create */
#Override
public Fragment getItem(int position) {
System.out.println("value of position "+position);
return this.fragments.get(position);
}
/** Returns the number of pages */
#Override
public int getCount() {
return this.fragments.size();
}
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return MyFragmentPagerAdapter.POSITION_NONE;
}
}
Interface FragmentData
public interface FragmentData {
public void setData(GrePracticeTestRecord p);
public GrePracticeTestRecord getData();
}
TestFragment Edited code
public class TestFragment extends Fragment {
AnswerEnterListener callBack;
FragmentData fD;
Button submitAnswer;
EditText userAnswer;
TextView qText,expl;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.qtype1, container, false);
}
public interface AnswerEnterListener
{
public void onInputAnswer(String ans);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
callBack=(AnswerEnterListener) activity;
fD=(FragmentData) activity;
} catch (Exception e) {
// TODO: handle exception
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
qText=(TextView) getActivity().findViewById(R.id.question_text);
expl=(TextView)getActivity().findViewById(R.id.explanation_text);
qText.setText(Html.fromHtml(fD.getData().getQUES()));
expl.setText(Html.fromHtml(fD.getData().getEXPL()));
}
}
Similar to TestFragment , I have not the other fragments too. Each for a layout type.
Issues :
The first layout is repeated two times at the first time , and also when I swipe back then the position of data is misplaced.
Is this the right approach, I have been suggested by someone that you should use three fragments and update the left and right fragments with data , but this actually bounced off me. Can anyone share a good example of it or a blog.
I must clear that I am looking for an example or answer where I can
use various differnt layout designs in a viewpager and the data in all
the pages would be dynamic and all pages can be interacted by the
user.
This should be quite easy to make but you seem to have complicated this a lot. You need to do something like this:
Get the data from the database with the LoadingTestView(); function.
Initialize the ViewPager and set it's adapter. The adapter will return the proper fragment instance in the getItem() method, based on whatever criteria you have.
In the fragments, retrieve the data from the activity which should expose the data through some method.
I've made a simple example that you can find here.