【问题标题】:How to handle multiple countdown timers in ListView?如何处理 ListView 中的多个倒数计时器?
【发布时间】:2015-09-12 14:03:08
【问题描述】:

我有一个列表视图(带有自定义列表适配器),我需要在每一行上显示倒计时。

例如,如果我的列表包含 4 个项目,我将有 4 行。 此时,我需要处理 4 个不同的倒计时(每行一个),因为时间不同。

到目前为止,我正在通过以下方式处理它:在自定义列表适配器中,在 getView() 方法中,我创建了一个新的 CountDownTimer 并在 TextView 中显示剩余时间。

但问题是它大大减慢了活动,在最坏的情况下我什至无法正确滚动(因为每次显示一行时,它都会创建一个新的 CountDownTimer)。

我搜索了很多更好的解决方案,但没有一个令人满意。

是否有更清洁、更流畅的解决方案来处理 listView 中的多个倒计时?

谢谢

【问题讨论】:

    标签: android listview countdown baseadapter countdowntimer


    【解决方案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_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>
    

    【讨论】:

    • 任何人都可以用 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_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>
    

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

      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        • 1970-01-01
        • 2011-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多