Android: Multiple simultaneous count-down timers in a ListView

前端 未结 4 604
说谎
说谎 2020-12-30 08:12

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 succes

相关标签:
4条回答
  • 2020-12-30 08:21

    Here is another solution of using ListView with multiple CountDownTimer. Firstly, we create a class MyCustomTimer that holds the CountDownTimer:

    public class MyCustomTimer{
        public MyCustomTimer() {
        }
        public void setTimer(TextView tv, long time) {
            new CountDownTimer(time, 1000) {
                public void onTick(long millisUntilFinished) {
                    //Set formatted date to your TextView
                    tv.setText(millisUntilFinished);
                }
                public void onFinish() {
                    tv.setText("Done!");
                }
            }.start();
        }
    }
    

    Then, initilize the created class in your adapter:

    public class MyAdapter extends BaseAdapter {
        private LayoutInflater mInflater;
        private MyCustomTimer myTimer;
        private ArrayList<Item> myItems;
    
        public MyAdapter(Context context, ArrayList<Item> data) {
            mInflater = LayoutInflater.from(context);
            myTimer= new MyCustomTimer();
            myItems = data;
        }
    
        //... implementation of other methods
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = mInflater.inflate(R.layout.listview_row, null);
            TextView tvTimer = (TextView) convertView.findViewById(R.id.textview_timer);
            TextView tvName = (TextView) convertView.findViewById(R.id.textview_name);
    
            Item item = data.get(position);
    
            tvName.setText(item.getName());
            myTimer.setTimer(tvTimer, item.getTime());
    
            return convertView;
        }
    }
    
    0 讨论(0)
  • 2020-12-30 08:24

    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);
            }
        }
    };
    
    0 讨论(0)
  • 2020-12-30 08:25

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-30 08:38

    after checking few ways to do that this is a creative solution i wrote .its simple and works perfectly .

    the idea it to check if the Runnable that updates the data is updating the same TextView and if the TextView is related to different view the Runnablewill stop and by this way there will be no extra Thread's in the background so there will be no blinking text's or memory leak.

    1 . inside your getView() add each TextView tag with his position .

    text = (TextView) view
            .findViewById(R.id.dimrix);
    text.setTag(position);
    

    2 . create class that implements Runnable so we can pass parameters .

    public class mUpdateClockTask implements Runnable {
        private TextView tv;
        final Handler mClockHandler = new Handler();
        String tag;
    
        public mUpdateClockTask(TextView tv,
                String tag) {
            this.tv = tv;
            this.tag = tag;
        }
    
        public void run() {
    
            if (tv.getTag().toString().equals(tag)) {
                            // do what ever you want to happen every second
                mClockHandler.postDelayed(this, 1000);
            }
    
        }
    
    };
    

    so what happen here is unless the TextView is not equals to the original tag the Runnable will stop .

    3 . go back to your getView()

            final Handler mClockHandler = new Handler();
            mUpdateClockTask clockTask = new mUpdateClockTask(text,
                    activeDraw, text.getTag().toString());
            mClockHandler.post(clockTask);
    

    that's it , work's perfect !

    0 讨论(0)
提交回复
热议问题