WEIDER
WEIDER

Reputation: 87

Cancel CountDownTimer in Recyclerview

I have an Recyclerview with countDownTimers which shows the time left to play in Days, Hours, Minutes and Seconds. Now I have the problem, that when I leave the activity, the countDownTimers are still running. Here is my Adapter:

Public class RvAdapterShowVsTeamChallenges extends RecyclerView.Adapter<RvAdapterShowVsTeamChallenges.ViewHolderClass> {

private Boolean boFirstTime = true;

private int [] ayColors = {R.drawable.circle_red,R.drawable.circle_purple,R.drawable.circle_orange,R.drawable.circle_blue,R.drawable.circle_tile};

private Context context;

private CountDownTimer countDownTimerTest;

private DatabaseWrite databaseWrite;

private SharedPrefs sharedPrefs;




public RvAdapterShowVsTeamChallenges(final Context context)
{
    this.context = context;
}


public class ViewHolderClass extends RecyclerView.ViewHolder{

    private TextView txtChallenger;
    private TextView txtOponnent;
    private TextView txtChallengerScore;
    private TextView txtOponnentScore;
    private TextView txtTime;

    private TextView txtChallengerLetters;
    private TextView txtOponnentLetters;

    private ProgressBar prgChallenger;
    private ProgressBar prgOponnent;



    public ViewHolderClass(View itemView) {
        super(itemView);

        sharedPrefs = new SharedPrefs(context);


        txtChallenger = (TextView) itemView.findViewById(R.id.txtChallenger);
        txtOponnent = (TextView) itemView.findViewById(R.id.txtOponnent);

        txtChallengerScore = (TextView) itemView.findViewById(R.id.txtChallengerScore);
        txtOponnentScore = (TextView) itemView.findViewById(R.id.txtOponnentScore);

        txtChallengerLetters = (TextView)itemView.findViewById(R.id.txtChallengerLetter);
        txtOponnentLetters = (TextView)itemView.findViewById(R.id.imgOponnent);

        txtTime = (TextView) itemView.findViewById(R.id.txtTime);


        prgChallenger = (ProgressBar) itemView.findViewById(R.id.prgTimeLeft);
        prgOponnent = (ProgressBar) itemView.findViewById(R.id.prgOponnent);




    }
}



@Override
public ViewHolderClass onCreateViewHolder(final ViewGroup parent, final int viewType) {

    View itemview1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_show_vs_team_challenges,null);


    if(boFirstTime)
    {
        for(int i=0; i<ShowVsTeam.ayListTimeLeftChallenge.size(); i++)
        {
            long substractFalseValue =  ((ShowVsTeam.ayListTimeLeftChallenge.get(i)-1209600033)/1000);

            long total = ((substractFalseValue + 1209600)*1000);

            long difference = (total - System.currentTimeMillis());
            ShowVsTeam.ayListTimeLeftChallenge.set(i, difference);
        }

        boFirstTime = false;
    }


    countDownTimerTest = new CountDownTimer(1800000,1000) {
        @Override
        public void onTick(long millisUntilFinished) {

            for(int i=0; i<ShowVsTeam.ayListTimeLeftChallenge.size(); i++)
            {
                long lngUntilNow = ShowVsTeam.ayListTimeLeftChallenge.get(i);

                long lngNow = (lngUntilNow-(1000/ShowVsTeam.ayListTimeLeftChallenge.size()));

                ShowVsTeam.ayListTimeLeftChallenge.set(i, Long.valueOf(lngNow));
            }

            notifyDataSetChanged();
        }

        @Override
        public void onFinish() {

        }
    }.start();



    return new ViewHolderClass(itemview1);
}

@Override
public void onBindViewHolder(final ViewHolderClass holder, final int position) {


    holder.txtChallenger.setText(sharedPrefs.getTeamnameServer(context));
    holder.txtOponnent.setText(ShowVsTeam.aylistOponnentTeamsChallenge.get(position));

    holder.txtChallengerScore.setText(ShowVsTeam.ayListScoreChallengerChallenge.get(position));
    holder.txtOponnentScore.setText(ShowVsTeam.ayListScoreOppnnentChallenge.get(position));

    String strLetterChallenger = sharedPrefs.getTeamnameServer(context).substring(0,1);
    String strLetterOpponnent = ShowVsTeam.aylistOponnentTeamsChallenge.get(position).substring(0,1);


    holder.txtChallengerLetters.setText(strLetterChallenger);
    holder.txtOponnentLetters.setText(strLetterOpponnent);
    holder.txtOponnentLetters.setBackgroundResource(ayColors[position]);

    holder.prgChallenger.setProgress(Integer.parseInt(ShowVsTeam.ayListScoreChallengerChallenge.get(position)));
    holder.prgOponnent.setProgress(Integer.parseInt(ShowVsTeam.ayListScoreOppnnentChallenge.get(position)));


    long seconds = TimeUnit.MILLISECONDS.toSeconds(ShowVsTeam.ayListTimeLeftChallenge.get(position));
    int intDay = (int)TimeUnit.SECONDS.toDays(seconds);
    long lngHours = TimeUnit.SECONDS.toHours(seconds) - (intDay *24);
    long lngMinutes = TimeUnit.SECONDS.toMinutes(seconds) - (TimeUnit.SECONDS.toHours(seconds)* 60);
    long lngSeconds = TimeUnit.SECONDS.toSeconds(seconds) - (TimeUnit.SECONDS.toMinutes(seconds) *60);

    holder.txtTime.setText(String.valueOf(intDay)+"d:"+String.valueOf(lngHours)+"h:"+String.valueOf(lngMinutes)+"m:"+String.valueOf(lngSeconds)+"s");

}


@Override
public int getItemCount() {
    return ShowVsTeam.aylistOponnentTeamsChallenge.size();
    }


}

I tried to cancel the CountDownTimer onStop() in my Activity but I get a Nullpointer, although the countDownTimers are still running.

In Adapter

public void finishTimer(){
    if(countDownTimerTest!=null){
        countDownTimerTest.cancel();
    }
}

In Activity

    @Override
protected void onStop() {

    if(rvAdapterShowVsTeamChallenges!=null){
        rvAdapterShowVsTeamChallenges.finishTimer();
    }

    super.onStop();
    }
}

Can you help me?

UPDATE

ADAPTER

public class RvAdapterShowVsTeamChallenges extends RecyclerView.Adapter<RvAdapterShowVsTeamChallenges.ViewHolderClass> {

private Boolean boFirstTime = true;

private int [] ayColors = {R.drawable.circle_red,R.drawable.circle_purple,R.drawable.circle_orange,R.drawable.circle_blue,R.drawable.circle_tile};

private Context context;


private DatabaseWrite databaseWrite;

private SharedPrefs sharedPrefs;

private CountDownTimerTest countDownTimerTest;


public RvAdapterShowVsTeamChallenges(final Context context)
{
    this.context = context;
}


public class ViewHolderClass extends RecyclerView.ViewHolder{

    private TextView txtChallenger;
    private TextView txtOponnent;
    private TextView txtChallengerScore;
    private TextView txtOponnentScore;
    private TextView txtTime;

    private TextView txtChallengerLetters;
    private TextView txtOponnentLetters;

    private ProgressBar prgChallenger;
    private ProgressBar prgOponnent;



    public ViewHolderClass(View itemView) {
        super(itemView);

        sharedPrefs = new SharedPrefs(context);


        txtChallenger = (TextView) itemView.findViewById(R.id.txtChallenger);
        txtOponnent = (TextView) itemView.findViewById(R.id.txtOponnent);

        txtChallengerScore = (TextView) itemView.findViewById(R.id.txtChallengerScore);
        txtOponnentScore = (TextView) itemView.findViewById(R.id.txtOponnentScore);

        txtChallengerLetters = (TextView)itemView.findViewById(R.id.txtChallengerLetter);
        txtOponnentLetters = (TextView)itemView.findViewById(R.id.imgOponnent);

        txtTime = (TextView) itemView.findViewById(R.id.txtTime);


        prgChallenger = (ProgressBar) itemView.findViewById(R.id.prgTimeLeft);
        prgOponnent = (ProgressBar) itemView.findViewById(R.id.prgOponnent);

    }
}



@Override
public ViewHolderClass onCreateViewHolder(final ViewGroup parent, final int viewType) {

    View itemview1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_show_vs_team_challenges,null);


    if(boFirstTime)
    {
        for(int i=0; i<ShowVsTeam.ayListTimeLeftChallenge.size(); i++)
        {
            long substractFalseValue =  ((ShowVsTeam.ayListTimeLeftChallenge.get(i)-1209600033)/1000);

            long total = ((substractFalseValue + 1209600)*1000);

            long difference = (total - System.currentTimeMillis());
            ShowVsTeam.ayListTimeLeftChallenge.set(i, difference);
        }

        boFirstTime = false;
    }


    return new ViewHolderClass(itemview1);
}

@Override
public void onBindViewHolder(final ViewHolderClass holder, final int position) {


    holder.txtChallenger.setText(sharedPrefs.getTeamnameServer(context));
    holder.txtOponnent.setText(ShowVsTeam.aylistOponnentTeamsChallenge.get(position));

    holder.txtChallengerScore.setText(ShowVsTeam.ayListScoreChallengerChallenge.get(position));
    holder.txtOponnentScore.setText(ShowVsTeam.ayListScoreOppnnentChallenge.get(position));

    String strLetterChallenger = sharedPrefs.getTeamnameServer(context).substring(0,1);
    String strLetterOpponnent = ShowVsTeam.aylistOponnentTeamsChallenge.get(position).substring(0,1);


    holder.txtChallengerLetters.setText(strLetterChallenger);
    holder.txtOponnentLetters.setText(strLetterOpponnent);
    holder.txtOponnentLetters.setBackgroundResource(ayColors[position]);

    holder.prgChallenger.setProgress(Integer.parseInt(ShowVsTeam.ayListScoreChallengerChallenge.get(position)));
    holder.prgOponnent.setProgress(Integer.parseInt(ShowVsTeam.ayListScoreOppnnentChallenge.get(position)));


    long seconds = TimeUnit.MILLISECONDS.toSeconds(ShowVsTeam.ayListTimeLeftChallenge.get(position));
    int intDay = (int)TimeUnit.SECONDS.toDays(seconds);
    long lngHours = TimeUnit.SECONDS.toHours(seconds) - (intDay *24);
    long lngMinutes = TimeUnit.SECONDS.toMinutes(seconds) - (TimeUnit.SECONDS.toHours(seconds)* 60);
    long lngSeconds = TimeUnit.SECONDS.toSeconds(seconds) - (TimeUnit.SECONDS.toMinutes(seconds) *60);

    holder.txtTime.setText(String.valueOf(intDay)+"d:"+String.valueOf(lngHours)+"h:"+String.valueOf(lngMinutes)+"m:"+String.valueOf(lngSeconds)+"s");

}


@Override
public int getItemCount() {
    return ShowVsTeam.aylistOponnentTeamsChallenge.size();
}


private class CountDownTimerTest extends CountDownTimer {

    private CountDownTimerTest(long millis, long interval) {
        super(millis, interval);
    }

    @Override
    public void onTick(long millisUntilFinished) {

        for(int i=0; i<ShowVsTeam.ayListTimeLeftChallenge.size(); i++)
        {
            long lngUntilNow = ShowVsTeam.ayListTimeLeftChallenge.get(i);

            long lngNow = (lngUntilNow-(1000/ShowVsTeam.ayListTimeLeftChallenge.size()));

            ShowVsTeam.ayListTimeLeftChallenge.set(i, Long.valueOf(lngNow));
        }

        notifyDataSetChanged();

    }

    @Override
    public void onFinish() {


    }
}


public void startTimer(){

    if(countDownTimerTest==null){
        countDownTimerTest= new CountDownTimerTest(1800000,1000);
    }

    countDownTimerTest.start();
}


public void cancelTimer(){

    countDownTimerTest.cancel();
    }

}

Activity

public class ShowVsTeam extends AppCompatActivity {

public int intTextBo = 0;

private String  strMethod;

static ArrayList<String> ayListChallengerTeamsRequest;
static ArrayList<String> ayListChallengerPlayedRequest;
static ArrayList<String> ayListChallengerNumberOfMembersRequest;
static ArrayList<String> ayListChallengerAcceptedRequest;
static ArrayList<String> ayListOponnentTeamsRequest;
static ArrayList<String> ayListOponnnetPlayedRequest;
static ArrayList<String> ayListOponnentNumberOfMembersRequest;

static ArrayList<String> ayListOnlyChallengerTeamsRequest;
static ArrayList<String> ayListOnlyAcceptedRequest;
static ArrayList<String> ayListOnlyRequiredRequest;


static ArrayList<String> aylistOponnentTeamsChallenge;
static ArrayList<String> ayListScoreChallengerChallenge;
static ArrayList<String> ayListScoreOppnnentChallenge;

static ArrayList<Long> ayListTimeLeftRequestChallenger;
static ArrayList<Long> ayListTimeLeftRequestOponnent;
static ArrayList<Long> ayListTimeLeftChallenge;


private TextView txtToolbarTitle;
static TextView txtNo;

private Toolbar toolbar;


private RecyclerView recyclerViewRequests;
private RecyclerView.Adapter rvAdapterRequests;
private RecyclerView.LayoutManager rvLayoutManagerRequests;

private RecyclerView recyclerViewChallenges;
private RecyclerView.Adapter rvAdapterChallenges;
private RecyclerView.LayoutManager rvLayoutManagerChallenges;

private RvAdapterShowVsTeamChallenges rvAdapterShowVsTeamChallenges;


private SharedPrefs sharedPrefs;
private DatabaseWrite databaseWrite;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.show_vs_team);

    initialize();
    declare();
    navigation();
    getVS();
}

private void initialize() {

    txtNo = (TextView)findViewById(R.id.txtNo);

    toolbar = (Toolbar)findViewById(R.id.toolbar);
    txtToolbarTitle = (TextView)toolbar.findViewById(R.id.txtToolbarTitle);


    recyclerViewRequests = (RecyclerView)findViewById(R.id.recyclerShowVsTeamRequests);
    rvLayoutManagerRequests = new LinearLayoutManager(this);
    rvAdapterRequests = new RvAdapterShowVsTeamRequests(this);


    recyclerViewChallenges = (RecyclerView)findViewById(R.id.recyclerShowVsTeamChallenges);
    rvLayoutManagerChallenges  = new LinearLayoutManager(this);
    rvAdapterChallenges  = new RvAdapterShowVsTeamChallenges(this);

    rvAdapterShowVsTeamChallenges = new RvAdapterShowVsTeamChallenges(this);

    sharedPrefs = new SharedPrefs(this);


}

 private void declare() {

     recyclerViewRequests.setLayoutManager(rvLayoutManagerRequests);
     recyclerViewChallenges.setLayoutManager(rvLayoutManagerChallenges);

}


private void navigation() {

    txtToolbarTitle.setText("BeforeTeam");

    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
}


private void getVS()
{
    strMethod = "showVsTeamRequests";

    databaseWrite = new DatabaseWrite(this);
    databaseWrite.passTheClassShowVsTeam(this);
    databaseWrite.execute(strMethod,sharedPrefs.getTeamnameServer(this));


    strMethod = "showVsTeamChallenges";

    databaseWrite = new DatabaseWrite(this);
    databaseWrite.passTheClassShowVsTeam(this);
    databaseWrite.execute(strMethod,sharedPrefs.getTeamnameServer(this));
}


public void showVsTeamRequests()
{
    recyclerViewRequests.setAdapter(rvAdapterRequests);


}

public void showVsteamChallenges()
{
    recyclerViewChallenges.setAdapter(rvAdapterChallenges);
    rvAdapterShowVsTeamChallenges.startTimer();
}


@Override
public void onBackPressed() {
    super.onBackPressed();

    rvAdapterShowVsTeamChallenges.cancelTimer();
}

@Override
protected void onStop() {
    super.onStop();

    rvAdapterShowVsTeamChallenges.cancelTimer();
}

@Override
protected void onPause() {
    super.onPause();

    rvAdapterShowVsTeamChallenges.cancelTimer();
    }
}

Upvotes: 4

Views: 2766

Answers (5)

Amr
Amr

Reputation: 1322

As @Atakan Yildirim pointed out onViewRecycled() is called for sure when you make adapter = null for your recyclerview so your code will be something like this

override fun onStop() {
    super.onStop()
    currentItemPosition =
        (binding.recyclerView.layoutManager as? LinearLayoutManager)
            ?.findFirstVisibleItemPosition() ?: 0

    binding.recyclerView.adapter = null
}

override fun onStart() {
    super.onStart()
    binding.recyclerView.adapter = myCustomAdapter
    binding.recyclerView.scrollToPosition(currentItemPosition)
}

then make sure that you implement correctly onViewRecycled() to free the resources.

Upvotes: 0

Haris
Haris

Reputation: 73

set CountDownTimer in SparseArrayand cancel all timers in activity onDestroy method, please check this link it help me in my project

Cancel CountDownTimer in Recyclerview

Upvotes: 0

Atakan Yıldırım
Atakan Yıldırım

Reputation: 902

I was actually using the onViewDetachedFromWindow and onViewAttachedFromWindow functions. However, when the activity destroyed, I saw that the onViewRecycled function works one time. These codes worked for me perfectly:

Add onViewRecycled to your RecycleViewAdapter

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
    super.onViewRecycled(holder);
    //add cancel function
    if (holder.timer != null) {
        holder.timer.cancel();
    }
}

After that, add onDestroy to your Activity

@Override
protected void onDestroy() {
    super.onDestroy();
    //set adapter
    recylerView.setAdapter(null);
}

Upvotes: 5

Opiatefuchs
Opiatefuchs

Reputation: 9870

make a public method inside your adapter like:

public void finishTimer(){    
   if(countDownTimerTest!=null){    
    countDownTimerTest.cancel();      
    countDownTimer = null;
   }
}

and call it from your activity if you finish it:

    @Override
protected void onStop() {

 if(mYourRecyclerViewAdapter!=null){
      mYourRecyclerViewAdapter.finishTimer();

   }

super.onStop();
}

EDIT

instead of creating CountDownTimer in your viewholder, you should make it as a class like:

 private class CountDownTimerTest extends CountDownTimer {

        private CountDownTimerTest(long millis, long interval) {
            super(millis, interval);
        }

        @Override
        public void onTick(long millisUntilFinished) {

            for(int i=0; i<ShowVsTeam.ayListTimeLeftChallenge.size(); i++)
            {
                long lngUntilNow = ShowVsTeam.ayListTimeLeftChallenge.get(i);

                long lngNow = (lngUntilNow-(1000/ShowVsTeam.ayListTimeLeftChallenge.size()));

                ShowVsTeam.ayListTimeLeftChallenge.set(i, Long.valueOf(lngNow));
            }

            notifyDataSetChanged();

        }

        @Override
        public void onFinish() {


        }
    }

Then you can make a global object in your adapter:

private CountDownTimerTest countDownTimerTest;

and a public start method in your adapter if you need to start it outside:

public void startTimer(){

if(countDownTimerTest==null){
  countDownTimerTest= new CountDownTimerTest(1800000,1000);
  }

  countDownTimerTest.start();
}

If you still want to start it in your viewholder, just call startTimer() inside. If you want to start it in your activity call mYourAdapter.startTimer(); . Then you can stop it like suggested above.

From your comments I get that everytime you start the activity, a new timer is created. If you do it like this, usually the problem should go away. Otherwise, You can avoid creating a new instance of your activity by setting android:launchMode="singleTop"in the manifest into your activity tag. By doing this, Your activity is only created new if it is destroyed before.

Upvotes: 0

Raphau
Raphau

Reputation: 126

In your RvAdapterShowVsTeamChallenges class you have a member called "countDownTimerTest" but it can only hold reference to one object of CountDownTimer. I suppose that the issue you have is that you try to display more than one item in your RecyclerView and every time onCreateViewHolder() is called you overwrite the reference for "countDownTimerTest".

I think the best solution to prevent that is to use Map<Integer, CountDownTimer> timerMap = new HashMap<Integer, CountDownTimer>(); to store all your CountDownTimers. Then in your onBindViewHolder() just check if the map contains item for this position, if not - then create new countdowntimer

CountDownTimer timer = timerMap.get(position); 
if(timer == null){
timer = new CountDownTimer(1800000,1000) {
    @Override
    public void onTick(long millisUntilFinished) {

        for(int i=0; i<ShowVsTeam.ayListTimeLeftChallenge.size(); i++){
            long lngUntilNow = ShowVsTeam.ayListTimeLeftChallenge.get(i);

            long lngNow = (lngUntilNow-(1000/ShowVsTeam.ayListTimeLeftChallenge.size()));

            ShowVsTeam.ayListTimeLeftChallenge.set(i, Long.valueOf(lngNow));
        }

        notifyDataSetChanged();
    }

    @Override
    public void onFinish() {

    }
}
    timer.start();
    timerMap.put(position, timer);
}

once you finish your Activity just call a method which will do this:

for(CountDownTimer timer : timerMap.values()){
        timer.cancel();
}

Upvotes: 0

Related Questions