【问题标题】:How to handle multiple countdown timers in RecyclerView?如何处理 RecyclerView 中的多个倒数计时器?
【发布时间】:2015-08-27 19:06:58
【问题描述】:

我有一个 Recyclerview ,我需要在每一行显示倒计时。

这是一个类似的问题 coutndown timers in listview 它有一个很好的解决方案,但我需要 recyclerview 来解决这个问题

编辑:

这是我的代码适配器 MyAdapter:

 public class AdapterItems extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private ArrayList<TopCompetitions> mListItems = new ArrayList<>();
private ImageLoader mImageLoader;
private Context context;
private Handler handler;
/******************************************/
String current_date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
long diff;
long diffSeconds;
long diffMinutes;
long diffHours;
long diffDays;

String reachableDate = "";
/******************************************/
private ScheduledFuture updateFuture;

public AdapterItems(Context context) {

    this.context = context;
    mImageLoader = AppController.getInstance().getImageLoader();

}

public void setmListItems(ArrayList<TopCompetitions> mListItems) {
    this.mListItems = mListItems;
    //update the adapter to reflect the new set of mListItems
    notifyDataSetChanged();
}


@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {


    View itemView = LayoutInflater.
            from(parent.getContext()).
            inflate(R.layout.custom_horizontal_row, parent, false);
    return new ItemHolder(itemView);


}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {


    final TopCompetitions currentItem = mListItems.get(position);
    final ItemHolder itemHolder = (ItemHolder) holder;

   /* start_date , name_com_ar , name_com_en,
            question_en,answer_ar1,answer_ar2,answer_ar3
            ,answer_en1,answer_en2,answer_en3,right_answer;
    */
    itemHolder.item_id.setText(currentItem.getPrize_id());
    itemHolder.item_description.setText(currentItem.getName_com_ar());
    itemHolder.start_date.setText(currentItem.getStart_date());
    itemHolder.end_date.setText(currentItem.getEnd_date());
    itemHolder.name_com_ar.setText(currentItem.getName_com_ar());
    itemHolder.name_com_en.setText(currentItem.getName_com_en());
    itemHolder.answer_en1.setText(currentItem.getAnswer_en1());
    itemHolder.answer_en2.setText(currentItem.getAnswer_en2());
    itemHolder.answer_en3.setText(currentItem.getAnswer_en3());
    itemHolder.answer_ar1.setText(currentItem.getAnswer_ar1());
    itemHolder.answer_ar2.setText(currentItem.getAnswer_ar2());
    itemHolder.answer_ar3.setText(currentItem.getAnswer_ar3());
    itemHolder.right_answer.setText(currentItem.getRight_answer());
    itemHolder.question_en.setText(currentItem.getQuestion_en());
    itemHolder.question_ar.setText(currentItem.getQuestion_ar());
    itemHolder.desc_ar.setText(currentItem.getPrize_desc_ar());
    itemHolder.desc_en.setText(currentItem.getPrize_desc_en());

    String urlLogo = currentItem.getPrize_pic1();
    loadImages(urlLogo, itemHolder);
    setDefferinceTimer(itemHolder , currentItem.getEnd_date());


    if (updateFuture == null) {
        final Handler mainHandler = new Handler(Looper.getMainLooper());
        updateFuture = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                setDefferinceTimer(itemHolder , currentItem.getEnd_date());
                notifyDataSetChanged();
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyDataSetChanged();
                    }
                });
            }
        }, 0, 1000, TimeUnit.MILLISECONDS);
    }

  /*  new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            new CountDownTimer(20000, 1000) {

                public void onTick(long millisUntilFinished) {
                    startCountDown(itemHolder, currentItem.getEnd_date() + " 00:00:00");
                    notifyDataSetChanged();
                }

                public void onFinish() {
                    //counterTextView.setText("done!");
                }
            }.start();
        }
    });
       */

}

public void setDefferinceTimer(final RecyclerView.ViewHolder holder , String itemEndDate){

    final ItemHolder itemHolder = (ItemHolder) holder;

    current_date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
   // reachableDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(itemEndDate);

    try {
        d1 = format.parse(current_date);
        d2 = format.parse(itemEndDate+" 00:00:00");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    diff = d2.getTime() - d1.getTime();

    diffSeconds = diff / 1000 % 60;
    diffMinutes = diff / (60 * 1000) % 60;
    diffHours = diff / (60 * 60 * 1000) % 24;
    diffDays = diff / (24 * 60 * 60 * 1000);

    itemHolder.days_tf.setText(""+diffDays);
    itemHolder.hours_tf.setText(""+diffHours);
    itemHolder.minutes_tf.setText(""+diffMinutes);
    itemHolder.seconds_tf.setText(""+diffSeconds);

}

private void loadImages(String urlThumbnail, final RecyclerView.ViewHolder holder) {
    final ItemHolder itemHolder = (ItemHolder) holder;
    mImageLoader.get(urlThumbnail, new ImageLoader.ImageListener() {
        @Override
        public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
            itemHolder.item_image.setImageBitmap(response.getBitmap());
            //holder.salon_gender.setImageBitmap(response.getBitmap());
        }

        @Override
        public void onErrorResponse(VolleyError error) {

        }
    });
}


@Override
public int getItemCount() {
    return mListItems.size();
}


private class ItemHolder extends RecyclerView.ViewHolder {

    public TextView item_id, item_description, end_date,
            start_date, name_com_ar, name_com_en, question_ar,
            question_en, answer_ar1, answer_ar2, answer_ar3, answer_en1,
            answer_en2, answer_en3, right_answer , desc_ar , desc_en;
    public TextView days_tf, hours_tf, minutes_tf, seconds_tf;
    public CircleImageView item_image;

    public ItemHolder(View itemView) {
        super(itemView);

        start_date = (TextView) itemView.findViewById(R.id.start_date);
        end_date = (TextView) itemView.findViewById(R.id.end_date);
        name_com_ar = (TextView) itemView.findViewById(R.id.name_com_ar);
        name_com_en = (TextView) itemView.findViewById(R.id.name_com_en);
        question_en = (TextView) itemView.findViewById(R.id.question_en);
        question_ar = (TextView) itemView.findViewById(R.id.question_ar);
        desc_ar = (TextView) itemView.findViewById(R.id.desc_ar);
        desc_en = (TextView) itemView.findViewById(R.id.desc_en);
        answer_ar1 = (TextView) itemView.findViewById(R.id.answer_ar1);
        answer_ar2 = (TextView) itemView.findViewById(R.id.answer_ar2);
        answer_ar3 = (TextView) itemView.findViewById(R.id.answer_ar3);
        answer_en1 = (TextView) itemView.findViewById(R.id.answer_en1);
        answer_en2 = (TextView) itemView.findViewById(R.id.answer_en2);
        answer_en3 = (TextView) itemView.findViewById(R.id.answer_en3);
        right_answer = (TextView) itemView.findViewById(R.id.right_answer);


        item_id = (TextView) itemView.findViewById(R.id.item_id);
        item_description = (TextView) itemView.findViewById(R.id.item_description);
        item_image = (CircleImageView) itemView.findViewById(R.id.item_image);


        days_tf = (TextView) itemView.findViewById(R.id.days_tf);
        hours_tf = (TextView) itemView.findViewById(R.id.hours_tf);
        minutes_tf = (TextView) itemView.findViewById(R.id.minutes_tf);
        seconds_tf = (TextView) itemView.findViewById(R.id.seconds_tf);

    }



}

【问题讨论】:

  • 展示你有什么,你试过什么
  • 我了解您想做什么,但您的具体问题是什么?如何使用自定义项目制作 recyclerview?
  • Mr.Erik.. 不,我已经有了 Recyclerview,一切进展顺利......但回收站中的每一件物品都必须有一个计时器倒计时......如上图......

标签: android android-layout android-intent android-recyclerview countdown


【解决方案1】:

简单的解决方案是:

Handler handler=new Handler();
handler.postDelayed(new UpdateTimerThread(holder),0); 

public Class UpdateTimerThread implements Runnable{
Holder holder;

    public UpdateTimerThread(Holder holder){
        this.holder=holder;
    }
    @Override
    public void run() {
        lgetCreatedTime = lgetCreatedTime + 1000;
        long diffSeconds;
        long diffMinutes;
        long diffHours;
        diffSeconds = lgetCreatedTime / 1000 % 60;
        diffMinutes = lgetCreatedTime / (60 * 1000) % 60;
        diffHours = lgetCreatedTime / (60 * 60 * 1000) % 24;
        holder.GameTimer.setText(String.format("%02d:%02d:%02d", diffHours,   diffMinutes, diffSeconds));
        handler.postDelayed(this,1000);
    }
}

注意: 1.holder是ViewHolder类的对象

2.创建类UpdateTimerThread实现Runnable

3.将日期和时间转换为long并存储到lgetCreatedTime中

4.每隔 1 秒运行一次 Handler 来打勾

【讨论】:

    【解决方案2】:

    我在这里扩展AppCompatTextView 并在其中添加CountDownTimer。我正在处理onStop 和onStart,所以CountDownTimer 在后台不起作用。

    class TimeCounterView : androidx.appcompat.widget.AppCompatTextView {
        constructor(context: Context) : super(context)
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
            context,
            attrs,
            defStyleAttr
        )
    
    var countDownTimer: CountDownTimer? = null
    
    fun startCountDown(expireTimeStamp: Long, removeItem: () -> Unit) {
        stopCounter()
        text = calculateTimeExpire(expireTimeStamp)//custom method to calculate time until expireTime
        countDownTimer = object : CountDownTimer(
            expireTimeStamp * 1000 - System.currentTimeMillis(),
            ONE_MINUTE_MILLIS / 16
        ) {
            override fun onTick(millisUntilFinished: Long) {
                text = calculateTimeExpire(expireTimeStamp)
            }
    
            override fun onFinish() {
                text = context.getString(R.string.choice_finished)
                removeItem()
            }
        }
        countDownTimer?.start()
    }
    
    fun stopCounter() {
        countDownTimer?.cancel()
    }
    
    companion object {
        const val ONE_MINUTE_MILLIS: Long = 60000
    }
    }
    

    然后像这样在你的 itemview xml 中使用它作为普通视图

    ....
        <com.example.custom.TimeCounterView
            android:id="@+id/timeTV"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="12dp"
            android:layout_weight="0"
            android:textColor="@color/colorText"
            tools:text="23:59" />
    ....
    

    然后在您的适配器中,您将需要实现非常重要的方法onViewRecycled,该方法将被调用以从计时器对象中释放内存。

     override fun onViewRecycled(holder: ChannelChoiceViewHolder) {
            super.onViewRecycled(holder)
            holder.stopCounter()
        }
    

    然后在持有人里面你会这样做

    fun bind(item: ChannelPageChoice, position: Int) {
        //...
        binding.timeTV.startCountDown(item.expire){ removeItem(position) }
        //...
    }
    fun stopCounter() {
        binding.timeTV.stopCounter()
    }
    

    然后在活动/片段中,您必须在 onStop 中将 recyclerview 设置为 null 并在 onStart 中重新附加它在屏幕上可见。

    override fun onStop() {
        super.onStop()
        currentItemPosition =
            (binding.recyclerView.layoutManager as? LinearLayoutManager)
                ?.findFirstVisibleItemPosition() ?: 0
    
        binding.recyclerView.adapter = null
    }
    
    override fun onStart() {
        super.onStart()
        binding.recyclerView.adapter = myCustomAdapter
        binding.recyclerView.scrollToPosition(currentItemPosition)
    }
    

    【讨论】:

      【解决方案3】:

      在ViewHolder 中添加CountDownTimer 成员。在onBindViewHolder() 中设置并启动计数器,不要忘记取消ViewHolder 同一实例中的任何现有计数器。在onTick() 中,您需要更新显示屏上的值,而不是启动计数器。

      【讨论】:

      • Recyclerview 有 getView() 方法吗!
      • 你的意思是 onBindViewHolder() ?
      • 是的。对不起,我太专注于解决方案的设计......我忘了你需要一个 RecyclerView 方法。 ListView 或 RecyclerView,它们的适配器是相似的——只是 getView 一分为二(创建和值绑定),你不得不在 RecyclerView 适配器中使用 ViewHolder 模式。
      • 是的,没问题.. 感谢您的回复,我只是在 onCreateView() 上添加了一个可运行的 notifyDatasetChange(),一切正常
      • @AhmadAlkhateeb 你能举个例子吗...我想要同样的
      【解决方案4】:

      这里可以查看和下载Countdown timers in a RecyclerView的源码

      activity_main.xml

      <RelativeLayout android:layout_width=”match_parent”
      android:layout_height=”match_parent”
      xmlns:android=”http://schemas.android.com/apk/res/android”&gt;
      
      <android.support.v7.widget.RecyclerView
      android:id=”@+id/recycler_view”
      android:layout_width=”match_parent”
      android:layout_height=”wrap_content”
      android:scrollbars=”vertical” />
      
      </RelativeLayout>
      

      adapter_layout.xml

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="match_parent"
      android:layout_height="wrap_content">
      
      <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textSize="15dp"
          android:padding="10dp"
          android:id="@+id/tv_timer"/>
      
      </LinearLayout>
      

      MainActivity.java

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

      自定义适配器:

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

      【讨论】:

        【解决方案5】:

        您可以在 ViewHolder 类中创建 CountDownTimer 引用,并在 Adapter 的 onBindViewHolder 方法中创建其实例。

        效果很好。

        自己测试过。

        【讨论】:

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