Recyclerview with multiple countdown timers causes flickering
Asked Answered
R

2

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();
        }
    }
Reichstag answered 8/3, 2016 at 6:45 Comment(2)
because when you scroll, it again call those methods and creates new objects of timer class.Canady
@HammadTariqSahi any solution for it...because in each cell i have different time so i have posted code in onBindViewHolderReichstag
R
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

Reichstag answered 8/3, 2016 at 9:44 Comment(8)
Can you explain why in your synchronized method you add the viewholder to the viewHoldersList just if (viewHoldersList.size()< (list.size()-2)) ?Betrothal
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 onBindViewHolderBetrothal
@Betrothal because i am using header and footer in my recyclerviewReichstag
I assumed the reason is something of that kind, but thank you for your reply anyway.Betrothal
@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.Parsec
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.Gamez
Won't this continue running while the app is in the background? Wasting battery?Enough
I used this way but have big problem :#59539826 please help me thx .Jenicejeniece
C
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.
        *
        * */
    }
}

}

Canady answered 8/3, 2016 at 7:0 Comment(6)
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.Canady
I have done exactly same in one of my project, let me check and give you code.Canady
ok i have tried your code with some modification...now scrolling problem solved but my whole recyclerview flickering as i used notifyDatasetchanged(position) to notifyReichstag
also when you call notifyDataSetChanged() cancel any ongoing threads, and start a new.Canady
very obvious yet clever workaround ! brilliant man ! :) saved a lot of timeHampson
Please help me sir in this link i have big problem and anyone no help me : #59539826Jenicejeniece

© 2022 - 2024 — McMap. All rights reserved.