如何处理 ListView 中的多个倒数计时器?
Posted
技术标签:
【中文标题】如何处理 ListView 中的多个倒数计时器?【英文标题】:How to handle multiple countdown timers in ListView? 【发布时间】:2015-09-12 14:03:08 【问题描述】:我有一个列表视图(带有自定义列表适配器),我需要在每一行上显示倒计时。
例如,如果我的列表包含 4 个项目,我将有 4 行。 此时,我需要处理 4 个不同的倒计时(每行一个),因为时间不同。
到目前为止,我正在通过以下方式处理它:在自定义列表适配器中,在 getView() 方法中,我创建了一个新的 CountDownTimer 并在 TextView 中显示剩余时间。
但问题是它大大减慢了活动,在最坏的情况下我什至无法正确滚动(因为每次显示一行时,它都会创建一个新的 CountDownTimer)。
我搜索了很多更好的解决方案,但没有一个令人满意。
是否有更清洁、更流畅的解决方案来处理 listView 中的多个倒计时?
谢谢
【问题讨论】:
【参考方案1】:我们的想法是更新可见项目的剩余时间,而不是尝试显示所有人的剩余时间。
请按照以下示例代码告诉我:
主要活动:
public class MainActivity extends Activity
private ListView lvItems;
private List<Product> lstProducts;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvItems = (ListView) findViewById(R.id.lvItems);
lstProducts = new ArrayList<>();
lstProducts.add(new Product("A", System.currentTimeMillis() + 10000));
lstProducts.add(new Product("B", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("C", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("D", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("E", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("F", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("G", System.currentTimeMillis() + 30000));
lstProducts.add(new Product("H", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("I", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("J", System.currentTimeMillis() + 40000));
lstProducts.add(new Product("K", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("L", System.currentTimeMillis() + 50000));
lstProducts.add(new Product("M", System.currentTimeMillis() + 60000));
lstProducts.add(new Product("N", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("O", System.currentTimeMillis() + 10000));
lvItems.setAdapter(new CountdownAdapter(MainActivity.this, lstProducts));
private class Product
String name;
long expirationTime;
public Product(String name, long expirationTime)
this.name = name;
this.expirationTime = expirationTime;
public class CountdownAdapter extends ArrayAdapter<Product>
private LayoutInflater lf;
private List<ViewHolder> lstHolders;
private Handler mHandler = new Handler();
private Runnable updateRemainingTimeRunnable = new Runnable()
@Override
public void run()
synchronized (lstHolders)
long currentTime = System.currentTimeMillis();
for (ViewHolder holder : lstHolders)
holder.updateTimeRemaining(currentTime);
;
public CountdownAdapter(Context context, List<Product> objects)
super(context, 0, objects);
lf = LayoutInflater.from(context);
lstHolders = new ArrayList<>();
startUpdateTimer();
private void startUpdateTimer()
Timer tmr = new Timer();
tmr.schedule(new TimerTask()
@Override
public void run()
mHandler.post(updateRemainingTimeRunnable);
, 1000, 1000);
@Override
public View getView(int position, View convertView, ViewGroup parent)
ViewHolder holder = null;
if (convertView == null)
holder = new ViewHolder();
convertView = lf.inflate(R.layout.list_item, parent, false);
holder.tvProduct = (TextView) convertView.findViewById(R.id.tvProduct);
holder.tvTimeRemaining = (TextView) convertView.findViewById(R.id.tvTimeRemaining);
convertView.setTag(holder);
synchronized (lstHolders)
lstHolders.add(holder);
else
holder = (ViewHolder) convertView.getTag();
holder.setData(getItem(position));
return convertView;
private class ViewHolder
TextView tvProduct;
TextView tvTimeRemaining;
Product mProduct;
public void setData(Product item)
mProduct = item;
tvProduct.setText(item.name);
updateTimeRemaining(System.currentTimeMillis());
public void updateTimeRemaining(long currentTime)
long timeDiff = mProduct.expirationTime - currentTime;
if (timeDiff > 0)
int seconds = (int) (timeDiff / 1000) % 60;
int minutes = (int) ((timeDiff / (1000 * 60)) % 60);
int hours = (int) ((timeDiff / (1000 * 60 * 60)) % 24);
tvTimeRemaining.setText(hours + " hrs " + minutes + " mins " + seconds + " sec");
else
tvTimeRemaining.setText("Expired!!");
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<ListView
android:id="@+id/lvItems"
android:layout_
android:layout_ />
</RelativeLayout>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/tvProduct"
android:layout_
android:layout_
android:layout_margin="10dp"
android:text="Product Name"
android:textSize="16dp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvTimeRemaining"
android:layout_
android:layout_
android:layout_margin="10dp"
android:text="Time Remaining : " />
</LinearLayout>
【讨论】:
任何人都可以用 recyclerview 做到这一点,我已经工作了 2 天,请帮忙!! @AhmadAlkhateeb 你有解决办法吗? 我也在做同样的事情。你能把回收站视图的代码发给我吗? @AhmadAlkhateeb 你有没有为 recyclerview 找到任何解决方案 谢谢我用你的逻辑找到了解决方案【参考方案2】:可能已经晚了,但这是使用 php 和 json 的 recyclerView 版本,基于 @Eldhose M Babu 的出色回答。希望对您有所帮助:)
Adapter.java
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder>
private Context context;
private final List<ViewHolder> lstHolders;
public List<Model> lst;
private Handler mHandler = new Handler();
private Runnable updateRemainingTimeRunnable = new Runnable()
@Override
public void run()
synchronized (lstHolders)
long currentTime = System.currentTimeMillis();
for (ViewHolder holder : lstHolders)
holder.updateTimeRemaining(currentTime);
;
public Adapter(List<Model> lst, Context context)
super();
this.lst = lst;
this.context = context;
lstHolders = new ArrayList<>();
startUpdateTimer();
private void startUpdateTimer()
Timer tmr = new Timer();
tmr.schedule(new TimerTask()
@Override
public void run()
mHandler.post(updateRemainingTimeRunnable);
, 1000, 1000);
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
@Override
public void onBindViewHolder(ViewHolder holder, int position)
holder.setData(lst.get(position));
synchronized (lstHolders)
lstHolders.add(holder);
holder.updateTimeRemaining(System.currentTimeMillis());
@Override
public int getItemCount()
return lst.size();
class ViewHolder extends RecyclerView.ViewHolder
public TextView textViewName;
public TextView tvTimeRemaining;
Model mModel;
public void setData(Model item)
mModel = item;
textViewName.setText(item.name);
updateTimeRemaining(System.currentTimeMillis());
public void updateTimeRemaining(long currentTime)
long timeDiff = mModel.expirationTime - currentTime;
if (timeDiff > 0)
int seconds = (int) (timeDiff / 1000) % 60;
int minutes = (int) ((timeDiff / (1000 * 60)) % 60);
int hours = (int) ((timeDiff / (1000 * 60 * 60)) % 24);
tvTimeRemaining.setText(hours + " hrs " + minutes + " mins " + seconds + " sec");
else
tvTimeRemaining.setText("Expired!!");
public ViewHolder(View itemView)
super(itemView);
tvTimeRemaining = (TextView) itemView.findViewById(R.id.cd);
textViewName = (TextView) itemView.findViewById(R.id.textViewName);
MainActivity.java
public class MainActivity extends AppCompatActivity
private List<Model> lst;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private RecyclerView.Adapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
lst = new ArrayList<>();
getData();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show();
);
private void getData()
final ProgressDialog loading = ProgressDialog.show(this,"Loading Data", "Please wait...",false,false);
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest("http://192.168.200.102/api.php",
new Response.Listener<JSONArray>()
@Override
public void onResponse(JSONArray response)
loading.dismiss();
parseData(response);
,
new Response.ErrorListener()
@Override
public void onErrorResponse(VolleyError error)
);
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(jsonArrayRequest);
private void parseData(JSONArray array)
for(int i = 0; i<array.length(); i++)
Model model = new Model();
JSONObject json = null;
try
json = array.getJSONObject(i);
model.setexpirationTime(json.getLong("expirationTime"));
model.setName(json.getString("name"));
catch (JSONException e)
e.printStackTrace();
lst.add(model);
adapter = new Adapter(lst, this);
recyclerView.setAdapter(adapter);
@Override
public boolean onCreateOptionsMenu(Menu menu)
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
int id = item.getItemId();
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
Model.java
public class Model
public String name;
public long expirationTime;
public long getexpirationTime()
return expirationTime;
public void setexpirationTime(long expire)
this.expirationTime = expire;
public String getName()
return name;
public void setName(String name)
this.name = name;
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:fitsSystemWindows="true"
tools:context="here.math.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_
android:layout_
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:layout_
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="here.math.MainActivity"
tools:showIn="@layout/activity_main">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_
android:layout_>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
list.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<android.support.v7.widget.CardView
android:layout_
android:layout_
android:layout_marginBottom="20dp">
<LinearLayout
android:padding="@dimen/activity_horizontal_margin"
android:orientation="vertical"
android:layout_
android:layout_>
<TableLayout
android:layout_
android:layout_>
<TableRow>
<TextView
android:text="Expire time"
android:paddingRight="10dp"
android:layout_
android:layout_ />
<TextView
android:id="@+id/cd"
android:textStyle="bold"
android:layout_
android:layout_ />
</TableRow>
<TableRow>
<TextView
android:text="Name"
android:paddingRight="10dp"
android:layout_
android:layout_ />
<TextView
android:id="@+id/textViewName"
android:textStyle="bold"
android:layout_
android:layout_ />
</TableRow>
</TableLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
对于 url 部分,您可以执行以下操作:
<?php
$arr = array(
array(
'name' => "Android",
'expirationTime' => 1456860322 * 1000, // timestamp multiply for display in milliseconds
),
array(
'name' => "Android 2",
'expirationTime' => 1456900522 * 1000,
),
array(
'name' => "Android 3",
'expirationTime' => 1459509819 * 1000,
),
array(
'name' => "Android 4",
'expirationTime' => 1457021950 * 1000,
),
);
echo json_encode($arr);
【讨论】:
我如何才能只为 2 行做到这一点 @andro,根据我发布的 php 脚本,删除最后两个数组。如果您遇到问题,请告诉我。 在您的示例中,问题是同步的 (lstHolders) lstHolders.add(holder);您总是将持有人添加到列表中。所以说如果当我向上滑动时 der 是 10 行 lstHolders 的大小会增加它应该是最大 10 我的问题陈述是我有时间从服务器现在我必须更新但说我有 50 行但我必须只更新第 10 行的时间 很好的解决方案,你肯定从循环中拯救了马头 :) :) @HamedOkhovvat【参考方案3】:请查看此Countdown Timer in listview android 示例。
自定义适配器
import android.os.CountDownTimer;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder>
private ArrayList<String> al_data;
public class MyViewHolder extends RecyclerView.ViewHolder
public TextView tv_timer;
CountDownTimer timer;
public MyViewHolder (View view)
super(view);
tv_timer = (TextView)view.findViewById(R.id.tv_timer);
public Adapter(ArrayList<String> al_data)
this.al_data = al_data;
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_layout,parent,false);
return new MyViewHolder(view);
@Override
public void onBindViewHolder(final MyViewHolder holder, int position)
holder.tv_timer.setText(al_data.get(position));
if (holder.timer != null)
holder.timer.cancel();
long timer = Long.parseLong(al_data.get(position));
timer = timer*1000;
holder.timer = new CountDownTimer(timer, 1000)
public void onTick(long millisUntilFinished)
holder.tv_timer.setText("" + millisUntilFinished/1000 + " Sec");
public void onFinish()
holder.tv_timer.setText("00:00:00");
.start();
@Override
public int getItemCount()
return al_data.size();
主要活动
package com.androidsolutionworld.multipletimer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.LinearLayout;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity
private RecyclerView recyclerView;
private ArrayList<String> al_data = new ArrayList<>();
private Adapter obj_adapter;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
al_data.add("1234");
al_data.add("1257");
al_data.add("100");
al_data.add("1547");
al_data.add("200");
al_data.add("500");
al_data.add("2000");
al_data.add("1000");
obj_adapter = new Adapter(al_data);
LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext(),LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(obj_adapter);
谢谢
【讨论】:
【参考方案4】:适配器类:-
公共类 CountdownAdapter 扩展 RecyclerView.Adapter 数组列表 mList; 上下文 mContext;
public CountdownAdapter(ArrayList<Long> mList, Context mContext)
this.mList = mList;
this.mContext = mContext;
public class ViewHolder extends RecyclerView.ViewHolder
CountDownTimer timerCount;
TextView mCountDownTxt;
public ViewHolder(View convertView)
super(convertView);
mCountDownTxt = (TextView)convertView.findViewById(R.id.countdown_text);
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
return null;
@Override
public void onBindViewHolder(final ViewHolder holder, final int position)
if(holder.timerCount==null)
holder.timerCount = new CountDownTimer(mList.get(position), 1000)
@Override
public void onTick(long millis)
String hms = String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)), TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
holder.mCountDownTxt.setText(hms);
@Override
public void onFinish()
;
holder.mCountDownTxt.setVisibility(View.VISIBLE);
holder.timerCount.start();
@Override
public int getItemCount()
return mList.size();
【讨论】:
以上是关于如何处理 ListView 中的多个倒数计时器?的主要内容,如果未能解决你的问题,请参考以下文章
当 ListView 有 ItemTemplate 时如何处理 ListViewItem 的右键单击?