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

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().

Related

How to restore ExoPlayer after onPause

I have created an ExoPlayer inside a fragment. If during the playback I press the Home button, and get back to the player, the player seems to be empty, the play/pause button does not work anymore. The video is gone. The video player view seems to be completely empty.
public class PlaybackFragment extends Fragment implements View.OnClickListener, EventListener, PlaybackControlView.VisibilityListener {
private static final String TAG = PlaybackFragment.class.getSimpleName();
Video mVideo; //Video object passed in for playback
SimpleExoPlayerView mExoPlayerView;
private TextView mVideoTitle;
private SimpleExoPlayer mExoPlayer;
private FrameLayout mFullScreenButton; //Holds the fullscreen icon
private Dialog mFullScreenDialog;
private ImageView mFullScreenIcon;
private boolean mExoPlayerFullScreen; //full-screen dialog
private static final String LOG_TAG = PlaybackFragment.class.getSimpleName();
/**
* Root ViewGroup holding the fragment
*/
private ViewGroup mPlayerViewContainer;
/**
* Context used across the class
*/
private Context mContext;
/**
* Activity to which the fragment is added
*/
private Activity mActivity;
/**
* Uri received from calling activity containing Uri to the video to be played
*/
private Uri mUri;
public PlaybackFragment() {
super();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: ");
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_playback, container, false);
mVideoTitle = root.findViewById(R.id.playback_video_title);
mExoPlayerView = root.findViewById(R.id.exoPlayerView);
mPlayerViewContainer = (ViewGroup) mExoPlayerView.getParent();
return root;
}
/**
* Sets the Uri to video to be played
* #param mediaUri
*/
public void playUri(Uri mediaUri) {
mUri = mediaUri;
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewStateRestored: ");
super.onViewStateRestored(savedInstanceState);
}
private void preparePlayback() {
mExoPlayerView.setControllerVisibilityListener(this);
mExoPlayerView.requestFocus();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(trackSelectionFactory);
mExoPlayer =
ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
mExoPlayerView.setPlayer(mExoPlayer);
DataSource.Factory dsFactory = new DefaultDataSourceFactory(mContext,
com.google.android.exoplayer2.util.Util.getUserAgent(mContext, mActivity.getApplicationInfo().processName), null);
// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource videoSource = new ExtractorMediaSource(mUri,
dsFactory, extractorsFactory, null, null);
// Prepare the player with the source.
mExoPlayer.prepare(videoSource);
//start player
mExoPlayer.setPlayWhenReady(true);
mExoPlayer.addListener(this);
// initFullscreenDialog();
initFullscreenButton();
}
#Override
public void onVisibilityChange(int visibility) {
}
#Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated: ");
super.onActivityCreated(savedInstanceState);
mContext = mActivity = getActivity();
preparePlayback();
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onClick(View v) {
}
public void pause() {
mExoPlayer.stop();
}
//Interface to trigger playback-finish event in the parent activity
public interface OnPlayStateListener {
void onPlayFinish();
}
OnPlayStateListener mCallback;
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if(playbackState == Player.STATE_ENDED) {
quitFullScreen(); //Leave fullscreen after playback finished
mCallback.onPlayFinish();
Log.i(LOG_TAG,"onPlayFinish() triggered");
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnPlayStateListener) context;
} catch (ClassCastException e){
throw new ClassCastException(context.toString() + " must implement OnPlayStateListener");
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
Toast.makeText(mContext,error.getLocalizedMessage(),Toast.LENGTH_SHORT).show();
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
private void goFullScreen() {
Util.showFullScreen(mActivity, mExoPlayerView);
// ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);
// mFullScreenDialog.addContentView(mExoPlayerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_fullscreen_exit));
mExoPlayerFullScreen = true;
// mFullScreenDialog.show();
}
private void quitFullScreen() {
Util.quitFullScreen(mActivity, mPlayerViewContainer, mExoPlayerView);
// ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);
// FrameLayout frame = mActivity.findViewById(R.id.main_media_frame);
// frame.addView(mExoPlayerView);
mExoPlayerFullScreen = false;
// mFullScreenDialog.dismiss();
mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_fullscreen_expand));
}
private void initFullscreenButton() {
PlaybackControlView controlView = mExoPlayerView.findViewById(R.id.exo_controller);
mFullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
mFullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button);
mFullScreenButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mExoPlayerFullScreen) {
goFullScreen();
}
else {
quitFullScreen();
}
}
});
}
private void initFullscreenDialog() {
//The following uses anonymous class to create a custom dialog
mFullScreenDialog = new Dialog(mContext, android.R.style.Theme_DeviceDefault_NoActionBar_Fullscreen) {
#Override
public void onBackPressed() {
if(mExoPlayerFullScreen) {
quitFullScreen();
}
super.onBackPressed();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
if(mExoPlayer != null) mExoPlayer.release();
Log.d(TAG, "onDestroy: ");
}
/**
* Moves forward or backward by step milliseconds <br/>
* #param step specifies step size. It is a positive or negative integer for moving forward and
* backward, respectively.
*/
public void movePlayPos(int step) {
mExoPlayer.seekTo(mExoPlayer.getCurrentPosition() + step);
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
Log.d(TAG, "onSaveInstanceState: ");
super.onSaveInstanceState(outState);
outState.putLong("position", mExoPlayer.getCurrentPosition());
}
}
I have checked the fragment lifecycle in Logcat, but it seems that after I get back to the player activity, only the fragment's onStart() is called. So, there is no way to restore the state. Any ideas how I can save a video playback position upon pause and resume playback on the activity's onResume?
You can save the current position of exoplayer in onPause() and then seek to that position in onResume().
try this:
long position;
#Override
protected void onPause() {
super.onPause();
if(mExoPlayerView != null && mExoPlayerView.getPlayWhenReady()) {
position = mExoPlayerView.getCurrentPosition();
mExoPlayerView.setPlayWhenReady(false);
}
}
#Override
protected void onResume() {
super.onResume();
if(mExoPlayerView != null) {
mExoPlayerView.seekTo(position);
mExoPlayerView.setPlayWhenReady(true);
}
}

Android: How to control when to start/stop a sound in different fragment pages?

I have an activity that has a fragment with 18 pages. In some of the pages, there is a sound that can be played when the play button is clicked. However, when the user clicks on the play button and switches to another page, the sound from the previous page is still playing. How can make it play only in the relevant page, and stop if the page is switched?
public class PageFragment_Bon extends Fragment implements View.OnClickListener{
public static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private Button start, stop, replay;
private MediaPlayer mediaPlayer;
int [] filer = new int[18];
public static PageFragment_Bon newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
PageFragment_Bon fragment = new PageFragment_Bon();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPage = getArguments().getInt(ARG_PAGE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_boenner, container, false);
start = (Button) view.findViewById(R.id.start);
start.setOnClickListener(this);
stop = (Button) view.findViewById(R.id.stop);
stop.setOnClickListener(this);
replay = (Button) view.findViewById(R.id.replay);
replay.setOnClickListener(this);
filer[2] = R.raw.Hello;
filer[4] = R.raw.Welcome;
filer[14] = R.raw.Intro;
filer[15] = R.raw.Farewell;
filer[16] = R.raw.Leave;
if(filer[mPage] != 0){
start.setVisibility(View.VISIBLE);
stop.setVisibility(View.VISIBLE);
replay.setVisibility(View.VISIBLE);
}
return view;
}
#Override
public void onResume() {
super.onResume();
if(mediaPlayer != null)
{
mediaPlayer.seekTo(0);
mediaPlayer.start();
}
}
#Override
public void onPause() {
super.onPause();
if(mediaPlayer != null)
{
mediaPlayer.pause();
}
}
#Override
public void onClick(View v) {
if(mediaPlayer == null)
mediaPlayer = MediaPlayer.create(getActivity().getBaseContext(), filer[mPage]);
if(v == start){
mediaPlayer.start();
}
else if(v == stop){
mediaPlayer.pause();
}
else if(v == replay){
mediaPlayer.seekTo(0);
mediaPlayer.start();
}
}
}
You can try something like this inside your fragments that have sounds
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(!isVisibleToUser){
if(mediaPlayer!=null) {
if (mediaPlayer.isPlaying()) {
try {
mediaPlayer.reset();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
By overriding setUserVisibleHint in your Fragment, you can determine when Fragment becomes visible
Edit
Start media player with try and catch (in case if there is any exception while you are trying to start media player)
if(v == start){
try {
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
It is better to override onPause() and onResume() in fragment. And we need to save the current position for media player. So, I am using the shared preference to save the position for media player.
In onCreateView,
mediaPlayer_position = getActivity().getSharedPreferences("PLAY_PAUSE", Activity.MODE_PRIVATE).getInt("CHECK_PLAY_PAUSE", 0);
#Override
public void onResume() {
super.onResume();
int position = getActivity().getSharedPreferences("PLAY_PAUSE", Activity.MODE_PRIVATE).getInt("CHECK_PLAY_PAUSE", 0);
if(position > 0) {
try {
mediaPlayer.setDataSource(mp3_link);//mp3_link from url to MediaPlayer data source
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaPlayer.seekTo(position);
mediaPlayer.start();
}
}
#Override
public void onPause() {
super.onPause();
if(mediaPlayer.isPlaying()) {
mediaPlayer_position = mediaPlayer.getCurrentPosition();
getActivity().getSharedPreferences("PLAY_PAUSE", Context.MODE_PRIVATE).edit().putInt("CHECK_PLAY_PAUSE", mediaPlayer_position).apply();
mediaPlayer.pause();
}
}
Ref: "https://www.hrupin.com/2011/02/example-of-streaming-mp3-mediafile-with-android-mediaplayer-class"

Stop Handler when Fragment is destroyed

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.

Listview showing strange behavior

What I have
My app has only one activity and all other are fragments
One of the fragment displays a listview that is populated from datbase through a cursor adapter.
My Problem
Everytime the screen rotates or i return back to the app after pressing home button, what i see is that I have another listview that lies over another one i.e another same listview is also populated over pre existing one.
Here is my code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tasks_list_frag, container, false);
Log.d("HirakDebug", "tasksListFrag View Created");
tasks_Database_Operations tasksDatabaseOperations = new tasks_Database_Operations(getActivity());
Cursor cursor = tasksDatabaseOperations.readData();
taskslist = (SwipeListView) view.findViewById(R.id.tasks_list);
adapter = new tasksCursorAdapter(getActivity(), cursor, CursorAdapter.FLAG_AUTO_REQUERY);
taskslist.setAdapter(adapter);
delete = (Button) view.findViewById(R.id.deleteAll);
delete.setOnClickListener(this);
taskslist.setSwipeListViewListener(new BaseSwipeListViewListener() {
#Override
public void onOpened(int position, boolean toRight) {
}
#Override
public void onClosed(int position, boolean fromRight) {
}
#Override
public void onListChanged() {
}
#Override
public void onMove(int position, float x) {
}
#Override
public void onStartOpen(int position, int action, boolean right) {
Log.d("swipe", String.format("onStartOpen %d - action %d", position, action));
}
#Override
public void onStartClose(int position, boolean right) {
Log.d("swipe", String.format("onStartClose %d", position));
}
#Override
public void onClickBackView(int position) {
Log.d("swipe", String.format("onClickBackView %d", position));
taskslist.closeAnimate(position);//when you touch back view it will close
}
#Override
public void onDismiss(int[] reverseSortedPositions) {
}
});
taskslist.setSwipeMode(SwipeListView.SWIPE_MODE_RIGHT); // there are five swiping modes
// there are four swipe actions
taskslist.setSwipeActionRight(SwipeListView.SWIPE_ACTION_REVEAL);
taskslist.setOffsetRight(convertDpToPixel(80f)); // right side offset
taskslist.setAnimationTime(500); // Animation time
// enable or disable SwipeOpenOnLongPress
Log.d("HirakDebug", "tasksListFrag Adapter set");
return view;
}
public int convertDpToPixel(float dp) {
DisplayMetrics metrics = getResources().getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return (int) px;
}
#Override
public void onResume() {
super.onResume();
Log.d("HirakDebug", "taskListFrag resumed");
getRetainInstance();
}
#Override
public void setRetainInstance(boolean retain) {
super.setRetainInstance(true);
}
#Override
public void onPause() {
super.onPause();
Log.d("LifeCycle", "tLF Pause");
}
#Override
public void onStop() {
super.onStop();
Log.d("LifeCycle", "tLF Stop");
}
#Override
public void onDestroyView() {
super.onDestroyView();
Log.d("LifeCycle", "tLF DestroyView");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("LifeCycle", "tLF Destroy");
}
#Override
public void onDetach() {
super.onDetach();
Log.d("LifeCycle", "tLF Detach");
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d("LifeCycle", "tLF Attach");
}
#Override
public void onStart() {
super.onStart();
Log.d("LifeCycle", "tLF Start");
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d("LifeCycle", "tLF saveInstance State");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onClick(View v) {
if (v == delete) {
Log.d("HirakDebug", "Delete Button Pressed");
tasks_Database_Operations tasksDatabaseOperations = new tasks_Database_Operations(getActivity());
SQLiteDatabase sqLiteDatabase = tasksDatabaseOperations.getWritableDatabase();
sqLiteDatabase.delete(tasksDatabaseOperations.ASSIS_TASK_TABLE_NAME, null, null);
Log.d("HirakDebug", "tLF All Data Deleted");
adapter.notifyDataSetChanged();
Log.d("HirakDebug", "tLF ArrayList Cleared");
sqLiteDatabase.close();
}
}
Just check inside your activity onCreate method
if (saveInstance == null) {
mFragment = new Fragment();
fm.beginTransaction().replace(mFragment, TAG_TASK_FRAGMENT).commit();
}

Android: Execute http requests via Service

I have a trouble with getting Activity(Nullpointerexception) after that I have rotate screen and received callback from AsyncTask to update my views of the fragment. If I wont change orientation then everything is OK(but not all the time, sometimes this bug appears)
My main activity:
public class MainActivity extends SherlockFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pager_layout);
fm = getSupportFragmentManager();
fm.addOnBackStackChangedListener(this);
session = new SessionManager(getApplicationContext());
if (session.isAuthorizated()) {
disableTabs();
FragmentTransaction ft = fm.beginTransaction();
if (session.termsAndConditions()) {
ft.replace(android.R.id.content, new TermsAndConditionsFragment(), "terms-and-conditions").commit();
}
}
} else {
enableTabs();
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(actionBar.newTab().setText("Log in"), LoginFragment.class, null);
mTabsAdapter.addTab(actionBar.newTab().setText("Calculator"), CalculatorFragment.class, null);
}
}
That`s my fragment:
public class TermsAndConditionsFragment extends SherlockFragment implements OnClickListener, OnTouchListener, OnEditorActionListener, ValueSelectedListener, AsyncUpdateViewsListener {
private static final String TAG = "TermsAndConditionsFragment";
private TermsAndConditionsManager termsAndConditionsM;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prepareData();
}
public void prepareData() {
if (getSherlockActivity() == null)
Log.d(TAG, "Activity is null");
termsAndConditionsM = new TermsAndConditionsManager(getSherlockActivity().getApplicationContext());
termsAndConditions = termsAndConditionsM.getTermsAndConditions();
...
// some stuff
...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = init(inflater, container);
return rootView;
}
private View init(LayoutInflater inflater, ViewGroup container) {
rootView = inflater.inflate(R.layout.fragment_terms_and_conditions, container, false);
//bla bla bla
return rootView;
}
public void updateTermsAndConditionsView() {
//update views here
}
#Override
public void onClick(View v) {
ft = fm.beginTransaction();
switch (v.getId()) {
case R.id.etHowMuch:
d = NumberPaymentsPickerFragment.newInstance(getSherlockActivity(), Integer.valueOf(howMuch.replace("£", "")), 0);
d.setValueSelectedListener(this);
d.show(getFragmentManager(), Const.HOW_MUCH);
break;
}
}
#Override
public void onValueSelected() {
Bundle args = new Bundle();
...
ExecuteServerTaskBackground task = new ExecuteServerTaskBackground(getSherlockActivity());
task.setAsyncUpdateViewsListener(this);
task.action = ServerAPI.GET_TERMS_AND_CONDITIONS;
task.args = args;
task.execute();
}
#Override
public void onUpdateViews() {
prepareData();
updateTermsAndConditionsView();
}
}
My AsyncTask with callback:
public class ExecuteServerTaskBackground extends AsyncTask<Void, Void, Void> {
private static final String TAG = "ExecuteServerTaskBackground";
Activity mActivity;
Context mContext;
private AsyncUpdateViewsListener callback;
public ExecuteServerTaskBackground(Activity activity) {
this.mActivity = activity;
this.mContext = activity.getApplicationContext();
}
public void setAsyncUpdateViewsListener(AsyncUpdateViewsListener listener) {
callback = listener;
}
#Override
protected Void doInBackground(Void... params) {
ServerAPI server = new ServerAPI(mContext);
if (!args.isEmpty())
msg = server.serverRequest(action, args);
else
msg = server.serverRequest(action, null);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
callback.onUpdateViews();
}
}
Why does it behave so? How can I get activity correctly if I change orientation.
EDIT:
As I understand correctly nullpointer appears after orientation changed and asynctask executed due to wrong reference between asyctask and Activity. Recreated activity doesnt have this reference thats why when I receive callback I use wrong activity reference which isn`t exist anymore. But how can I save current activity reference?
EDIT:
I have decided to try realize my task throughout Service and that`s what I have done.
Activity:
public class MainFragment extends Fragment implements ServiceExecutorListener, OnClickListener {
private static final String TAG = MainFragment.class.getName();
Button btnSend, btnCheck;
TextView serviceStatus;
Intent intent;
Boolean bound = false;
ServiceConnection sConn;
RESTService service;
ProgressDialog pd = new ProgressDialog();
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
intent = new Intent(getActivity(), RESTService.class);
getActivity().startService(intent);
sConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "MainFragment onServiceConnected");
service = ((RESTService.MyBinder) binder).getService();
service.registerListener(MainFragment.this);
if (service.taskIsDone())
serviceStatus.setText(service.getResult());
bound = true;
}
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "MainFragment onServiceDisconnected");
bound = false;
}
};
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
serviceStatus = (TextView) rootView.findViewById(R.id.tvServiceStatusValue);
btnSend = (Button) rootView.findViewById(R.id.btnSend);
btnCheck = (Button) rootView.findViewById(R.id.btnCheck);
btnSend.setOnClickListener(this);
btnCheck.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSend:
pd.show(getFragmentManager(), "ProgressDialog");
service.run(7);
service.run(2);
service.run(4);
break;
case R.id.btnCheck:
if (service != null)
serviceStatus.setText(String.valueOf(service.taskIsDone()) + service.getTasksCount());
break;
}
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, "Bind service");
getActivity().bindService(intent, sConn, 0);
}
#Override
public void onPause() {
super.onDestroy();
Log.d(TAG, "onDestroy: Unbind service");
if (!bound)
return;
getActivity().unbindService(sConn);
service.unregisterListener(this);
bound = false;
}
#Override
public void onComplete(String result) {
Log.d(TAG, "Task Completed");
pd.dismiss();
serviceStatus.setText(result);
}
}
Dialog:
public class ProgressDialog extends DialogFragment implements OnClickListener {
final String TAG = ProgressDialog.class.getName();
public Dialog onCreateDialog(Bundle savedInstanceState) {
setRetainInstance(true);
AlertDialog.Builder adb = new AlertDialog.Builder(getActivity())
.setTitle("Title!")
.setPositiveButton(R.string.yes, this)
.setNegativeButton(R.string.no, this)
.setNeutralButton(R.string.maybe, this)
.setCancelable(false)
.setMessage(R.string.message_text)
.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
return true;
}
});
return adb.create();
}
public void onClick(DialogInterface dialog, int which) {
int i = 0;
switch (which) {
case Dialog.BUTTON_POSITIVE:
i = R.string.yes;
break;
case Dialog.BUTTON_NEGATIVE:
i = R.string.no;
break;
case Dialog.BUTTON_NEUTRAL:
i = R.string.maybe;
break;
}
if (i > 0)
Log.d(TAG, "Dialog 2: " + getResources().getString(i));
}
public void onDismiss(DialogInterface dialog) {
Log.d(TAG, "Dialog 2: onDismiss");
// Fix to avoid simple dialog dismiss in orientation change
if ((getDialog() != null) && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
Log.d(TAG, "Dialog 2: onCancel");
}
}
Service:
public class RESTService extends Service {
final String TAG = RESTService.class.getName();
MyBinder binder = new MyBinder();
ArrayList<ServiceExecutorListener> listeners = new ArrayList<ServiceExecutorListener>();
Handler h = new Handler();
RequestManager mRequest;
ExecutorService es;
Object obj;
int time;
StringBuilder builder;
String result = null;
public void onCreate() {
super.onCreate();
Log.d(TAG, "RESTService onCreate");
es = Executors.newFixedThreadPool(1);
obj = new Object();
builder = new StringBuilder();
}
public void run(int time) {
RunRequest rr = new RunRequest(time);
es.execute(rr);
}
class RunRequest implements Runnable {
int time;
public RunRequest(int time) {
this.time = time;
Log.d(TAG, "RunRequest create");
}
public void run() {
Log.d(TAG, "RunRequest start, time = " + time);
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Log.d(TAG, "RunRequest obj = " + obj.getClass());
} catch (NullPointerException e) {
Log.d(TAG, "RunRequest error, null pointer");
}
builder.append("result " + time + ", ");
result = builder.toString();
sendCallback();
}
}
private void sendCallback() {
h.post(new Runnable() {
#Override
public void run() {
for (ServiceExecutorListener listener : listeners)
listener.onComplete();
}
});
}
public boolean taskIsDone() {
if (result != null)
return true;
return false;
}
public String getResult() {
return result;
}
public void registerListener(ServiceExecutorListener listener) {
listeners.add(listener);
}
public void unregisterListener(ServiceExecutorListener listener) {
listeners.remove(listener);
}
public IBinder onBind(Intent intent) {
Log.d(TAG, "RESTService onBind");
return binder;
}
public boolean onUnbind(Intent intent) {
Log.d(TAG, "RESTService onUnbind");
return true;
}
public class MyBinder extends Binder {
public RESTService getService() {
return RESTService.this;
}
}
}
As you mention in your edit, the current Activity is destroyed and recreated on orientation change.
But how can I save current activity reference?
You shouldn't. The previous Activity is no longer valid. This will not only cause NPEs but also memory leaks because the AsyncTask might hold the reference to old Activity, maybe forever.
Solution is to use Loaders.

Categories

Resources