Stop Handler when Fragment is destroyed - android

I am starting handler in a fragment like below :
public class FeedsFragment extends Fragment{
private Handler handler;
private Runnable runnableCode;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Create the Handler object (on the main thread by default)
handler = new Handler();
// Define the task to be run here
runnableCode = new Runnable() {
#Override
public void run() {
// Get the No of New Feeds
getFeedCounts();
// Repeat this runnable code again every 30 seconds
handler.postDelayed(runnableCode, 30,000);
}
};
handler.post(runnableCode);
}
#Override
public void onDetach() {
super.onDetach();
handler.removeCallbacksAndMessages(runnableCode);
}
#Override
public void onDestroyView() {
handler.removeCallbacksAndMessages(runnableCode);
super.onDestroyView();
}
#Override
public void onPause() {
super.onPause();
handler.removeCallbacksAndMessages(runnableCode);
}
#Override
public void onResume() {
/**
* Start the handler again if coming back from any profile page
* or hashtag search
*/
isHandlerStopped = false;
handler.post(runnableCode);
super.onResume();
}
I am removing all callbacks in onDetach and onDestroyView functions, but it is not working.
When I load a different fragment in the same activity this handler
doesn't stop itself, handler stops only when I load some other
activity in my application.
I want this handler to run only when the fragment in which I am using it is loaded in the activity, if some other fragment is being loaded in the same activity it should be stopped.
Also, this fragment is inside viewpager, so when I switch the tab and other fragment gets loaded the handler doesn't stop. Please check the below code in activity :
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
FeedsFragment feedsFragment;
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
// when user do a swipe the selected tab change
switch (position){
case 0:
break;
case 1:
feedsFragment.removeHandler();
break;
case 2:
feedsFragment.removeHandler();
break;
case 3:
feedsFragment.removeHandler();
break;
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
FragmentAdapter adapter = new FragmentAdapter(getActivity().getSupportFragmentManager());
feedsFragment = FeedsFragment.newInstance(bundle);
adapter.addFragmentWithIcon(FeedsFragment.newInstance(null))
, mContext.getResources().getString(R.string.feed_tab_name));
adapter.addFragmentWithIcon(FeedFriendRequests.newInstance(null),
mContext.getResources().getString(R.string.requests_tab_name));
adapter.addFragmentWithIcon(NewMessagesFragment.newInstance(null), mContext.getResources().getString(R.string.message_tab_name));
adapter.addFragmentWithIcon(NotificationFragment.newInstance(null)
, mContext.getResources().getString(R.string.notification_tab_name));
pager.setAdapter(adapter);
When I select any other tab at that time I'm calling the method removeHandler in FeedFragment.
public void removeHandler(){
handler.removeCallbacks(runnableCode);
}
But still handler is getting called.
Please help me if anyone have idea about this.
Thanks a lot in advance

I don't think it has to do with your fragment lifecycle. You need to replace
handler.removeCallbacksAndMessages(runnableCode);
// this does not seem to work
with
handler.removeCallbacks(runnableCode);
Just an alternative create a method in FeedsFragment
public void removeHandler()
{
Log.i("Stop Handler ","Yes");
handler.removeCallbacks(runnableCode);
}
In activity when you want to replace fragment
if(feedsFragment.isAdded())
{
feedsFragment.removeHandler();
}
...// rest of the code to replace the fragment with new one
Edit:
viewPager.addOnPageChangeListener(pageChangeListener);
viewPager.post(new Runnable() {
#Override
public void run() {
pageChangeListener.onPageSelected(0);
}
});
Then
ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {
int currentPosition = 0;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int newPosition) {
FragmentLifeCycle fragmentToShow = (FragmentLifeCycle) adapter.getItem(newPosition);
fragmentToShow.onResumeFragment();
if(newPosition==0)
{
currentPosition = newPosition;
}else {
FragmentLifeCycle fragmentToHide = (FragmentLifeCycle) adapter.getItem(currentPosition);
fragmentToHide.onPauseFragment();
currentPosition = newPosition;
}
}
Have a interface
public interface FragmentLifeCycle {
void onPauseFragment();
void onResumeFragment();
}
Implement the interface in all fragments. Assuming the handler is used in Fragment at position 0.
#Override
public void onPauseFragment() {
if(handler!=null)
handler.removeCallbacks(runnableCode);
}
#Override
public void onResumeFragment() {
handler = new Handler();
// Define the task to be run here
runnableCode = new Runnable() {
#Override
public void run() {
Log.i("Do ","Something");
handler.postDelayed(runnableCode, 3000);
}
};
handler.post(runnableCode);
}
In other fragment just implement the interface and do nothing.

Related

How to check if a Fragment is visible in a ViewPager?

First, none of the solution worked provided on stackoverflow because of either change in the Android or my problem was totally different.
What I want to achive - I want to detect when my view (for example - ThirdFragment) comes in focus and becomes visible to user. Also, it should detect when user swipes away from the current view to next view or previous view.
Why I want to do that? - Well, I am creating a media player in one of the view as shown in the example fragment code and user can play any music listed in the view by tapping on the music in the list but when user swipes to next screen it should stop.
What is the problem? - The problem is mediaplayer doesn't stop right after switching from the view but [***] if I swipe two step away, it stops but not cleanly, it jitters in the background.
What I have tried before - I have tried this setUserVisibleHint but it wasn't working because it has been depricated. onPause, onDetach, onStop none is working as mentioned here[***]
/*------Home ViewPager----- */
public class HomeFragment extends Fragment {
private ViewPager viewPager;
private String[] titles = {"First","Second","Third","Fourth"};
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView subtitle = getActivity().findViewById(R.id.toolbar_right_subtitle);
subtitle.setText(titles[0]);
return inflater.inflate(R.layout.fragment_home, container, false);
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
/* pages */
viewPager = view.findViewById(R.id.interfaces);
InterfaceAdapter interfacePagerAdapter = new InterfaceAdapter(getActivity().getSupportFragmentManager(),0);
viewPager.setAdapter(interfacePagerAdapter);
viewPager.setCurrentItem(0);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
TextView subtitle = getActivity().findViewById(R.id.toolbar_right_subtitle);
subtitle.setText(titles[position]);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
}
/*---------- My Fragment Adapter --------- */
public class InterfaceAdapter extends FragmentStatePagerAdapter {
public InterfaceAdapter(#NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
#NonNull
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new FirstFragment();
case 1:
return new SecondFragment();
case 2:
return new ThirdFragment();
case 3:
return new FourthFragment();
default:
return null;
}
}
#Override
public int getCount() {
return 4;
}
}
/*---------- One of my Fragment --------- */
public class ThirdFragment extends Fragment {
//------Media Player ----------
private MusicAdapter musicAdapter;
private List<SongData> songsList;
private ListView listView;
private static MediaPlayer mediaPlayer;
private Handler handler = new Handler();
private Runnable runnable;
private ImageView playButton, prevButton, nextButton;
private SeekBar seekbarPayer;
private TextView playerTime,songTitle;
private int pos = 0;
public ThirdFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
eventViewModels = new ViewModelProvider((FragmentActivity) getContext()).get(EventViewModels.class);
mediaPlayer = new MediaPlayer();
getAllSongs();
return inflater.inflate(R.layout.fragment_focus, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//--------Music Player---------------
mediaPlayer = new MediaPlayer();
playButton = view.findViewById(R.id.playpause_player);
prevButton = view.findViewById(R.id.prev_player);
nextButton = view.findViewById(R.id.next_player);
seekbarPayer = view.findViewById(R.id.seekbar_player);
playerTime = view.findViewById(R.id.playertime);
songTitle = view.findViewById(R.id.songname);
songTitle.setSelected(true);
songsList = new ArrayList<>();
musicAdapter = new MusicAdapter(getActivity(), songsList);
listView = view.findViewById(R.id.songList);
listView.setAdapter(musicAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
pos = position;
playMusicFile(songsList.get(position).getUri(),songsList.get(position).getTitle());
}
});
//play or pause music
playButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playPause();
}
});
//next music
nextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (pos < songsList.size() - 1) {
pos++;
} else {
pos = 0;
}
playMusicFile(songsList.get(pos).getUri(),songsList.get(pos).getTitle());
}
});
//next music
prevButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (pos > 0) {
pos--;
} else {
pos = songsList.size() - 1;
}
playMusicFile(songsList.get(pos).getUri(),songsList.get(pos).getTitle());
}
});
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
eventViewModels.getTrigger().observe((LifecycleOwner) getContext(), new Observer<Integer>() {
#Override
public void onChanged(#Nullable final Integer val) {
assert val!=null;
Log.d("ObserverValue",val+"");
}
});
}
#Override
public void onStart() {
super.onStart();
getAllSongs();
Log.d("SystemStatus","Start");
}
#Override
public void onResume() {
super.onResume();
getAllSongs();
Log.d("SystemStatus","Resume");
}
#Override
public void onStop() {
super.onStop();
stopPlayer();
Log.d("SystemStatus","Stop");
}
#Override
public void onDetach() {
super.onDetach();
stopPlayer();
Log.d("SystemStatus","Detach");
}
#Override
public void onPause() {
super.onPause();
stopPlayer();
Log.d("SystemStatus","Pause");
if (eventViewModels != null && eventViewModels.getTrigger().hasObservers()) {
eventViewModels.getTrigger().removeObservers(this);
}
}
//---Media Player-----
private void getAllSongs() {
/*
* get all songs and add it into songlist
*/
}
private void playMusicFile(String filePath,final String title){
if(mediaPlayer!=null) {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
stopPlayer();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void playPause() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}else {
mediaPlayer.pause();
}
}
private void stopPlayer() {
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.reset();
}
}
}
Edit:
Thanks to #ianhanniballake for providing the solution.
It solved my problem "partially". I can stop the mediaplayer but I can't stop my observer observing the event on pause. code update
You're passing in 0 as the behavior, which corresponds to the deprecated BEHAVIOR_SET_USER_VISIBLE_HINT. By using that behavior, setUserVisibleHint() will be called with true when your fragment becomes the current page and false when it is not.
If you switch to the not deprecated BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, then only the current page will get onResume(). When the page changes, that Fragment will get a callback to onPause() and the newly active fragment will get a call to onResume().

Why my Image Slider is Swipe Fast after Comeback to Activity or Fragment android

I try to get Image from Server side and make Image Slider to swipe and Automatic Swipe Slider using Timer() ,in that Fragment my slider work perfectly but when i going to other Activity or Fragment and comeback to Slider Fragment slider goes slide fast, but i don't understand what wrong with my code . Please check my code :- (I use Fragment)
private void slider() {
mPager.setAdapter(new SlidingImage_Pager_Adapter(getContext(), imageModelArrayList));
indicator.setViewPager(mPager);
final float density = getResources().getDisplayMetrics().density;
//Set circle indicator radius
indicator.setRadius(5 * density);
NUM_PAGES = imageModelArrayList.size();
// Auto start of viewpager
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == NUM_PAGES) {
currentPage = 0;
}
mPager.setCurrentItem(currentPage++, true);
}
};
// Automatic Swip viewPager
swipeTimer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
}, 3500, 3500);
// Pager listener over indicator
indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
currentPage = position;
}
#Override
public void onPageScrolled(int pos, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int pos) {
}
});
}
#Override
public void onDestroyView() {
super.onDestroyView();
swipeTimer.cancel();
}
I also put swipeTimer cancel in onDestroyView() method but it's not working for me.

Repeatedly call a function in a fragment?

I have a fragment that stems off the main activity. I am trying to have a textbox update with the users GPS location as they move around. I currently have it so every time you resume the fragment it updates, but I would like it to happen automatically every 10 seconds or so.
I am currently attempting to use runOnUiThread, which didn't cause my app to crash but didn't seem to do anything.
Within the fragment:
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView newText = getView().findViewById(R.id.wText);
newText.setText(getStringCoordinates);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
newText.setText(getStringCoordinates);
}
});
}
Try using a handler, something like this should work
private Handler myHandler;
private static final int DELAY = 10000;
#Override
protected void onResume() {
super.onResume();
myHandler = new Handler(Looper.getMainLooper());
checkAgain();
}
private void checkAgain() {
myHandler.postDelayed(()-> checkGps(),DELAY);
}
private void checkGps() {
//do stuff here
checkAgain();
}
#Override
protected void onStop() {
super.onStop();
myHandler.removeCallbacksAndMessages(null);
myHandler = null;
}
basically it sends a message to the main thread every 10 seconds to check gps
the code may be wrong cause I'm writing it off the top of my head, but it should give you a good start
Maybe this is working
public class c_Thread_Update_Fragment extends Thread {
int i =0;
c_Thread_Update_Fragment(FragmentManager fm, ViewPager vp)
{
this.fragmentManager =fm;
this.mViewpager =vp;
}
#Override
public void run() {
while(true)
{
f.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
yourfragmentclass.updateData(i);
}
});
i++;
sleep(1000);
}
}
public static void setFragment(Fragment f){
f =f;
}
}
Implement a public static void update (xxx){} in yourfragmentclass
Use setFragment(f) in your Fragment adapterclass and pass the current fragment.

how to change the image automatically in view pager when it reaches the last image it should come to the first image automatically

I am now implementing the view pager and i want to continuously change the image one by one automatically for a particular periods of time may be once in 5 ms .
I also have to manually allow the user to swipe the images (it is working properly)
But the image is not automatically changing and when it reaches the last image it should come to the first image automatically
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == strImages.length-1) {
currentPage = 0;
}
intro_images.setCurrentItem(currentPage++, true);
}
};
timer = new Timer(); // This will create a new Thread
timer .schedule(new TimerTask() { // task to be scheduled
#Override
public void run() {
handler.post(Update);
}
}, 500, 3000);
I used above code but it does not work properly
try this add addOnPageChangeListener to your viewPager like this
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == strImages.length-1) {
currentPage = 0;
}
intro_images.setCurrentItem(currentPage++, true);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
or for auto change page try this create a time task like this
Timer timer;
timer = new Timer();
timer.scheduleAtFixedRate(new RemindTask(), 0, 3000); // delay*/
private class RemindTask extends TimerTask {
int current = viewPager.getCurrentItem();
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
if (current < strImages.size()) {
viewPager.setCurrentItem(current);
current += 1;
} else {
current = 0;
viewPager.setCurrentItem(current);
}
}
});
}
}
The below code is to get the last position and to scroll to the 1st position:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
{
#Override
public void onPageScrolled(int position, float positionOffset,int positionOffsetPixels) {
if (position == Yourlist.size()-1) {
pager.setCurrentItem(0);
}
pager.setCurrentItem(position++,true);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
The below code is For automatic change picture in every 10s using CountDown Timer:
- First you have to declare one array,which will contain your
images,then after you have to use below code.
int i=0;
new CountDownTimer(1000,1000)
{
#Override
public void onTick(long millisUntilFinished) {}
#Override
public void onFinish() {
iV.setImageDrawable(sdk.getContext().getResources().getDrawable(array[i]));
i++;
if(i== array.length-1) {i=0;}
start();
}
}.start();
You don't need the combination of Handler and TimerTask for this, You can use simple Handler like this using Handler.postDelayed
private Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
int totalCount = intro_images.getAdapter().getCount();
int currentItem = intro_images.getCurrentItem();
if (currentItem == (totalCount - 1)) {
currentItem = 0;
} else {
currentItem = currentItem + 1;
}
intro_images.setCurrentItem(currentItem, true);
handler.postDelayed(this, 3000);
}
};
#Override
protected void onResume() {
super.onResume();
handler.postDelayed(Update, 500);
}
#Override
protected void onPause() {
super.onPause();
if(handler != null) {
handler.removeCallbacks(Update);
}
}
just add listener to your viewpager to get the last position and to scroll to the 1st position.
vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if(position==yourlist.length){
vp.setCurrentItem(0);
}
}
});
AnimationDrawable animation = new AnimationDrawable();

Update postdelay seconds for runnable when page scrolled in viewpager android?

i am displaying images using viewpager and that images will be automatically change after 20 seconds automatically changes images every 20 seconds work perfectly but i want to reset handler postdelayed time if user manually scroll pager and next image should be display 20 seconds
Current Working :
if i am on the first position of viewpager for display image this image time is 20 seconds
after 10 seconds if i scrollpage than next image is display is only 10 seconds but it should be display 20000 seconds
how to update post delayed time for next image display
check below code what i have tried.
onpageselected i have set again post delayed but it was not worked
Thanks
final int []sliderImageArray={R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d,R.drawable.e};
runnable = new Runnable()
{
//int i=0;
public void run()
{ // slider image run
if(startt)
{
if(i==0)
{
viewPager.setCurrentItem(i,false);
}
else
{
viewPager.setCurrentItem(i,false);
}
i++;
if(i>sliderImageArray.length-1)
{
i=0;
}
}
else
{
}
imagehandler.postDelayed(this, 20000);
}
};
imagehandler.postDelayed(runnable,20000);
.
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
i=arg0;
imagehandler.postDelayed(runnable, 20000);
Log.d("onpageselected","----->"+arg0);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
You should call the imagehandler in onPageScrollStateChanged.
So call imagehandler.postDelayed if the ViewPager is in the process of settling to the next position and call imagehandler.removeCallbacks if the ViewPager is being dragged by the user ViewPager Scroll-States.
Here is an example implementation:
final int[] sliderImageArray = new int[] { R.drawable.a, R.drawable.b,
R.drawable.c, R.drawable.d, R.drawable.e };
private Runnable imageSliderRunnable = new Runnable() {
#Override
public void run() {
final int itemCount = sliderImageArray.length;
final int currentItem = viewPager.getCurrentItem();
final int nextItem = (currentItem + 1) % itemCount;
viewPager.setCurrentItem(nextItem, false);
}
};
private void setupViewPager() {
this.viewPager
.setOnPageChangeListener(new SimpleOnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int state) {
switch (state) {
case ViewPager.SCROLL_STATE_DRAGGING:
imagehandler.removeCallbacks(imageSliderRunnable);
break;
case ViewPager.SCROLL_STATE_SETTLING:
imagehandler.removeCallbacks(imageSliderRunnable);
imagehandler.postDelayed(imageSliderRunnable, 20000);
break;
}
}
});
imagehandler.postDelayed(imageSliderRunnable, 20000);
}
DonĀ“t forget to remove the callbacks from the handler if enclosing Activity or Fragment will be paused.
#Override
public void onResume() {
super.onResume();
setupViewPager();
}
#Override
public void onPause() {
imagehandler.removeCallbacks(imageSliderRunnable);
super.onPause();
}
Try do this
imagehandler.removeCallbacksAndMessages(null);
imagehandler.postDelayed(this, 20000);
Here you can do like this way
private int runnableMill = 20000;
now use this object
imagehandler.postDelayed(runnable, runnableMill);
update time
runnableMill = 10000;
On the next execution it will be use new value to execute your thread

Categories

Resources