Intereting Posts
Изменение навигации Просмотр цвета элемента Динамически Android Как правильно реализовать Parcelable с ArrayList <Parcelable>? Push-уведомление для приложений Android Не найдено ни одного вида для id … для фрагмента PlaceholderFragment Применение темы к панели поддержки поддержки v7 Как преобразовать файл: // uri в content: // uri? Регулярное уведомление не будет показывать кнопку закрытия X в android Рекомендации GC GC – когда выполняется GC, и можно ли отслеживать состояние запуска из кода? Изменение источника данных для воспроизведения звука с использованием существующего MediaPlayer? Уничтожение / жизненный цикл ContentProvider Анимация на вкладке Viewpager изменит fadein / fadeout как на экране ввода Linkedin Android Multipart Upload Вспышка внутри Android WebView – Как автоматически установить полноэкранный режим? Ant не удалось создать подписанный apk после обновления до android v20 Лучший способ быстро запустить тесты JUnit в Android-проекте в Android Studio

Android: несколько одновременных таймеров обратного отсчета в ListView

Я создаю приложение, которое требует ListView с неопределенным количеством элементов, каждый из которых имеет таймер, который отсчитывает от переменного числа. Я могу успешно сделать один из них обратным отсчетом, но я не могу понять, как включить таймер в каждый элемент ListView.

В настоящее время я использую CountDownTimer (не забудьте использовать D для копирования с сайта, они ошибаются).

Любой код или источники, указывающие мне в правильном направлении, очень ценятся.

Вот мой текущий класс EventAdapter, он устанавливает текст, отображаемый в элементе TextView элемента ListView. Что мне нужно сделать, так это то, что CountView обращается вниз каждую секунду. Поскольку каждый элемент ListView отображает что-то другое, я полагаю, мне нужен способ дифференцирования каждого элемента.

Я мог бы просто обновлять весь список каждую секунду, но есть и другие элементы, которые я не включил, например изображения, загруженные из Интернета, что было бы непрактично обновлять каждую секунду.

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; } } 

Solutions Collecting From Web of "Android: несколько одновременных таймеров обратного отсчета в ListView"

Пожалуйста, посмотрите здесь, в моем блоге, где вы найдете пример о том, как этого достичь.

Одним из решений является размещение TextView, который представляет каждый счетчик в HashMap вместе с его позицией в списке в качестве ключа.

В 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); } 

Затем вы можете обновлять счетчики с помощью обработчика, и вы отправляете 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); } } }; 

Это пример того, как я это делаю, и он отлично работает:

 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(); } } 

И это адаптер

 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(); } } в 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(); } } 

После проверки нескольких способов сделать это, это творческое решение, которое я написал. Просто и прекрасно работает.

Идея проверить, обновляет ли Runnable , обновляющий данные TextView и если TextView связан с другим представлением, Runnable остановится, и таким образом в фоновом режиме не будет дополнительного Thread поэтому не будет Мигание текста или утечка памяти.

1. Внутри вашего getView() добавить каждый тег TextView с его позицией.

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

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); } } }; 

Так что случается здесь, если TextView не соответствует исходному тегу Runnable который остановится.

3. Вернитесь к getView()

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

Вот и все, работа прекрасна!

Вот еще одно решение с помощью 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(); } } 

Затем инициализируйте созданный класс в своем адаптере:

 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; } } 

Вот мое решение (полный код) 20 независимый счетчик в ListView, который можно запускать, останавливать, перезапускать, перезагружать самостоятельно.

Я также столкнулся с этой проблемой несколько дней назад, но, наконец, я нашел решение проблемы. Я создал приложение, которое имеет 20 таймеров в представлении списка, которые могут работать независимо только одним CountDownTimer. Каждый таймер может запускать, останавливать, перезагружать, перезапускать самостоятельно. Каждый таймер установлен на 2 минуты. Вот мой полный код


** нажмите, чтобы увидеть скриншот приложения. **


Шаг 1: создайте приложение для Android с пустой активностью


Шаг 2. Мой файл макета MainActivity


 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/lv"/> </RelativeLayout> 

Шаг 3: создайте файл макета, который используется как элемент списка для вашего списка (имя файла res / layout / list_item.xml)


 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:padding="5dp" android:layout_margin="5dp" android:textDirection="firstStrong"> <Button android:layout_width="100dp" android:layout_height="wrap_content" android:text="RESET" android:id="@+id/resetBtn" android:layout_alignParentLeft="true" android:background="@color/colorAccent" android:layout_weight=".2" android:textColor="#ffffff" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tv" android:layout_toRightOf="@+id/resetBtn" android:text="Time : 02:00" android:textAlignment="center" android:textColor="#ffffff" android:layout_weight=".6" android:padding="@dimen/activity_vertical_margin" android:focusableInTouchMode="false" android:focusable="false" android:enabled="false" /> <Button android:layout_width="100dp" android:layout_height="wrap_content" android:textColor="#ffffff" android:id="@+id/startStopBtn" android:text="START" android:layout_weight=".2" android:background="@color/colorAccent" android:layout_alignParentRight="true" /> </LinearLayout> 

Шаг 4: создайте класс, в котором хранятся данные каждого таймера (My class name CounterInfo)

  package com.example.rahul.timerapp1; import java.util.ArrayList; import java.util.List; public class CounterInfo { public boolean status; public int time; public static List<CounterInfo> getCounterListInfo() { List<CounterInfo> l = new ArrayList<CounterInfo>(); for(int i=0; i<20;i++) { CounterInfo c = new CounterInfo(); c.status=false; c.time=-1; l.add(c); } return l; } } 

Шаг 5: создайте adaper для listView (мое имя адаптера MyAdapter.java)


 import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.TextView; import java.util.List; public class MyAdapter extends BaseAdapter { List<CounterInfo> counterInfo; LayoutInflater inflater; Context context; MyAdapter(MainActivity mainActivity , List<CounterInfo> c) { context=mainActivity; counterInfo=c; } @Override public int getCount() { return counterInfo.size(); } @Override public Object getItem(int position) { return (CounterInfo)counterInfo.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { if(convertView==null) { inflater =(LayoutInflater)context.getSystemService(context.LAYOUT_INFLATER_SERVICE); convertView=inflater.inflate(R.layout.list_item, parent ,false); } Button resetBtn =(Button)convertView.findViewById(R.id.resetBtn); resetBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { counterInfo.get(position).time=-1; counterInfo.get(position).status=false; notifyDataSetChanged(); } }); Button startStopBtn = (Button)convertView.findViewById(R.id.startStopBtn); startStopBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if ((counterInfo.get(position)).status) { (counterInfo.get(position)).status = false; } else { (counterInfo.get(position)).status = true; if(counterInfo.get(position).time==-1 || counterInfo.get(position).time==-2) (counterInfo.get(position)).time = 119; } notifyDataSetChanged(); } }); TextView title=(TextView) convertView.findViewById(R.id.tv); title.setText(getRemainingTime(((CounterInfo)counterInfo.get(position)).time)); if(getRemainingTime(counterInfo.get(position).time).equals("done!")) { counterInfo.get(position).status=false; counterInfo.get(position).time=-2; } if((counterInfo.get(position).status)) { startStopBtn.setText("STOP"); } else { if(counterInfo.get(position).time==-2) startStopBtn.setText("RESTART"); else { if(counterInfo.get(position).time==-1) startStopBtn.setText("START"); else startStopBtn.setText("RESTART"); } } return convertView; } public String getRemainingTime(int seconds) { String time; if(seconds==-1) { time="02:00"; } else { if (seconds >= 60) { time = "01"; time += ":" + (seconds - 60); } else { if (seconds==-2) { time="done!"; } else { time = "00:" + seconds; } } } if(time.length()==4) time=time.substring(0,3)+"0"+time.charAt(3); if(time.equals("00:00")) { time="done!"; } return time; } } 

Шаг 6: запустите приложение!