简体   繁体   English

在 Recyclerview 中取消 CountDownTimer

[英]Cancel CountDownTimer in Recyclerview

I have an Recyclerview with countDownTimers wich shows the time left to play in Days,Hours,Minutes and Seconds.我有一个带有 countDownTimers 的 Recyclerview,它显示了以天、小时、分钟和秒为单位的剩余时间。 Now I have the problem, that when i leave the activity, the countDownTimers still running.现在我遇到了问题,当我离开活动时,countDownTimers 仍在运行。 Here is my Adapter:这是我的适配器:

ublic 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 countDownTimer(s) is still running.我试图在我的 Activity 中取消 CountDownTimer onStop() 但我得到一个 Nullpointer,尽管 countDownTimer(s) 仍在运行。

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();
    }
}

I was actually using the onViewDetachedFromWindow and onViewAttachedFromWindow functions.我实际上是在使用 onViewDetachedFromWindow 和 onViewAttachedFromWindow 函数。 However, when the activity destroyed, I saw that the onViewRecycled function works one time.但是,当活动销毁时,我看到 onViewRecycled 函数工作了一次。 These codes worked for me perfectly:这些代码非常适合我:

Add onViewRecycled to your RecycleViewAdapteronViewRecycled添加到您的 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之后,将onDestroy添加到您的活动中

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

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:而不是在您的查看器中创建 CountDownTimer,您应该将其作为一个类:

 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.如果你仍然想在你的 viewholder 中启动它,只需在里面调用startTimer() If you want to start it in your activity call mYourAdapter.startTimer();如果你想在你的活动中启动它,请调用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.否则,您可以通过将清单中的android:launchMode="singleTop"设置为您的活动标签来避免创建您的活动的新实例。 By doing this, Your activity is only created new if it is destroyed before.通过这样做,您的活动只有在之前被销毁时才会被创建为新的。

In your RvAdapterShowVsTeamChallenges class you have a member called "countDownTimerTest" but it can only hold reference to one object of CountDownTimer.在您的 RvAdapterShowVsTeamChallenges 类中,您有一个名为“countDownTimerTest”的成员,但它只能保存对 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".我想您遇到的问题是您尝试在 RecyclerView 中显示多个项目,并且每次调用 onCreateViewHolder() 时都会覆盖对“countDownTimerTest”的引用。

I think the best solution to prevent that is to use Map<Integer, CountDownTimer> timerMap = new HashMap<Integer, CountDownTimer>();我认为防止这种情况的最佳解决方案是使用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然后在您的 onBindViewHolder() 中检查地图是否包含该位置的项目,如果没有 - 然后创建新的倒计时

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:完成 Activity 后,只需调用一个方法即可:

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

set CountDownTimer in SparseArray and cancel all timers in activity onDestroy method, please check this link it help me in my projectSparseArray设置CountDownTimer并取消活动onDestroy方法中的所有计时器,请查看此链接,它对我的​​项目有帮助

Cancel CountDownTimer in Recyclerview 在 Recyclerview 中取消 CountDownTimer

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正如@Atakan Yildirim 指出的那样,当您为recyclerview adapter = null时,肯定会调用onViewRecycled()因此您的代码将是这样的

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.然后确保您正确实施onViewRecycled()以释放资源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM