8

I want to show how much time is left inside each cell of my RecyclerView...for that I have used a countdown timer for each. In each row I start a counter and manage onTick()...all works as expected...I've got a timer tick for each row and my cell is also updating but my cell is flickering now....and it goes crazy when I scroll.

Here is my adapter...

if (product.getProductType().equalsIgnoreCase("Auction Product")) {
                isAuction=true;
                viewHolder.img_product_type.setImageResource(R.drawable.bid);
                viewHolder.txt_timeleft.setVisibility(View.VISIBLE);
               start_countDown(product.getStart(),product.getStop(),viewHolder.txt_timeleft);
            }

counter code is as below....

private void start_countDown(String start, String stop, final TextView txt_timeleft) {
        try {
            //Log.e("hetal",start+"....."+stop);
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            Calendar start_date = Calendar.getInstance();
            start_date.setTime(format.parse(start));

            Calendar end_date = Calendar.getInstance();
            end_date.setTime(format.parse(stop));

            final Calendar today = Calendar.getInstance();
            CountDownTimer timer;

            txt_timeleft.setTextColor(Color.DKGRAY);
            if(today.before(start_date)){
                txt_timeleft.setTextColor(context.getResources().getColor(R.color.red));
                txt_timeleft.setText(context.getString(R.string.auction_not_start));
                Animation anim = new AlphaAnimation(0.0f, 1.0f);
                anim.setDuration(1000); //You can manage the time of the blink with this parameter
                anim.setStartOffset(20);
                anim.setRepeatMode(Animation.REVERSE);
                anim.setRepeatCount(Animation.INFINITE);
                txt_timeleft.startAnimation(anim);
                return;
            }
            if (!today.before(end_date)) {

                txt_timeleft.setTextColor(context.getResources().getColor(R.color.red));
                txt_timeleft.setText(context.getString(R.string.time_out));
                        Animation anim = new AlphaAnimation(0.0f, 1.0f);
                        anim.setDuration(1000); //You can manage the time of the blink with this parameter
                        anim.setStartOffset(20);
                        anim.setRepeatMode(Animation.REVERSE);
                        anim.setRepeatCount(Animation.INFINITE);
                txt_timeleft.startAnimation(anim);
                return;
            }

            timer = new CountDownTimer(end_date.getTimeInMillis(), 1000) {
                @Override
                public void onTick(long millisUntilFinished) {

                    Calendar calendar = Calendar.getInstance();
                    calendar.setTimeInMillis(millisUntilFinished);

                    long diff = calendar.getTimeInMillis() - today.getTimeInMillis();

                    long seconds = diff / 1000 % 60;
                    long minutes = diff / (60 * 1000) % 60;
                    long hours = diff / (60 * 60 * 1000) % 24;
                    //long days = (int) diff / (24 * 60 * 60 * 1000);
                    long days = TimeUnit.MILLISECONDS.toDays(diff);


                    String left = "";
                    if (days > 0)
                        left += days + " " + context.getString(R.string.txt_day) + " ,";
                    if (hours > 0)
                        left += hours + " " + context.getString(R.string.txt_hour) + " ,";
                    if (minutes > 0)
                        left += minutes + " " + context.getString(R.string.txt_minute) + " ,";

                    left += seconds + " " + context.getString(R.string.txt_second);

                    final String finalLeft = left;


                            if (finalLeft.equals("0") || finalLeft.contains("-")) {
                                txt_timeleft.setText(context.getString(R.string.time_out));
                                txt_timeleft.setTextColor(context.getResources().getColor(R.color.red));
                                Animation anim = new AlphaAnimation(0.0f, 1.0f);
                                anim.setDuration(1000); //You can manage the time of the blink with this parameter
                                anim.setStartOffset(20);
                                anim.setRepeatMode(Animation.REVERSE);
                                anim.setRepeatCount(Animation.INFINITE);
                                txt_timeleft.startAnimation(anim);
                            } else
                                txt_timeleft.setText(finalLeft);
                }

                @Override
                public void onFinish() {

                }
            };
            timer.start();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
H Raval
  • 1,903
  • 17
  • 39
  • 1
    because when you scroll, it again call those methods and creates new objects of timer class. – Developine Mar 08 '16 at 07:02
  • @HammadTariqSahi any solution for it...because in each cell i have different time so i have posted code in onBindViewHolder – H Raval Mar 08 '16 at 07:03

2 Answers2

6

thanx Hammad Tariq Sahi i have used your logic and solve my problem in this way....i have also refereed this link

in my adapter

ArrayList<ViewHolder> viewHoldersList;
    private Handler handler = new Handler();
    private Runnable updateRemainingTimeRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (viewHoldersList) {
                for (ViewHolder holder : viewHoldersList) {
                    holder.updateTimeRemaining();
                }
            }
        }
    };

inside constructor of my adapter

viewHoldersList = new ArrayList<>();
startUpdateTimer();

and added this method to calculate time

private void startUpdateTimer() {
        Timer tmr = new Timer();
        tmr.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.post(updateRemainingTimeRunnable);
            }
        }, 1000, 1000);
    }

added two methods to my viewholder class

public void setData(Product product){
            this.product = product;
        }
        public void updateTimeRemaining() {
            if(product.getProductType().equalsIgnoreCase("Auction Product")) {
                Log.e("hetal",product.getProductType());
                try {
                    String start = product.getStart();
                    String stop = product.getStop();

                    //txt_timeleft.setText("");
                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                    Calendar start_date = Calendar.getInstance();
                    start_date.setTime(format.parse(start));

                    Calendar end_date = Calendar.getInstance();
                    end_date.setTime(format.parse(stop));

                    final Calendar today = Calendar.getInstance();
                    CountDownTimer timer;

                    long timeDiff = end_date.getTimeInMillis() - today.getTimeInMillis();
                    if (timeDiff > 0) {
                        long seconds = timeDiff / 1000 % 60;
                        long minutes = timeDiff / (60 * 1000) % 60;
                        long hours = timeDiff / (60 * 60 * 1000) % 24;
                        //long days = (int) diff / (24 * 60 * 60 * 1000);
                        long days = TimeUnit.MILLISECONDS.toDays(timeDiff);


                        String left = "";
                        if (days > 0)
                            left += days + " " + context.getString(R.string.txt_day) + " ,";
                        if (hours > 0)
                            left += hours + " " + context.getString(R.string.txt_hour) + " ,";
                        if (minutes > 0)
                            left += minutes + " " + context.getString(R.string.txt_minute) + " ,";

                        left += seconds + " " + context.getString(R.string.txt_second);

                        final String finalLeft = left;
                        txt_timeleft.setText(finalLeft);
                    } else {
                        txt_timeleft.setText("Time Out !!");
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }

and finally inside onBindViewHolder

synchronized (viewHoldersList) {
                viewHolder.setData(product);
                if(viewHoldersList.size()< (list.size()-2)) viewHoldersList.add(viewHolder);
            }

works perfect....thanx all

Community
  • 1
  • 1
H Raval
  • 1,903
  • 17
  • 39
  • Can you explain why in your synchronized method you add the viewholder to the viewHoldersList just if (viewHoldersList.size()< (list.size()-2)) ? – Sandra Apr 26 '16 at 07:35
  • I am having troubles still, the text is not shown when I call updateTimeRemaining, like it is using an old view holder instance even though I call viewHoldersList.add(viewHolder) in onBindViewHolder – Sandra Apr 26 '16 at 08:53
  • @Sandra because i am using header and footer in my recyclerview – H Raval Jul 01 '16 at 08:14
  • I assumed the reason is something of that kind, but thank you for your reply anyway. – Sandra Jul 11 '16 at 13:45
  • @HRaval, I got the same requirement to display timer in RecyclerView. The above solution provided by you isn't working for me. I would like to get your help. – Rakesh May 11 '17 at 11:39
  • I am trying the same thing. but I am getting same value in every cell of recyclerview. can't figure it out whats causing the problem. also, its returning the last index as well. – Arpit todewale Mar 21 '18 at 12:51
  • Won't this continue running while the app is in the background? Wasting battery? – behelit May 30 '18 at 03:25
  • I used this way but have big problem :https://stackoverflow.com/questions/59539326/android-recyclerview-countdowntimer-problem-after-delete-and-insert-new-item please help me thx . – zobydeh karimi Jan 01 '20 at 05:21
4

You need to add count down timer in your onCreateViewHolder method of RecyclerView because onCreateViewHolder will be called only once.

onBindViewHolder will be called every time user scroll up or down

please check below code.

public class UsageDetailsAdapter extends RecyclerView.Adapter<UsageDetailsAdapter.ViewHolder> {

Context mContext;
int position;
DecoView decoView;

public UsageDetailsAdapter(Context context) {

    this.mContext = context;
}

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

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_usage_details, parent, false);
    decoView = (DecoView) view.findViewById(R.id.dynamicArcView);


    Toast.makeText(mContext,""+ position, Toast.LENGTH_SHORT).show();


    position++;
    return new ViewHolder(view);


}

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

    /*
    * onBindViewHolder will be called every time user scroll up or down
    *
    * */


}

@Override
public int getItemCount() {
    return 10;
}

public class ViewHolder extends RecyclerView.ViewHolder {

    public ViewHolder(View itemView) {
        super(itemView);
        decoView = (DecoView) itemView.findViewById(R.id.dynamicArcView);
        decoView.setBackgroundColor(mContext.getResources().getColor(R.color.textColorPrimary));
        /*
        * View holder will be called once, but if user scroll before completing arc view
        * animation will not be completed.
        *
        * */
    }
}

}

Developine
  • 12,483
  • 8
  • 38
  • 42
  • initialize a variable named position in your constructor, make it global and increment it at the end of onCreateViewHolder, so you can handle each cell data, and can get data position wise. – Developine Mar 08 '16 at 07:04
  • I have done exactly same in one of my project, let me check and give you code. – Developine Mar 08 '16 at 07:13
  • ok i have tried your code with some modification...now scrolling problem solved but my whole recyclerview flickering as i used notifyDatasetchanged(position) to notify – H Raval Mar 08 '16 at 07:36
  • also when you call notifyDataSetChanged() cancel any ongoing threads, and start a new. – Developine Mar 08 '16 at 09:11
  • very obvious yet clever workaround ! brilliant man ! :) saved a lot of time – Adeel Ahmad Sep 08 '16 at 06:27
  • Please help me sir in this link i have big problem and anyone no help me : https://stackoverflow.com/questions/59539326/android-recyclerview-countdowntimer-problem-after-delete-and-insert-new-item – zobydeh karimi Jan 01 '20 at 05:23