web-dev-qa-db-fra.com

Comment gérer plusieurs comptes à rebours dans ListView?

J'ai une liste (avec un adaptateur de liste personnalisé), je dois afficher un compte à rebours sur chaque ligne. 

Par exemple, si ma liste contient 4 éléments, j'aurai 4 lignes ..__ À ce stade, je dois gérer 4 comptes à rebours différents (un pour chaque ligne) car le temps est différent.

enter image description here

Jusqu'à présent, je le gère de la manière suivante: dans l'adaptateur de liste personnalisé, dans la méthode getView (), je crée un nouveau compte CountDownTimer et affiche le temps restant dans TextView. 

Mais le problème est que cela ralentit beaucoup l'activité, je ne peux même pas faire défiler correctement dans le pire des cas (car chaque fois qu'une ligne est affichée, cela crée un nouveau compte à rebours).

J'ai beaucoup cherché une meilleure solution, mais personne n'a été satisfait.

Existe-t-il une solution plus propre et plus fluide pour gérer plusieurs comptes à rebours dans un listView?

Merci

15
Rob

Au lieu d'essayer de montrer le temps restant pour tous, l'idée est de mettre à jour le temps restant pour les éléments visibles.

Veuillez suivre l'exemple de code suivant et laissez-moi savoir: 

Activité principale : 

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_width="match_parent"
Android:layout_height="match_parent">

<ListView
    Android:id="@+id/lvItems"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />

</RelativeLayout>

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical"
Android:padding="5dp">

<TextView
    Android:id="@+id/tvProduct"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_margin="10dp"
    Android:text="Product Name"
    Android:textSize="16dp"
    Android:textStyle="bold" />

<TextView
    Android:id="@+id/tvTimeRemaining"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_margin="10dp"
    Android:text="Time Remaining : " />

</LinearLayout>
26
Eldhose M Babu

Il est peut-être tard, mais il s’agit d’une version recyclerView utilisant php et json, basée sur l’excellente réponse de @Eldhose M Babu. J'espère être utile :)

 Multiple countdown photo display

Adaptateur.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_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context="here.math.MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            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_width="wrap_content"
        Android:layout_height="wrap_content"
        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_width="match_parent"
    Android:layout_height="match_parent"
    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_width="match_parent"
            Android:layout_height="wrap_content">
        </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_width="match_parent"
    Android:layout_height="match_parent">

    <Android.support.v7.widget.CardView
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_marginBottom="20dp">

        <LinearLayout
            Android:padding="@dimen/activity_horizontal_margin"
            Android:orientation="vertical"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent">


            <TableLayout
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content">

                <TableRow>
                    <TextView
                        Android:text="Expire time"
                        Android:paddingRight="10dp"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                    <TextView
                        Android:id="@+id/cd"
                        Android:textStyle="bold"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                </TableRow>

                <TableRow>
                    <TextView
                        Android:text="Name"
                        Android:paddingRight="10dp"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                    <TextView
                        Android:id="@+id/textViewName"
                        Android:textStyle="bold"
                        Android:layout_width="wrap_content"
                        Android:layout_height="wrap_content" />

                </TableRow>

            </TableLayout>
        </LinearLayout>
    </Android.support.v7.widget.CardView>

</RelativeLayout>

et pour la partie url, vous pouvez faire quelque chose comme:

<?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);
13
Hamed Okhovvat

S'il vous plaît jeter un oeil sur cette Compte à rebours dans listview Android exemple.

Adaptateur personnalisé

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



}

Activité principale

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

Merci

1
Dev

Classe d'adaptateur: -


la classe publique CountdownAdapter étend RecyclerView.Adapter { ArrayList mList; Contexte 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();
}

}

1
Imeshke