Android: Multiple timers in a ListView with handler and runnable. 2 Problems -
i'm creating app contains listview 2 columns. on first column countdown should displayed , on second column additional text, explains countdown for. below see code works ... more or less. have listview multiple rows timers ticking. 1 problem is: set.text() in runnable seems override rows. e.g. runnable row 1 sets text row 2 , 3, runnable row 2 sets text 1 , 3 , on. has effect first column blinks (with correct values , values of other rows). how can set text specific row in listview?
next problem: runnable running on , on if remove callbacks handler. when activity in background or closed timer ticking not needed , don't want waste system resources.
my activity:
public class timeractivity extends listactivity { mytimeradapter mytimeradapter = null; arraylist<long> timerlist = new arraylist<long>(); arraylist<string> textlist = new arraylist<string>(); @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.listactivity); mytimeradapter = new mytimeradapter(this, r.layout.row, r.id.tv_timer, r.id.tv_text); setlistadapter(mytimeradapter); } @override protected void onresume() { super.onresume(); refreshview(); } @override protected void onpause() { mytimeradapter.clear(); mytimeradapter.notifydatasetchanged(); super.onpause(); } private void refreshview() { mytimeradapter.clear(); timerlist.clear(); textlist.clear(); // code read database , fill // array timerlist long value (used displaying countdown) // , textlist additional text mytimeradapter.add(timerlist, textlist); mytimeradapter.notifydatasetchanged(); } }
my adapter:
public class mytimeradapter extends arrayadapter<string> { private activity mcontext; private arraylist<long> mtimer; private arraylist<string> mtext; private int mviewid; private int mviewidfieldtimer; private int mviewidfieldtext; private int listsize; private arraylist<handler> handlerlist = new arraylist<handler>(); private arraylist<timerrunnable> runlist = new arraylist<timerrunnable>(); public mytimeradapter(activity context, int textviewresid, int tv1, int tv2) { super(context, textviewresid); mcontext = context; mviewid = textviewresid; mviewidfieldtimer = tv1; mviewidfieldtext = tv2; listsize = 0; } public void add(arraylist<long> timer, arraylist<string> text) { mtimer = timer; mtext = text; listsize = mtext.size(); handlerlist.clear(); runlist.clear(); } @override public void clear() { super.clear(); int i; (i=0; i<listsize; i++) { handlerlist.get(i).removecallbacksandmessages(runlist.get(i)); runlist.get(i).stophandler(); } } @override public int getcount() { return listsize; } @override public view getview(int position, view convertview, viewgroup parent) { view v = convertview; if (v == null) { layoutinflater vi = mcontext.getlayoutinflater(); v = vi.inflate(mviewid, null); } long timerline = mtimer.get(position); if (timerline != 0) { textview tvtimer = (textview) v.findviewbyid(mviewidfieldtimer); if (tvtimer != null) { tvtimer.settag(position); final handler mtimerhandler = new handler(); timerrunnable timertask = new timerrunnable(tvtimer, tvtimer.gettag().tostring(), timerline); mtimerhandler.post(timertask); // save in array stop later handlerlist.add(mtimerhandler); runlist.add(timertask); } } string textline = mtext.get(position); if (textline != null) { textview tvtext = (textview) v.findviewbyid(mviewidfieldtext); if (tvtext != null) { tvtext.settext(textline); } } return v; } }
my runnable:
public class timerrunnable implements runnable { private textview tv; final handler mtimerhandler = new handler(); string tag; long endtime; long sec; public timerrunnable (textview tv, string tag, long endtime) { this.tv = tv; this.tag = tag; this.endtime = endtime; } public void run() { if (tv.gettag().tostring().equals(tag)) { calendar cal = calendar.getinstance(); sec = endtime - (cal.gettimeinmillis() / 1000); //endtime - aktuelle zeit if (sec >= 0) { // code formatting time in seconds hh:mm:ss (var string txt) tv.settext(txt); system.out.println(txt); // tests; see runnable still running mtimerhandler.postdelayed(this, 1000); } } } public void stophandler() { mtimerhandler.removecallbacksandmessages(null); } }
i follows:
- use timer (http://developer.android.com/reference/java/util/timer.html) , let run periodically every second or whatever.
- create timer object in activities oncreate() , cancel() timer in activities ondestroy() (or better start in onresume , cancel in onpause()). thats simplest way how avoid memory leaks , timer running if activity closed.
update listview running timer. there 2 options:
- simply call
adapter.notifydatasetchanged()
timer. lead "blinking" listview. try , checkout if work in application, since simplest implementation. update directly textviews visible in listview
private void updatetime(){ int firstvisibleitemindex = listview.getfirstvisibleposition(); (int = 0; < listview.getchildcount(); i++) { view v = listview.getchildat(i); youritem item = (youritem)adapter .getitem(firstvisibleitemindex + i)); viewholder vh = (viewholder) v.gettag(); // calculate time somehow, i.e. call methot on data item vh.tvtimer.settext(item.getelapsedtime()); } }
}
- simply call
so guess second option best. may wonder viewholder is. viewholder pattern increases performance of listview. http://developer.android.com/training/improving-layouts/smooth-scrolling.html#viewholder problem of adapter is, findviewbyid() called everytime user scrolls. findviewbyid() expensive method call, since has traverse view childs find 1 given id. in simple adapter may not have impact (since have single child view, textview). viewholder should use in adapter implementation
Comments
Post a Comment