java Android:ListView 中的多个同时倒计时计时器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6346075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android: Multiple simultaneous count-down timers in a ListView
提问by Kevin Cooper
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 oneof them count down, but I can't figure out how to include a timer in each element of the ListView.
我正在创建一个应用程序,它需要一个元素数量不确定的 ListView,每个元素都有一个从可变数字开始倒计时的计时器。我能够成功地让其中一个倒计时,但我无法弄清楚如何在 ListView 的每个元素中包含一个计时器。
I am currently using a CountDownTimer(make sure to capitalize the D if copying from the website, they have it wrong).
我目前正在使用CountDownTimer(如果从网站复制,请确保大写 D,他们错了)。
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.
这是我当前的 EventAdapter 类,它设置在每个 ListView 元素的 TextView 中显示的文本。我需要做的是让 TextView 每秒倒计时。由于 ListView 的每个元素都显示不同的内容,我想我需要一种区分每个元素的方法。
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;
}
}
采纳答案by rochdev
Please have a look here at my blogwhere 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.
一种解决方案是将表示每个计数器的 TextView 连同它在列表中的位置一起放入一个 HashMap 中作为键。
In getView()
在 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.
然后,您可以通过使用 Handler 以及在何处发布可运行文件来更新计数器。
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);
}
}
};
回答by Pablo Moncunill
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();
}
}
回答by Jesus Dimrix
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 Runnable
will 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.
检查Runnable
更新数据是否更新相同TextView
以及是否TextView
与不同视图相关的想法Runnable
将停止,通过这种方式Thread
在后台不会有额外的,因此不会有闪烁的文本或内存泄漏.
1 . inside your getView()
add each TextView tag with his position .
1 . 在您getView()
添加每个 TextView 标签和他的位置。
text = (TextView) view
.findViewById(R.id.dimrix);
text.setTag(position);
2 . create class that implements Runnable so we can pass parameters .
2 . 创建实现 Runnable 的类,以便我们可以传递参数。
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 .
所以这里发生的是,除非TextView
不等于原始标签,Runnable
否则将停止。
3 . go back to your getView()
3 . 回到你的getView()
final Handler mClockHandler = new Handler();
mUpdateClockTask clockTask = new mUpdateClockTask(text,
activeDraw, text.getTag().toString());
mClockHandler.post(clockTask);
that's it , work's perfect !
就是这样,工作完美!
回答by Bakyt
Here is another solution of using ListView with multiple CountDownTimer. Firstly, we create a class MyCustomTimerthat holds the CountDownTimer:
这是将 ListView 与多个 CountDownTimer 一起使用的另一种解决方案。首先,我们创建一个类MyCustomTimer来保存 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;
}
}