I display a countdown timer in each item of my listvew (in a TextView), that I update every second. It works perfectly as every item has its very own correct timer. But whenever the listview gets longer and obliges me to scroll down or up, the items get confused and the first hidden item displays not his timer but rather that of the first one, it's pretty much confusing to get what is happening. I know it has something to do with the listview recycler, and that this is how listview works. But I need to solve this problem and I don't know how.
Also, if I remove the statement if(convertView == null)
, it'll get fixed. But the listview will become extremely slow to load when I scroll.
Here's the custom adapter I'm using :
public class TicketAdapter extends ArrayAdapter<TicketModel> implements View.OnClickListener{
private ArrayList<TicketModel> dataSet;
Context mContext;
// View lookup cache
private class ViewHolder {
TextView txtName;
TextView txtType;
TextView txtTempsRestant;
TextView txtDate;
TextView txtSLA;
ImageView info;
RelativeLayout layout;
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
long timeLeftMS = bundle.getLong("time");
int day = (int) ((timeLeftMS / (24*3600000)));
int hour = (int) ((timeLeftMS / (1000*60*60)) % 24);
int minute = (int) ((timeLeftMS / (60000)) % 60);
int second = (int)timeLeftMS % 60000 / 1000;
String timeLeftText = "";
if (day<10) timeLeftText += "0";
timeLeftText += day;
timeLeftText += ":";
if (hour<10) timeLeftText += "0";
timeLeftText += hour;
timeLeftText += ":";
if (minute<10) timeLeftText += "0";
timeLeftText += minute;
timeLeftText += ":";
if (second<10) timeLeftText += "0";
timeLeftText += seconde;
txtTempsRestant.setText(timeLeftText); //----- This is where I'm updating my textview every second ------
}
};
Handler handlerLate = new Handler(){
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
long time = bundle.getLong("time");
if (time == -1){
txtTempsRestant.setText("Ancient version");
txtTempsRestant.setTextColor(Color.parseColor("#434343"));
}
else {
txtTempsRestant.setText("Late");
txtTempsRestant.setTextColor(Color.parseColor("#434343"));
layout.setBackgroundColor(Color.parseColor("#3caa0000"));
info.setImageResource(R.drawable.haute);
}
}
};
Handler handlerFinishLate = new Handler(){
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
String Nom = bundle.getString("name");
String idTicket = bundle.getString("id");
txtTempsRestant.setText("Late");
txtTempsRestant.setTextColor(Color.parseColor("#434343"));
layout.setBackgroundColor(Color.parseColor("#3caa0000"));
info.setImageResource(R.drawable.haute);
}
};
Handler handlerAttente = new Handler(){
@Override
public void handleMessage(Message msg) {
txtTempsRestant.setText("En attente...");
txtTempsRestant.setTextColor(Color.parseColor("#434343"));
layout.setBackgroundColor(Color.parseColor("#949494"));
info.setImageResource(R.drawable.enattente);
}
};
public void startTimer(long timeLeftMS, String statut, final String Nom, final String idTicket) {
if(statut.equals("4")){
handlerAttente.sendEmptyMessage(0);
}
else{
if (timeLeftMS<0){
//handlerLate.sendEmptyMessage(0);
Bundle bundle = new Bundle();
bundle.putLong("time", timeLeftMS);
Message message = new Message();
message.setData(bundle);
handlerLate.sendMessage(message);
}
else{
CountDownTimer countDownTimer = new CountDownTimer(timeLeftMS, 1000) {
@Override
public void onTick(long l) {
Bundle bundle = new Bundle();
bundle.putLong("time", l);
bundle.putString("name", Nom);
bundle.putString("id", idTicket);
Message message = new Message();
message.setData(bundle);
handler.sendMessage(message);
}
@Override
public void onFinish() {
Bundle bundle = new Bundle();
bundle.putString("name", Nom);
bundle.putString("id", idTicket);
Message message = new Message();
message.setData(bundle);
handlerFinishLate.sendMessage(message);
}
}.start();
}
}
}
}
public TicketAdapter(ArrayList<TicketModel> data, Context context) {
super(context, R.layout.row_item_ticket, data);
this.dataSet = data;
this.mContext=context;
}
@Override
public void onClick(View v) {
int position=(Integer) v.getTag();
Object object= getItem(position);
TicketModel TicketModel=(TicketModel)object;
switch (v.getId())
{
case R.id.item_info:
Snackbar.make(v, "Late? : " +TicketModel.isTicketEnRetard(), Snackbar.LENGTH_LONG)
.setAction("No action", null).show();
break;
}
}
private int lastPosition = -1;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
TicketModel TicketModel = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
final ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
long timeLeft;
String Statut;
String Nom;
String idTicket;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_item_ticket, parent, false);
viewHolder.txtName = (TextView) convertView.findViewById(R.id.titreTV);
viewHolder.txtDate = (TextView) convertView.findViewById(R.id.dateTV);
viewHolder.txtSLA = (TextView) convertView.findViewById(R.id.slaTV);
viewHolder.txtTempsRestant = (TextView) convertView.findViewById(R.id.SLARestantTV);
viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);
viewHolder.layout = (RelativeLayout) convertView.findViewById(R.id.backgroundRow);
timeLeft = Long.valueOf(TicketModel.getTempsRestantTicket());
Statut = TicketModel.getStatut();
Nom = TicketModel.getTitreTicket();
idTicket = TicketModel.getIdTicket();
result=convertView;
viewHolder.startTimer(timeLeft, Statut, Nom, idTicket);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
viewHolder.txtName = (TextView) convertView.findViewById(R.id.titreTV);
viewHolder.txtDate = (TextView) convertView.findViewById(R.id.dateTV);
viewHolder.txtSLA = (TextView) convertView.findViewById(R.id.slaTV);
viewHolder.txtTempsRestant = (TextView) convertView.findViewById(R.id.SLARestantTV);
viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);
viewHolder.layout = (RelativeLayout) convertView.findViewById(R.id.backgroundRow);
timeLeft = Long.valueOf(TicketModel.getTempsRestantTicket());
Statut = TicketModel.getStatut();
Nom = TicketModel.getTitreTicket();
idTicket = TicketModel.getIdTicket();
lastPosition = position;
viewHolder.txtName.setText(TicketModel.getTitreTicket());
viewHolder.txtDate.setText(TicketModel.getDateTicket());
viewHolder.txtSLA.setText(TicketModel.getSlaTicket());
if (Long.valueOf(TicketModel.getTempsRestantTicket())<0){
viewHolder.txtTempsRestant.setText("Late");
}
viewHolder.layout.setBackgroundColor(getColorBG(TicketModel.isTicketEnRetard()));
viewHolder.info.setOnClickListener(this);
viewHolder.info.setTag(position);
// Return the completed view to render on screen
return convertView;
}
}
Also, if I remove the statement if(convertView == null), it'll get fixed. But the listview will become extremely slow to load when I scroll.
ListView
works on the concept of recycling the scraped view. When you scroll up and the view present on 1st state gets scraped and the new view which appears on the bottom is the same view which was scraped before.
To overcome the issue, the the view know how many types of view :
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public int getCount() {
return dataSet.size();
}
@Override
public Object getItem(int position) {
return dataSet.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
Also
make sure you init. ArrayList
as private ArrayList<TicketModel> dataSet = new ArrayList<>();