Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Multiple simultaneous count-down timers in a ListView

I am creating an app that requires a ListView with an undetermined number of elements, each of which has a timer that counts down from a variable number. I am able to successfully make one of them count down, but I can't figure out how to include a timer in each element of the ListView.

I am currently using a CountDownTimer (make sure to capitalize the D if copying from the website, they have it wrong).

Any code or sources to point me in the right direction are much appreciated.

Here is my current EventAdapter class, it sets the text displayed in each ListView element's TextView. What I need to do is make the TextView count down every second. Since each element of the ListView is displaying something different, I suppose I need a way of differentiating each element.

I could just update the whole list every second, but there are other elements I have not included such as images loaded from the internet that it would be impractical to refresh every second.

private class EventAdapter extends ArrayAdapter<Event>
{
    private ArrayList<Event> items;

    public EventAdapter(Context context, int textViewResourceId, ArrayList<Event> items) {
        super(context, textViewResourceId, items);
        this.items = items;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.row, null);
        }

        Event e = items.get(position);

        if (e != null) {
            TextView tv = (TextView) v.findViewById(R.id.text);

            if (tv != null)
                tv.setText(e.getName());
        }
        return v;
    }
}
like image 574
Kevin Cooper Avatar asked Jun 14 '11 15:06

Kevin Cooper


2 Answers

This is an example of the way I do it and it works perfect:

public class TestCounterActivity extends ListActivity
{
    TestAdapter adapter;

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

        // Example values
        ArrayList<Date> values = new ArrayList<Date>();
        values.add(new Date(1482464366239L));
        values.add(new Date(1480464366239L));
        values.add(new Date(1470464366239L));
        values.add(new Date(1460464366239L));
        values.add(new Date(1450464366239L));
        values.add(new Date(1440464366239L));
        values.add(new Date(1430464366239L));
        values.add(new Date(1420464366239L));
        values.add(new Date(1410464366239L));
        values.add(new Date(1490464366239L));

        adapter = new TestAdapter(this, values);

        setListAdapter(adapter);
    }

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

        // Dont forget to cancel the running timers
        adapter.cancelAllTimers();
    }
}

And this is the adapter

public class TestAdapter extends ArrayAdapter<Date> 
{
    private final Activity context;
    private final List<Date> values;
    private HashMap<TextView,CountDownTimer> counters;

    static class TestViewHolder 
    {
        public TextView tvCounter;
    }

    public TestAdapter(Activity context, List<Date> values) 
    {
        super(context, R.layout.test_row, values);
        this.context = context;
        this.values = values;
        this.counters = new HashMap<TextView, CountDownTimer>();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
    {
        View rowView = convertView;

        if(rowView == null)
        {
            LayoutInflater inflater = context.getLayoutInflater();
            rowView = inflater.inflate(R.layout.test_row, null);
            final TestViewHolder viewHolder = new TestViewHolder();
            viewHolder.tvCounter = (TextView) rowView.findViewById(R.id.tvCounter);

            rowView.setTag(viewHolder);
        }

        TestViewHolder holder = (TestViewHolder) rowView.getTag();
        final TextView tv = holder.tvCounter;

        CountDownTimer cdt = counters.get(holder.tvCounter);
        if(cdt!=null)
        {
            cdt.cancel();
            cdt=null;
        }

        Date date = values.get(position);
        long currentDate = Calendar.getInstance().getTime().getTime();
        long limitDate = date.getTime();
        long difference = limitDate - currentDate;

        cdt = new CountDownTimer(difference, 1000)
        {
            @Override
            public void onTick(long millisUntilFinished) 
            {
                int days = 0;
                int hours = 0;
                int minutes = 0;
                int seconds = 0;
                String sDate = "";

                if(millisUntilFinished > DateUtils.DAY_IN_MILLIS)
                {
                    days = (int) (millisUntilFinished / DateUtils.DAY_IN_MILLIS);
                    sDate += days+"d";
                }

                millisUntilFinished -= (days*DateUtils.DAY_IN_MILLIS);

                if(millisUntilFinished > DateUtils.HOUR_IN_MILLIS)
                {
                    hours = (int) (millisUntilFinished / DateUtils.HOUR_IN_MILLIS);
                }

                millisUntilFinished -= (hours*DateUtils.HOUR_IN_MILLIS);

                if(millisUntilFinished > DateUtils.MINUTE_IN_MILLIS)
                {
                    minutes = (int) (millisUntilFinished / DateUtils.MINUTE_IN_MILLIS);
                }

                millisUntilFinished -= (minutes*DateUtils.MINUTE_IN_MILLIS);

                if(millisUntilFinished > DateUtils.SECOND_IN_MILLIS)
                {
                    seconds = (int) (millisUntilFinished / DateUtils.SECOND_IN_MILLIS);
                }

                sDate += " "+String.format("%02d",hours)+":"+String.format("%02d",minutes)+":"+String.format("%02d",seconds);
                tv.setText(sDate.trim());
            }

            @Override
            public void onFinish() {
                tv.setText("Finished");
            }
        };

        counters.put(tv, cdt);
        cdt.start();

        return rowView;
    }

    public void cancelAllTimers()
    {
        Set<Entry<TextView, CountDownTimer>> s = counters.entrySet();
        Iterator it = s.iterator();
        while(it.hasNext())
        {
            try
            {
                Map.Entry pairs = (Map.Entry)it.next();
                CountDownTimer cdt = (CountDownTimer)pairs.getValue();

                cdt.cancel();
                cdt = null;
            }
            catch(Exception e){}
        }

        it=null;
        s=null;
        counters.clear();
    }
}
like image 142
Pablo Moncunill Avatar answered Nov 15 '22 13:11

Pablo Moncunill


Please have a look here at my blog where you will find an example on how to achieve this.

One solution is to put the TextView that represents each counter into a HashMap together with it's position in the list as the key.

In getView()

TextView counter = (TextView) v.findViewById(R.id.myTextViewTwo);
if (counter != null) {
    counter.setText(myData.getCountAsString());
    // add the TextView for the counter to the HashMap.
    mCounterList.put(position, counter);
}

Then you can update the counters by using a Handler and where you post a runnable.

private final Runnable mRunnable = new Runnable() {
    public void run() {
        MyData myData;
        TextView textView;

        // if counters are active
        if (mCountersActive) {                
            if (mCounterList != null && mDataList != null) {
                for (int i=0; i < mDataList.size(); i++) {
                    myData = mDataList.get(i);
                    textView = mCounterList.get(i);
                    if (textView != null) {
                        if (myData.getCount() >= 0) {
                            textView.setText(myData.getCountAsString());
                            myData.reduceCount();
                        }
                    }
                }
            }
            // update every second
            mHandler.postDelayed(this, 1000);
        }
    }
};
like image 40
rochdev Avatar answered Nov 15 '22 13:11

rochdev